├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── daemon.py ├── docker-compose.yml ├── home ├── __init__.py ├── admin.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── manage.py ├── reader ├── __init__.py ├── documents.py ├── helpers.py ├── learn.py ├── migrations │ └── __init__.py ├── templatetags │ ├── __init__.py │ ├── articles.py │ └── subscriptions.py ├── urls.py └── views │ ├── __init__.py │ ├── articles.py │ ├── queries │ ├── __init__.py │ ├── read_records.py │ └── subscriptions.py │ └── subscriptions.py ├── static ├── admin │ ├── css │ │ ├── base.css │ │ ├── changelists.css │ │ ├── dashboard.css │ │ ├── forms.css │ │ ├── ie.css │ │ ├── login.css │ │ ├── rtl.css │ │ └── widgets.css │ ├── img │ │ ├── changelist-bg.gif │ │ ├── changelist-bg_rtl.gif │ │ ├── default-bg-reverse.gif │ │ ├── default-bg.gif │ │ ├── deleted-overlay.gif │ │ ├── gis │ │ │ ├── move_vertex_off.png │ │ │ └── move_vertex_on.png │ │ ├── icon-no.gif │ │ ├── icon-unknown.gif │ │ ├── icon-yes.gif │ │ ├── icon_addlink.gif │ │ ├── icon_alert.gif │ │ ├── icon_calendar.gif │ │ ├── icon_changelink.gif │ │ ├── icon_clock.gif │ │ ├── icon_deletelink.gif │ │ ├── icon_error.gif │ │ ├── icon_searchbox.png │ │ ├── icon_success.gif │ │ ├── inline-delete-8bit.png │ │ ├── inline-delete.png │ │ ├── inline-restore-8bit.png │ │ ├── inline-restore.png │ │ ├── inline-splitter-bg.gif │ │ ├── nav-bg-grabber.gif │ │ ├── nav-bg-reverse.gif │ │ ├── nav-bg-selected.gif │ │ ├── nav-bg.gif │ │ ├── selector-icons.gif │ │ ├── selector-search.gif │ │ ├── sorting-icons.gif │ │ ├── tooltag-add.png │ │ └── tooltag-arrowright.png │ └── js │ │ ├── LICENSE-JQUERY.txt │ │ ├── SelectBox.js │ │ ├── SelectFilter2.js │ │ ├── actions.js │ │ ├── actions.min.js │ │ ├── admin │ │ ├── DateTimeShortcuts.js │ │ └── RelatedObjectLookups.js │ │ ├── calendar.js │ │ ├── collapse.js │ │ ├── collapse.min.js │ │ ├── core.js │ │ ├── inlines.js │ │ ├── inlines.min.js │ │ ├── jquery.init.js │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ ├── prepopulate.js │ │ ├── prepopulate.min.js │ │ ├── timeparse.js │ │ └── urlify.js ├── favicon.ico └── reader │ ├── css │ ├── clearfix.css │ ├── components │ │ └── button.scss │ ├── core.css │ ├── frame.css │ ├── layouts │ │ ├── arrow-nav.scss │ │ ├── article.scss │ │ ├── blog.scss │ │ ├── clients.scss │ │ ├── footer.scss │ │ ├── header.scss │ │ ├── next-articles.scss │ │ └── products.scss │ ├── media │ │ ├── ipad.css │ │ ├── ipad.css.map │ │ ├── ipad.scss │ │ ├── iphone.css │ │ ├── iphone.css.map │ │ └── iphone.scss │ ├── normalize.css │ ├── override.css │ ├── views │ │ ├── account-form.scss │ │ ├── homepage.scss │ │ └── subscriptions.scss │ └── windows.css │ ├── img │ └── logo.png │ ├── js │ ├── bubble.js │ ├── jquery.fitvids.js │ ├── jquery.min.js │ ├── jquery.scroll.js │ ├── jquery.validate.min.js │ ├── main-1.1.js │ ├── next-articles.js │ ├── post.js │ ├── statistics.js │ └── subscriptions.js │ └── vendor │ └── font-awesome-4.2.0 │ ├── css │ ├── font-awesome.css │ └── font-awesome.min.css │ ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff │ ├── less │ ├── bordered-pulled.less │ ├── core.less │ ├── fixed-width.less │ ├── font-awesome.less │ ├── icons.less │ ├── larger.less │ ├── list.less │ ├── mixins.less │ ├── path.less │ ├── rotated-flipped.less │ ├── spinning.less │ ├── stacked.less │ └── variables.less │ └── scss │ ├── _bordered-pulled.scss │ ├── _core.scss │ ├── _fixed-width.scss │ ├── _icons.scss │ ├── _larger.scss │ ├── _list.scss │ ├── _mixins.scss │ ├── _path.scss │ ├── _rotated-flipped.scss │ ├── _spinning.scss │ ├── _stacked.scss │ ├── _variables.scss │ └── font-awesome.scss ├── storycafe ├── __init__.py ├── settings │ ├── __init__.py │ ├── development.py │ └── production.py ├── urls.py └── wsgi.py └── templates ├── account ├── account_inactive.html ├── base.html ├── email.html ├── email │ ├── email_confirmation_message.txt │ ├── email_confirmation_signup_message.txt │ ├── email_confirmation_signup_subject.txt │ ├── email_confirmation_subject.txt │ ├── password_reset_key_message.txt │ └── password_reset_key_subject.txt ├── email_confirm.html ├── email_confirmed.html ├── login.html ├── logout.html ├── messages │ ├── cannot_delete_primary_email.txt │ ├── email_confirmation_sent.txt │ ├── email_confirmed.txt │ ├── email_deleted.txt │ ├── logged_in.txt │ ├── logged_out.txt │ ├── password_changed.txt │ ├── password_set.txt │ ├── primary_email_set.txt │ └── unverified_primary_email.txt ├── password_change.html ├── password_reset.html ├── password_reset_done.html ├── password_reset_from_key.html ├── password_reset_from_key_done.html ├── password_set.html ├── signup.html ├── signup_closed.html ├── snippets │ └── already_logged_in.html ├── verification_sent.html └── verified_email_required.html ├── base.html ├── home ├── about.html └── welcome.html └── reader ├── article.html ├── base.html ├── frame.html ├── no-more.html └── subscriptions.html /.dockerignore: -------------------------------------------------------------------------------- 1 | .db 2 | .mongo 3 | .sass-cache 4 | .docker 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Sqlite3 Database 2 | *.sqlite3 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | bin/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | tmp/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # Installer logs 28 | pip-log.txt 29 | pip-delete-this-directory.txt 30 | 31 | # Unit test / coverage reports 32 | .tox/ 33 | .coverage 34 | .cache 35 | nosetests.xml 36 | coverage.xml 37 | 38 | # Translations 39 | *.mo 40 | 41 | # Mr Developer 42 | .mr.developer.cfg 43 | .project 44 | .pydevproject 45 | 46 | # Rope 47 | .ropeproject 48 | 49 | # Django stuff: 50 | *.log 51 | *.pot 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # DB Data as a Docker Volume 57 | .db 58 | .mongo 59 | .docker-built 60 | 61 | # CSS Cache 62 | .sass-cache 63 | static/CACHE 64 | 65 | .docker/ 66 | 67 | docker-compose.production.yml -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM base/archlinux 2 | MAINTAINER Wei Tang 3 | 4 | RUN pacman -Sy archlinux-keyring --noconfirm 5 | RUN pacman -Syyu --noconfirm 6 | RUN pacman-db-upgrade 7 | RUN pacman -S --noconfirm python-django python-scikit-learn python-beautifulsoup4 python-lxml python-pip jdk8-openjdk gcc ruby postgresql-libs python-psycopg2 make python-nltk 8 | ENV PATH /root/.gem/ruby/2.3.0/bin:$PATH 9 | 10 | RUN gem install sass compass 11 | RUN pip install django-guardian boilerpipe-py3 feedparser markdown simplejson django-allauth django_compressor mongoengine blinker 12 | 13 | RUN mkdir /app 14 | ADD . /app 15 | WORKDIR /app 16 | 17 | EXPOSE 8000 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 sorpa'as plat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Reread: Yet Another Hacker News Reader 2 | ====== 3 | Reread is my personal Hacker News Reader. It is hackable and easy to deploy. A 4 | hosted version of Reread can be found at [reread.id.hn](http://reread.id.hn/). 5 | 6 | {F2893, size=full, alt="Reread Screenshot"} 7 | 8 | ## Features 9 | 10 | * **Read Hacker News** 11 | * Read RSS Feed 12 | * Hackable 13 | * A Little Bit Machine Learning 14 | * Subscriptions 15 | 16 | ## Why Another News Reader? 17 | 18 | Because privacy and control of data becomes even more important when we want to 19 | add a little Machine Learning in the tools we use. And hackable and fun things 20 | are always better than anything else. 21 | 22 | ## Install 23 | 24 | Reread can be built with [Docker](http://docker.io) with Docker Compose. Once 25 | you have them installed, simply run: 26 | 27 | docker-compose up 28 | 29 | And a running instance will be accessable at http://localhost:3020 30 | 31 | ## Configuration 32 | 33 | There are several variables in `./docker-compose.yml` that you need to configure 34 | if you want to deploy Reread into a production environment. 35 | 36 | * **SECRET_KEY**: A secret key that needs keeping secretly. It can be generated 37 | easily with things like [this](https://gist.github.com/ndarville/3452907). 38 | * **DJANGO_SETTINGS_MODULE**: Change this to `storycafe.settings.production` for 39 | production environment. 40 | 41 | ## Hacking the Learning Model 42 | 43 | Currently the predict function in `reader/learn.py` is really naive, but I 44 | found it already enough for personal use. If you want ot hack it, simply modify 45 | the `predict_articles` function in `reader/learn.py`. 46 | 47 | ## Issues and Contribution 48 | 49 | The project is managed at a [Phabricator instance](https://source.id.hn/). 50 | [Create a new issue](https://source.id.hn/maniphest/task/edit/form/default/?projects=reread). 51 | -------------------------------------------------------------------------------- /daemon.py: -------------------------------------------------------------------------------- 1 | import os 2 | import django 3 | import threading 4 | 5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "storycafe.settings.production") 6 | django.setup() 7 | 8 | from reader.documents import * 9 | from mongoengine import signals 10 | 11 | 12 | def post_save_notify(sender, document, created): 13 | print((str(document) + " has been successfully " + ("created" if created else "saved") + ".").encode("utf-8")) 14 | 15 | signals.post_save.connect(post_save_notify) 16 | 17 | 18 | class UpdateSources(threading.Thread): 19 | def run(self): 20 | while True: 21 | for source in Source.objects: 22 | source.update_articles() 23 | 24 | 25 | class UpdateReadingLists(threading.Thread): 26 | def run(self): 27 | while True: 28 | for reader in Reader.objects: 29 | reader.extend_reading_list() 30 | 31 | if __name__ == "__main__": 32 | try: 33 | HackerNewsSource.objects.get(alias="hackernews") 34 | except HackerNewsSource.DoesNotExist: 35 | HackerNewsSource(alias="hackernews", title="Hacker News").save() 36 | 37 | update_sources = UpdateSources() 38 | update_sources.run() 39 | 40 | update_reading_lists = UpdateReadingLists() 41 | update_reading_lists.run() 42 | while True: 43 | if not update_sources.is_alive(): 44 | update_sources = UpdateSources() 45 | update_sources.run() 46 | if not update_reading_lists.is_alive(): 47 | update_reading_lists = UpdateReadingLists() 48 | update_reading_lists.run() 49 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | web: 2 | build: . 3 | links: 4 | - db 5 | - mongo 6 | ports: 7 | - "3020:8000" 8 | command: python manage.py runserver --insecure 0.0.0.0:8000 9 | environment: 10 | DJANGO_SETTINGS_MODULE: storycafe.settings.development 11 | SECRET_KEY: "mytopsecretkey" 12 | REREAD_HOST: "localhost:3020" 13 | db: 14 | image: postgres 15 | restart: always 16 | volumes_from: 17 | - dbdata 18 | environment: 19 | POSTGRES_USER: reread 20 | POSTGRES_PASSWORD: postgres 21 | mongo: 22 | image: mongo 23 | restart: always 24 | volumes_from: 25 | - mongodata 26 | daemon: 27 | build: . 28 | restart: always 29 | links: 30 | - db 31 | - mongo 32 | command: python daemon.py 33 | environment: 34 | DJANGO_SETTINGS_MODULE: storycafe.settings.development 35 | SECRET_KEY: "mytopsecretkey" 36 | REREAD_HOST: "localhost:3020" 37 | dbdata: 38 | image: debian:7.7 39 | volumes: 40 | - "/var/lib/postgresql/data" 41 | mongodata: 42 | image: debian:7.7 43 | volumes: 44 | - "/data/db" 45 | -------------------------------------------------------------------------------- /home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/home/__init__.py -------------------------------------------------------------------------------- /home/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/home/migrations/__init__.py -------------------------------------------------------------------------------- /home/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /home/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /home/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from home import views 3 | 4 | urlpatterns = [ 5 | url(r'^$', views.index, name='index'), 6 | url(r'^about/$', views.about, name='about'), 7 | ] 8 | -------------------------------------------------------------------------------- /home/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | def index(request): 5 | return render(request, 'home/welcome.html', {}) 6 | 7 | def about(request): 8 | return render(request, 'home/about.html', {}) 9 | -------------------------------------------------------------------------------- /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", "storycafe.settings.production") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /reader/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/reader/__init__.py -------------------------------------------------------------------------------- /reader/helpers.py: -------------------------------------------------------------------------------- 1 | def build_url(url, **kwargs): 2 | import urllib 3 | removed_kwargs = {} 4 | for key in kwargs: 5 | if kwargs[key] is not None: 6 | removed_kwargs[key] = kwargs[key] 7 | 8 | params = urllib.parse.urlencode(removed_kwargs) 9 | return url + "?%s" % params 10 | 11 | 12 | def redirect_link_with_params(url_name, *args, **kwargs): 13 | from django.core.urlresolvers import reverse 14 | url = reverse(url_name, args=args) 15 | return build_url(url, **kwargs) 16 | 17 | 18 | def last_liked(article, reader): 19 | try: 20 | record = ReadRecord.objects.filter(reader=reader, article=article).latest("read_at") 21 | return record.liked 22 | except ReadRecord.DoesNotExist: 23 | pass 24 | return False 25 | -------------------------------------------------------------------------------- /reader/learn.py: -------------------------------------------------------------------------------- 1 | from sklearn.linear_model import SGDRegressor 2 | from nltk.stem.snowball import EnglishStemmer 3 | from sklearn.pipeline import Pipeline 4 | from sklearn.feature_extraction.text import TfidfTransformer, CountVectorizer 5 | 6 | 7 | def normalize(text): 8 | stemmer = EnglishStemmer() 9 | return ' '.join([stemmer.stem(s) for s in text.split()]) 10 | 11 | 12 | def predict_articles(reader, articles, n=5): 13 | from reader.documents import ReadRecord 14 | text_clf = Pipeline([('vect', CountVectorizer()), 15 | ('tfidf', TfidfTransformer()), 16 | ('clf', SGDRegressor())]) 17 | records = ReadRecord.objects(reader=reader) 18 | text_clf.fit([normalize(x.article.content_text) for x in records], 19 | [(1 if x.is_liked else 0) for x in records]) 20 | predicted = text_clf.predict([normalize(x.content_text) for x in articles]) 21 | return sorted(zip(list(articles), list(predicted)), 22 | key=lambda t: t[1], 23 | reverse=True)[:n] 24 | -------------------------------------------------------------------------------- /reader/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/reader/migrations/__init__.py -------------------------------------------------------------------------------- /reader/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/reader/templatetags/__init__.py -------------------------------------------------------------------------------- /reader/templatetags/articles.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from reader.documents import * 3 | 4 | register = template.Library() 5 | 6 | @register.filter(name='is_hacker_news_article') 7 | def is_hacker_news_article(article): 8 | return isinstance(article, HackerNewsArticle) 9 | -------------------------------------------------------------------------------- /reader/templatetags/subscriptions.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from reader.documents import * 3 | 4 | register = template.Library() 5 | 6 | @register.filter(name='js_is_subscribed') 7 | def js_is_subscribed(source, reader): 8 | return "true" if reader.is_subscribed(source) else "false" 9 | 10 | @register.filter(name='is_subscribed') 11 | def is_subscribed(source, reader): 12 | return reader.is_subscribed(source) 13 | -------------------------------------------------------------------------------- /reader/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from reader.views import articles, subscriptions 3 | import reader.views.queries.read_records as queries_read_records 4 | import reader.views.queries.subscriptions as queries_subscriptions 5 | 6 | urlpatterns = [ 7 | url(r'^article/(?P[a-z0-9]+)$', 8 | articles.show, name='show'), 9 | url(r'^$', 10 | articles.select, name='select'), 11 | url(r'^subscriptions/$', 12 | subscriptions.index), 13 | url(r'^query/read_records/like/(?P[0-9a-z]+)$', queries_read_records.like_article), 14 | url(r'^query/read_records/unlike/(?P[0-9a-z]+)$', queries_read_records.unlike_article), 15 | url(r'^query/read_records/create/(?P[0-9a-z]+)$', queries_read_records.create), 16 | url(r'^query/subscriptions/subscribe/(?P[0-9a-z]+)$', queries_subscriptions.subscribe), 17 | url(r'^query/subscriptions/unsubscribe/(?P[0-9a-z]+)$', queries_subscriptions.unsubscribe), 18 | ] 19 | -------------------------------------------------------------------------------- /reader/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/reader/views/__init__.py -------------------------------------------------------------------------------- /reader/views/articles.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponse, HttpResponseRedirect 3 | from django.contrib.auth.decorators import login_required 4 | from reader.documents import * 5 | import datetime 6 | from reader.helpers import build_url, redirect_link_with_params 7 | 8 | 9 | @login_required 10 | def select(request): 11 | reader = Reader.reader_for(request.user) 12 | if len(reader.reading_list) == 0: 13 | reader.extend_reading_list() 14 | reader = Reader.reader_for(request.user) 15 | if len(reader.reading_list) == 0: 16 | return HttpResponseRedirect('/reader/subscriptions') 17 | 18 | article = Article.objects.get(id=reader.reading_list[0]['article_id']) 19 | 20 | param_view = request.GET.get('type') 21 | param_next = request.path 22 | 23 | return show(request, str(article.id), 24 | view=param_view, 25 | next=param_next, 26 | history_link=redirect_link_with_params( 27 | 'show', article.id, 28 | view=param_view, 29 | next=param_next)) 30 | 31 | 32 | def show(request, article_id, **kwargs): 33 | VIEW_TEMPLATES = { 34 | 'frame': 'reader/frame.html', 35 | 'article': 'reader/article.html', 36 | } 37 | DEFAULT_VIEW = 'article' 38 | DEFAULT_NEXT_LINK = '/reader' 39 | 40 | article = Article.objects.get(id=article_id) 41 | next_link = kwargs.get('next') or request.GET.get('next') or DEFAULT_NEXT_LINK 42 | history_link = kwargs.get('history_link') 43 | view = kwargs.get('view') or request.GET.get('view') or DEFAULT_VIEW 44 | 45 | def article_view_link(): 46 | return build_url('/reader/article/' + article_id, view='article', 47 | next=next_link) 48 | 49 | def frame_view_link(): 50 | return build_url('/reader/article/' + article_id, view='frame', 51 | next=next_link) 52 | 53 | if request.user.is_authenticated(): 54 | reader = Reader.reader_for(request.user) 55 | reader.preferred_article_view = kwargs.get('view') or request.GET.get('view') or reader.preferred_article_view 56 | reader.save() 57 | view = reader.preferred_article_view 58 | predicted_articles = [(Article.objects.get(id=x['article_id']), x['score']) for x in reader.reading_list] 59 | 60 | if not article.is_framing_allowed: 61 | view = 'article' 62 | 63 | return render(request, VIEW_TEMPLATES[view], { 64 | 'article': article, 65 | 'next_link': next_link, 66 | 'article_view_link': article_view_link(), 67 | 'frame_view_link': frame_view_link(), 68 | 'predicted_articles': predicted_articles, 69 | 'history_link': history_link}) 70 | -------------------------------------------------------------------------------- /reader/views/queries/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/reader/views/queries/__init__.py -------------------------------------------------------------------------------- /reader/views/queries/read_records.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponse, HttpResponseRedirect 3 | from django.contrib.auth.decorators import login_required 4 | from reader.documents import * 5 | import datetime 6 | import json 7 | import urllib 8 | 9 | @login_required 10 | def like_article(request, record_id): 11 | record = ReadRecord.objects.get(id=record_id) 12 | record.is_liked = True 13 | record.save() 14 | return HttpResponse(json.dumps({"status": "ok"}), 15 | content_type="application/json") 16 | 17 | @login_required 18 | def unlike_article(request, record_id): 19 | record = ReadRecord.objects.get(id=record_id) 20 | record.is_liked = False 21 | record.save() 22 | return HttpResponse(json.dumps({"status": "ok"}), 23 | content_type="application/json") 24 | 25 | @login_required 26 | def create(request, article_id): 27 | reader = Reader.reader_for(request.user) 28 | article = Article.objects.get(id=article_id) 29 | reader.reading_list = [x for x in reader.reading_list if str(x['article_id']) != str(article_id)] 30 | reader.save() 31 | try: 32 | record = ReadRecord.objects.get(reader=reader, article=article) 33 | except ReadRecord.DoesNotExist: 34 | record = ReadRecord(reader=reader, article=article) 35 | record.save() 36 | return HttpResponse(json.dumps({"status": "ok", 37 | "record_id": str(record.id), 38 | "is_liked": record.is_liked}), 39 | content_type="application/json") 40 | -------------------------------------------------------------------------------- /reader/views/queries/subscriptions.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponse, HttpResponseRedirect 3 | from django.contrib.auth.decorators import login_required 4 | from reader.documents import * 5 | import datetime 6 | import json 7 | import urllib 8 | 9 | @login_required 10 | def subscribe(request, source_id): 11 | reader = Reader.reader_for(request.user) 12 | source = Source.objects.get(id=source_id) 13 | reader.subscribe(source) 14 | return HttpResponse(json.dumps({"status": "ok"}), 15 | content_type="application/json") 16 | 17 | 18 | @login_required 19 | def unsubscribe(request, source_id): 20 | reader = Reader.reader_for(request.user) 21 | source = Source.objects.get(id=source_id) 22 | reader.unsubscribe(source) 23 | return HttpResponse(json.dumps({"status": "ok"}), 24 | content_type="application/json") 25 | -------------------------------------------------------------------------------- /reader/views/subscriptions.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponse, HttpResponseRedirect 3 | from django.contrib.auth.decorators import login_required 4 | from reader.documents import * 5 | 6 | 7 | @login_required 8 | def index(request): 9 | reader = Reader.reader_for(request.user) 10 | from django import forms 11 | 12 | class RSSForm(forms.Form): 13 | title = forms.CharField(label='Title', max_length=100, 14 | widget=forms.TextInput( 15 | attrs={'placeholder': 'Title'})) 16 | link = forms.CharField(label="Link", max_length=250, 17 | widget=forms.TextInput( 18 | attrs={'placeholder': 'RSS Url'})) 19 | 20 | if request.method == 'POST': 21 | form = RSSForm(request.POST) 22 | if form.is_valid(): 23 | try: 24 | rss_source = RSSSource.objects.get(url=form.cleaned_data['link']) 25 | except RSSSource.DoesNotExist: 26 | rss_source = RSSSource(title=form.cleaned_data['title'], 27 | alias="rss::" + form.cleaned_data['link'], 28 | url=form.cleaned_data['link']) 29 | rss_source.save() 30 | reader.subscribe(rss_source) 31 | return HttpResponseRedirect('/reader/subscriptions') 32 | 33 | rss_form = RSSForm() 34 | source_list = Source.objects 35 | 36 | return render(request, 'reader/subscriptions.html', 37 | {'source_list': source_list, 38 | 'rss_form': rss_form, 39 | 'reader': reader}) 40 | -------------------------------------------------------------------------------- /static/admin/css/changelists.css: -------------------------------------------------------------------------------- 1 | /* CHANGELISTS */ 2 | 3 | #changelist { 4 | position: relative; 5 | width: 100%; 6 | } 7 | 8 | #changelist table { 9 | width: 100%; 10 | } 11 | 12 | .change-list .hiddenfields { display:none; } 13 | 14 | .change-list .filtered table { 15 | border-right: 1px solid #ddd; 16 | } 17 | 18 | .change-list .filtered { 19 | min-height: 400px; 20 | } 21 | 22 | .change-list .filtered { 23 | background: white url(../img/changelist-bg.gif) top right repeat-y !important; 24 | } 25 | 26 | .change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { 27 | margin-right: 160px !important; 28 | width: auto !important; 29 | } 30 | 31 | .change-list .filtered table tbody th { 32 | padding-right: 1em; 33 | } 34 | 35 | #changelist-form .results { 36 | overflow-x: auto; 37 | } 38 | 39 | #changelist .toplinks { 40 | border-bottom: 1px solid #ccc !important; 41 | } 42 | 43 | #changelist .paginator { 44 | color: #666; 45 | border-top: 1px solid #eee; 46 | border-bottom: 1px solid #eee; 47 | background: white url(../img/nav-bg.gif) 0 180% repeat-x; 48 | overflow: hidden; 49 | } 50 | 51 | .change-list .filtered .paginator { 52 | border-right: 1px solid #ddd; 53 | } 54 | 55 | /* CHANGELIST TABLES */ 56 | 57 | #changelist table thead th { 58 | padding: 0; 59 | white-space: nowrap; 60 | vertical-align: middle; 61 | } 62 | 63 | #changelist table thead th.action-checkbox-column { 64 | width: 1.5em; 65 | text-align: center; 66 | } 67 | 68 | #changelist table tbody td, #changelist table tbody th { 69 | border-left: 1px solid #ddd; 70 | } 71 | 72 | #changelist table tbody td:first-child, #changelist table tbody th:first-child { 73 | border-left: 0; 74 | border-right: 1px solid #ddd; 75 | } 76 | 77 | #changelist table tbody td.action-checkbox { 78 | text-align:center; 79 | } 80 | 81 | #changelist table tfoot { 82 | color: #666; 83 | } 84 | 85 | /* TOOLBAR */ 86 | 87 | #changelist #toolbar { 88 | padding: 3px; 89 | border-bottom: 1px solid #ddd; 90 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 91 | color: #666; 92 | } 93 | 94 | #changelist #toolbar form input { 95 | font-size: 11px; 96 | padding: 1px 2px; 97 | } 98 | 99 | #changelist #toolbar form #searchbar { 100 | padding: 2px; 101 | } 102 | 103 | #changelist #changelist-search img { 104 | vertical-align: middle; 105 | } 106 | 107 | /* FILTER COLUMN */ 108 | 109 | #changelist-filter { 110 | position: absolute; 111 | top: 0; 112 | right: 0; 113 | z-index: 1000; 114 | width: 160px; 115 | border-left: 1px solid #ddd; 116 | background: #efefef; 117 | margin: 0; 118 | } 119 | 120 | #changelist-filter h2 { 121 | font-size: 11px; 122 | padding: 2px 5px; 123 | border-bottom: 1px solid #ddd; 124 | } 125 | 126 | #changelist-filter h3 { 127 | font-size: 12px; 128 | margin-bottom: 0; 129 | } 130 | 131 | #changelist-filter ul { 132 | padding-left: 0; 133 | margin-left: 10px; 134 | } 135 | 136 | #changelist-filter li { 137 | list-style-type: none; 138 | margin-left: 0; 139 | padding-left: 0; 140 | } 141 | 142 | #changelist-filter a { 143 | color: #999; 144 | } 145 | 146 | #changelist-filter a:hover { 147 | color: #036; 148 | } 149 | 150 | #changelist-filter li.selected { 151 | border-left: 5px solid #ccc; 152 | padding-left: 5px; 153 | margin-left: -10px; 154 | } 155 | 156 | #changelist-filter li.selected a { 157 | color: #5b80b2 !important; 158 | } 159 | 160 | /* DATE DRILLDOWN */ 161 | 162 | .change-list ul.toplinks { 163 | display: block; 164 | background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x; 165 | border-top: 1px solid white; 166 | float: left; 167 | padding: 0 !important; 168 | margin: 0 !important; 169 | width: 100%; 170 | } 171 | 172 | .change-list ul.toplinks li { 173 | padding: 3px 6px; 174 | font-weight: bold; 175 | list-style-type: none; 176 | display: inline-block; 177 | } 178 | 179 | .change-list ul.toplinks .date-back a { 180 | color: #999; 181 | } 182 | 183 | .change-list ul.toplinks .date-back a:hover { 184 | color: #036; 185 | } 186 | 187 | /* PAGINATOR */ 188 | 189 | .paginator { 190 | font-size: 11px; 191 | padding-top: 10px; 192 | padding-bottom: 10px; 193 | line-height: 22px; 194 | margin: 0; 195 | border-top: 1px solid #ddd; 196 | } 197 | 198 | .paginator a:link, .paginator a:visited { 199 | padding: 2px 6px; 200 | border: solid 1px #ccc; 201 | background: white; 202 | text-decoration: none; 203 | } 204 | 205 | .paginator a.showall { 206 | padding: 0 !important; 207 | border: none !important; 208 | } 209 | 210 | .paginator a.showall:hover { 211 | color: #036 !important; 212 | background: transparent !important; 213 | } 214 | 215 | .paginator .end { 216 | border-width: 2px !important; 217 | margin-right: 6px; 218 | } 219 | 220 | .paginator .this-page { 221 | padding: 2px 6px; 222 | font-weight: bold; 223 | font-size: 13px; 224 | vertical-align: top; 225 | } 226 | 227 | .paginator a:hover { 228 | color: white; 229 | background: #5b80b2; 230 | border-color: #036; 231 | } 232 | 233 | /* ACTIONS */ 234 | 235 | .filtered .actions { 236 | margin-right: 160px !important; 237 | border-right: 1px solid #ddd; 238 | } 239 | 240 | #changelist table input { 241 | margin: 0; 242 | } 243 | 244 | #changelist table tbody tr.selected { 245 | background-color: #FFFFCC; 246 | } 247 | 248 | #changelist .actions { 249 | color: #999; 250 | padding: 3px; 251 | border-top: 1px solid #fff; 252 | border-bottom: 1px solid #ddd; 253 | background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x; 254 | } 255 | 256 | #changelist .actions.selected { 257 | background: #fffccf; 258 | border-top: 1px solid #fffee8; 259 | border-bottom: 1px solid #edecd6; 260 | } 261 | 262 | #changelist .actions span.all, 263 | #changelist .actions span.action-counter, 264 | #changelist .actions span.clear, 265 | #changelist .actions span.question { 266 | font-size: 11px; 267 | margin: 0 0.5em; 268 | display: none; 269 | } 270 | 271 | #changelist .actions:last-child { 272 | border-bottom: none; 273 | } 274 | 275 | #changelist .actions select { 276 | border: 1px solid #aaa; 277 | margin-left: 0.5em; 278 | padding: 1px 2px; 279 | } 280 | 281 | #changelist .actions label { 282 | font-size: 11px; 283 | margin-left: 0.5em; 284 | } 285 | 286 | #changelist #action-toggle { 287 | display: none; 288 | } 289 | 290 | #changelist .actions .button { 291 | font-size: 11px; 292 | padding: 1px 2px; 293 | } 294 | -------------------------------------------------------------------------------- /static/admin/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* DASHBOARD */ 2 | 3 | .dashboard .module table th { 4 | width: 100%; 5 | } 6 | 7 | .dashboard .module table td { 8 | white-space: nowrap; 9 | } 10 | 11 | .dashboard .module table td a { 12 | display: block; 13 | padding-right: .6em; 14 | } 15 | 16 | /* RECENT ACTIONS MODULE */ 17 | 18 | .module ul.actionlist { 19 | margin-left: 0; 20 | } 21 | 22 | ul.actionlist li { 23 | list-style-type: none; 24 | } 25 | 26 | ul.actionlist li { 27 | overflow: hidden; 28 | text-overflow: ellipsis; 29 | -o-text-overflow: ellipsis; 30 | } 31 | -------------------------------------------------------------------------------- /static/admin/css/forms.css: -------------------------------------------------------------------------------- 1 | @import url('widgets.css'); 2 | 3 | /* FORM ROWS */ 4 | 5 | .form-row { 6 | overflow: hidden; 7 | padding: 8px 12px; 8 | font-size: 11px; 9 | border-bottom: 1px solid #eee; 10 | } 11 | 12 | .form-row img, .form-row input { 13 | vertical-align: middle; 14 | } 15 | 16 | form .form-row p { 17 | padding-left: 0; 18 | font-size: 11px; 19 | } 20 | 21 | .hidden { 22 | display: none; 23 | } 24 | 25 | /* FORM LABELS */ 26 | 27 | form h4 { 28 | margin: 0 !important; 29 | padding: 0 !important; 30 | border: none !important; 31 | } 32 | 33 | label { 34 | font-weight: normal !important; 35 | color: #666; 36 | font-size: 12px; 37 | } 38 | 39 | .required label, label.required { 40 | font-weight: bold !important; 41 | color: #333 !important; 42 | } 43 | 44 | /* RADIO BUTTONS */ 45 | 46 | form ul.radiolist li { 47 | list-style-type: none; 48 | } 49 | 50 | form ul.radiolist label { 51 | float: none; 52 | display: inline; 53 | } 54 | 55 | form ul.inline { 56 | margin-left: 0; 57 | padding: 0; 58 | } 59 | 60 | form ul.inline li { 61 | float: left; 62 | padding-right: 7px; 63 | } 64 | 65 | /* ALIGNED FIELDSETS */ 66 | 67 | .aligned label { 68 | display: block; 69 | padding: 3px 10px 0 0; 70 | float: left; 71 | width: 8em; 72 | word-wrap: break-word; 73 | } 74 | 75 | .aligned ul label { 76 | display: inline; 77 | float: none; 78 | width: auto; 79 | } 80 | 81 | .colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { 82 | width: 350px; 83 | } 84 | 85 | form .aligned p, form .aligned ul { 86 | margin-left: 7em; 87 | padding-left: 30px; 88 | } 89 | 90 | form .aligned table p { 91 | margin-left: 0; 92 | padding-left: 0; 93 | } 94 | 95 | form .aligned p.help { 96 | padding-left: 38px; 97 | } 98 | 99 | .aligned .vCheckboxLabel { 100 | float: none !important; 101 | display: inline; 102 | padding-left: 4px; 103 | } 104 | 105 | .colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField { 106 | width: 610px; 107 | } 108 | 109 | .checkbox-row p.help { 110 | margin-left: 0; 111 | padding-left: 0 !important; 112 | } 113 | 114 | fieldset .field-box { 115 | float: left; 116 | margin-right: 20px; 117 | } 118 | 119 | /* WIDE FIELDSETS */ 120 | 121 | .wide label { 122 | width: 15em !important; 123 | } 124 | 125 | form .wide p { 126 | margin-left: 15em; 127 | } 128 | 129 | form .wide p.help { 130 | padding-left: 38px; 131 | } 132 | 133 | .colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField { 134 | width: 450px; 135 | } 136 | 137 | /* COLLAPSED FIELDSETS */ 138 | 139 | fieldset.collapsed * { 140 | display: none; 141 | } 142 | 143 | fieldset.collapsed h2, fieldset.collapsed { 144 | display: block !important; 145 | } 146 | 147 | fieldset.collapsed h2 { 148 | background-image: url(../img/nav-bg.gif); 149 | background-position: bottom left; 150 | color: #999; 151 | } 152 | 153 | fieldset.collapsed .collapse-toggle { 154 | background: transparent; 155 | display: inline !important; 156 | } 157 | 158 | /* MONOSPACE TEXTAREAS */ 159 | 160 | fieldset.monospace textarea { 161 | font-family: "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace; 162 | } 163 | 164 | /* SUBMIT ROW */ 165 | 166 | .submit-row { 167 | padding: 5px 7px; 168 | text-align: right; 169 | background: white url(../img/nav-bg.gif) 0 100% repeat-x; 170 | border: 1px solid #ccc; 171 | margin: 5px 0; 172 | overflow: hidden; 173 | } 174 | 175 | body.popup .submit-row { 176 | overflow: auto; 177 | } 178 | 179 | .submit-row input { 180 | margin: 0 0 0 5px; 181 | } 182 | 183 | .submit-row p { 184 | margin: 0.3em; 185 | } 186 | 187 | .submit-row p.deletelink-box { 188 | float: left; 189 | } 190 | 191 | .submit-row .deletelink { 192 | background: url(../img/icon_deletelink.gif) 0 50% no-repeat; 193 | padding-left: 14px; 194 | } 195 | 196 | /* CUSTOM FORM FIELDS */ 197 | 198 | .vSelectMultipleField { 199 | vertical-align: top !important; 200 | } 201 | 202 | .vCheckboxField { 203 | border: none; 204 | } 205 | 206 | .vDateField, .vTimeField { 207 | margin-right: 2px; 208 | } 209 | 210 | .vDateField { 211 | min-width: 6.85em; 212 | } 213 | 214 | .vTimeField { 215 | min-width: 4.7em; 216 | } 217 | 218 | .vURLField { 219 | width: 30em; 220 | } 221 | 222 | .vLargeTextField, .vXMLLargeTextField { 223 | width: 48em; 224 | } 225 | 226 | .flatpages-flatpage #id_content { 227 | height: 40.2em; 228 | } 229 | 230 | .module table .vPositiveSmallIntegerField { 231 | width: 2.2em; 232 | } 233 | 234 | .vTextField { 235 | width: 20em; 236 | } 237 | 238 | .vIntegerField { 239 | width: 5em; 240 | } 241 | 242 | .vBigIntegerField { 243 | width: 10em; 244 | } 245 | 246 | .vForeignKeyRawIdAdminField { 247 | width: 5em; 248 | } 249 | 250 | /* INLINES */ 251 | 252 | .inline-group { 253 | padding: 0; 254 | border: 1px solid #ccc; 255 | margin: 10px 0; 256 | } 257 | 258 | .inline-group .aligned label { 259 | width: 8em; 260 | } 261 | 262 | .inline-related { 263 | position: relative; 264 | } 265 | 266 | .inline-related h3 { 267 | margin: 0; 268 | color: #666; 269 | padding: 3px 5px; 270 | font-size: 11px; 271 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 272 | border-bottom: 1px solid #ddd; 273 | } 274 | 275 | .inline-related h3 span.delete { 276 | float: right; 277 | } 278 | 279 | .inline-related h3 span.delete label { 280 | margin-left: 2px; 281 | font-size: 11px; 282 | } 283 | 284 | .inline-related fieldset { 285 | margin: 0; 286 | background: #fff; 287 | border: none; 288 | width: 100%; 289 | } 290 | 291 | .inline-related fieldset.module h3 { 292 | margin: 0; 293 | padding: 2px 5px 3px 5px; 294 | font-size: 11px; 295 | text-align: left; 296 | font-weight: bold; 297 | background: #bcd; 298 | color: #fff; 299 | } 300 | 301 | .inline-group .tabular fieldset.module { 302 | border: none; 303 | border-bottom: 1px solid #ddd; 304 | } 305 | 306 | .inline-related.tabular fieldset.module table { 307 | width: 100%; 308 | } 309 | 310 | .last-related fieldset { 311 | border: none; 312 | } 313 | 314 | .inline-group .tabular tr.has_original td { 315 | padding-top: 2em; 316 | } 317 | 318 | .inline-group .tabular tr td.original { 319 | padding: 2px 0 0 0; 320 | width: 0; 321 | _position: relative; 322 | } 323 | 324 | .inline-group .tabular th.original { 325 | width: 0px; 326 | padding: 0; 327 | } 328 | 329 | .inline-group .tabular td.original p { 330 | position: absolute; 331 | left: 0; 332 | height: 1.1em; 333 | padding: 2px 7px; 334 | overflow: hidden; 335 | font-size: 9px; 336 | font-weight: bold; 337 | color: #666; 338 | _width: 700px; 339 | } 340 | 341 | .inline-group ul.tools { 342 | padding: 0; 343 | margin: 0; 344 | list-style: none; 345 | } 346 | 347 | .inline-group ul.tools li { 348 | display: inline; 349 | padding: 0 5px; 350 | } 351 | 352 | .inline-group div.add-row, 353 | .inline-group .tabular tr.add-row td { 354 | color: #666; 355 | padding: 3px 5px; 356 | border-bottom: 1px solid #ddd; 357 | background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x; 358 | } 359 | 360 | .inline-group .tabular tr.add-row td { 361 | padding: 4px 5px 3px; 362 | border-bottom: none; 363 | } 364 | 365 | .inline-group ul.tools a.add, 366 | .inline-group div.add-row a, 367 | .inline-group .tabular tr.add-row td a { 368 | background: url(../img/icon_addlink.gif) 0 50% no-repeat; 369 | padding-left: 14px; 370 | font-size: 11px; 371 | outline: 0; /* Remove dotted border around link */ 372 | } 373 | 374 | .empty-form { 375 | display: none; 376 | } 377 | -------------------------------------------------------------------------------- /static/admin/css/ie.css: -------------------------------------------------------------------------------- 1 | /* IE 6 & 7 */ 2 | 3 | /* Proper fixed width for dashboard in IE6 */ 4 | 5 | .dashboard #content { 6 | *width: 768px; 7 | } 8 | 9 | .dashboard #content-main { 10 | *width: 535px; 11 | } 12 | 13 | /* IE 6 ONLY */ 14 | 15 | /* Keep header from flowing off the page */ 16 | 17 | #container { 18 | _position: static; 19 | } 20 | 21 | /* Put the right sidebars back on the page */ 22 | 23 | .colMS #content-related { 24 | _margin-right: 0; 25 | _margin-left: 10px; 26 | _position: static; 27 | } 28 | 29 | /* Put the left sidebars back on the page */ 30 | 31 | .colSM #content-related { 32 | _margin-right: 10px; 33 | _margin-left: -115px; 34 | _position: static; 35 | } 36 | 37 | .form-row { 38 | _height: 1%; 39 | } 40 | 41 | /* Fix right margin for changelist filters in IE6 */ 42 | 43 | #changelist-filter ul { 44 | _margin-right: -10px; 45 | } 46 | 47 | /* IE ignores min-height, but treats height as if it were min-height */ 48 | 49 | .change-list .filtered { 50 | _height: 400px; 51 | } 52 | 53 | /* IE doesn't know alpha transparency in PNGs */ 54 | 55 | .inline-deletelink { 56 | background: transparent url(../img/inline-delete-8bit.png) no-repeat; 57 | } 58 | 59 | /* IE7 doesn't support inline-block */ 60 | .change-list ul.toplinks li { 61 | zoom: 1; 62 | *display: inline; 63 | } -------------------------------------------------------------------------------- /static/admin/css/login.css: -------------------------------------------------------------------------------- 1 | /* LOGIN FORM */ 2 | 3 | body.login { 4 | background: #eee; 5 | } 6 | 7 | .login #container { 8 | background: white; 9 | border: 1px solid #ccc; 10 | width: 28em; 11 | min-width: 300px; 12 | margin-left: auto; 13 | margin-right: auto; 14 | margin-top: 100px; 15 | } 16 | 17 | .login #content-main { 18 | width: 100%; 19 | } 20 | 21 | .login form { 22 | margin-top: 1em; 23 | } 24 | 25 | .login .form-row { 26 | padding: 4px 0; 27 | float: left; 28 | width: 100%; 29 | } 30 | 31 | .login .form-row label { 32 | padding-right: 0.5em; 33 | line-height: 2em; 34 | font-size: 1em; 35 | clear: both; 36 | color: #333; 37 | } 38 | 39 | .login .form-row #id_username, .login .form-row #id_password { 40 | clear: both; 41 | padding: 6px; 42 | width: 100%; 43 | -webkit-box-sizing: border-box; 44 | -moz-box-sizing: border-box; 45 | box-sizing: border-box; 46 | } 47 | 48 | .login span.help { 49 | font-size: 10px; 50 | display: block; 51 | } 52 | 53 | .login .submit-row { 54 | clear: both; 55 | padding: 1em 0 0 9.4em; 56 | } 57 | 58 | .login .password-reset-link { 59 | text-align: center; 60 | } 61 | -------------------------------------------------------------------------------- /static/admin/css/rtl.css: -------------------------------------------------------------------------------- 1 | body { 2 | direction: rtl; 3 | } 4 | 5 | /* LOGIN */ 6 | 7 | .login .form-row { 8 | float: right; 9 | } 10 | 11 | .login .form-row label { 12 | float: right; 13 | padding-left: 0.5em; 14 | padding-right: 0; 15 | text-align: left; 16 | } 17 | 18 | .login .submit-row { 19 | clear: both; 20 | padding: 1em 9.4em 0 0; 21 | } 22 | 23 | /* GLOBAL */ 24 | 25 | th { 26 | text-align: right; 27 | } 28 | 29 | .module h2, .module caption { 30 | text-align: right; 31 | } 32 | 33 | .addlink, .changelink { 34 | padding-left: 0px; 35 | padding-right: 12px; 36 | background-position: 100% 0.2em; 37 | } 38 | 39 | .deletelink { 40 | padding-left: 0px; 41 | padding-right: 12px; 42 | background-position: 100% 0.25em; 43 | } 44 | 45 | .object-tools { 46 | float: left; 47 | } 48 | 49 | thead th:first-child, 50 | tfoot td:first-child { 51 | border-left: 1px solid #ddd !important; 52 | } 53 | 54 | /* LAYOUT */ 55 | 56 | #user-tools { 57 | right: auto; 58 | left: 0; 59 | text-align: left; 60 | } 61 | 62 | div.breadcrumbs { 63 | text-align: right; 64 | } 65 | 66 | #content-main { 67 | float: right; 68 | } 69 | 70 | #content-related { 71 | float: left; 72 | margin-left: -19em; 73 | margin-right: auto; 74 | } 75 | 76 | .colMS { 77 | margin-left: 20em !important; 78 | margin-right: 10px !important; 79 | } 80 | 81 | /* SORTABLE TABLES */ 82 | 83 | table thead th.sorted .sortoptions { 84 | float: left; 85 | } 86 | 87 | thead th.sorted .text { 88 | padding-right: 0; 89 | padding-left: 42px; 90 | } 91 | 92 | /* dashboard styles */ 93 | 94 | .dashboard .module table td a { 95 | padding-left: .6em; 96 | padding-right: 12px; 97 | } 98 | 99 | /* changelists styles */ 100 | 101 | .change-list .filtered { 102 | background: white url(../img/changelist-bg_rtl.gif) top left repeat-y !important; 103 | } 104 | 105 | .change-list .filtered table { 106 | border-left: 1px solid #ddd; 107 | border-right: 0px none; 108 | } 109 | 110 | #changelist-filter { 111 | right: auto; 112 | left: 0; 113 | border-left: 0px none; 114 | border-right: 1px solid #ddd; 115 | } 116 | 117 | .change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { 118 | margin-right: 0px !important; 119 | margin-left: 160px !important; 120 | } 121 | 122 | #changelist-filter li.selected { 123 | border-left: 0px none; 124 | padding-left: 0px; 125 | margin-left: 0; 126 | border-right: 5px solid #ccc; 127 | padding-right: 5px; 128 | margin-right: -10px; 129 | } 130 | 131 | .filtered .actions { 132 | border-left:1px solid #DDDDDD; 133 | margin-left:160px !important; 134 | border-right: 0 none; 135 | margin-right:0 !important; 136 | } 137 | 138 | #changelist table tbody td:first-child, #changelist table tbody th:first-child { 139 | border-right: 0; 140 | border-left: 1px solid #ddd; 141 | } 142 | 143 | /* FORMS */ 144 | 145 | .aligned label { 146 | padding: 0 0 3px 1em; 147 | float: right; 148 | } 149 | 150 | .submit-row { 151 | text-align: left 152 | } 153 | 154 | .submit-row p.deletelink-box { 155 | float: right; 156 | } 157 | 158 | .submit-row .deletelink { 159 | background: url(../img/icon_deletelink.gif) 0 50% no-repeat; 160 | padding-right: 14px; 161 | } 162 | 163 | .vDateField, .vTimeField { 164 | margin-left: 2px; 165 | } 166 | 167 | form ul.inline li { 168 | float: right; 169 | padding-right: 0; 170 | padding-left: 7px; 171 | } 172 | 173 | input[type=submit].default, .submit-row input.default { 174 | float: left; 175 | } 176 | 177 | fieldset .field-box { 178 | float: right; 179 | margin-left: 20px; 180 | margin-right: 0; 181 | } 182 | 183 | .errorlist li { 184 | background-position: 100% .3em; 185 | padding: 4px 25px 4px 5px; 186 | } 187 | 188 | .errornote { 189 | background-position: 100% .3em; 190 | padding: 4px 25px 4px 5px; 191 | } 192 | 193 | /* WIDGETS */ 194 | 195 | .calendarnav-previous { 196 | top: 0; 197 | left: auto; 198 | right: 0; 199 | } 200 | 201 | .calendarnav-next { 202 | top: 0; 203 | right: auto; 204 | left: 0; 205 | } 206 | 207 | .calendar caption, .calendarbox h2 { 208 | text-align: center; 209 | } 210 | 211 | .selector { 212 | float: right; 213 | } 214 | 215 | .selector .selector-filter { 216 | text-align: right; 217 | } 218 | 219 | .inline-deletelink { 220 | float: left; 221 | } 222 | 223 | /* MISC */ 224 | 225 | .inline-related h2, .inline-group h2 { 226 | text-align: right 227 | } 228 | 229 | .inline-related h3 span.delete { 230 | padding-right: 20px; 231 | padding-left: inherit; 232 | left: 10px; 233 | right: inherit; 234 | float:left; 235 | } 236 | 237 | .inline-related h3 span.delete label { 238 | margin-left: inherit; 239 | margin-right: 2px; 240 | } 241 | 242 | /* IE7 specific bug fixes */ 243 | 244 | div.colM { 245 | position: relative; 246 | } 247 | 248 | .submit-row input { 249 | float: left; 250 | } -------------------------------------------------------------------------------- /static/admin/img/changelist-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/changelist-bg.gif -------------------------------------------------------------------------------- /static/admin/img/changelist-bg_rtl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/changelist-bg_rtl.gif -------------------------------------------------------------------------------- /static/admin/img/default-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/default-bg-reverse.gif -------------------------------------------------------------------------------- /static/admin/img/default-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/default-bg.gif -------------------------------------------------------------------------------- /static/admin/img/deleted-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/deleted-overlay.gif -------------------------------------------------------------------------------- /static/admin/img/gis/move_vertex_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/gis/move_vertex_off.png -------------------------------------------------------------------------------- /static/admin/img/gis/move_vertex_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/gis/move_vertex_on.png -------------------------------------------------------------------------------- /static/admin/img/icon-no.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon-no.gif -------------------------------------------------------------------------------- /static/admin/img/icon-unknown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon-unknown.gif -------------------------------------------------------------------------------- /static/admin/img/icon-yes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon-yes.gif -------------------------------------------------------------------------------- /static/admin/img/icon_addlink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_addlink.gif -------------------------------------------------------------------------------- /static/admin/img/icon_alert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_alert.gif -------------------------------------------------------------------------------- /static/admin/img/icon_calendar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_calendar.gif -------------------------------------------------------------------------------- /static/admin/img/icon_changelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_changelink.gif -------------------------------------------------------------------------------- /static/admin/img/icon_clock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_clock.gif -------------------------------------------------------------------------------- /static/admin/img/icon_deletelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_deletelink.gif -------------------------------------------------------------------------------- /static/admin/img/icon_error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_error.gif -------------------------------------------------------------------------------- /static/admin/img/icon_searchbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_searchbox.png -------------------------------------------------------------------------------- /static/admin/img/icon_success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/icon_success.gif -------------------------------------------------------------------------------- /static/admin/img/inline-delete-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/inline-delete-8bit.png -------------------------------------------------------------------------------- /static/admin/img/inline-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/inline-delete.png -------------------------------------------------------------------------------- /static/admin/img/inline-restore-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/inline-restore-8bit.png -------------------------------------------------------------------------------- /static/admin/img/inline-restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/inline-restore.png -------------------------------------------------------------------------------- /static/admin/img/inline-splitter-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/inline-splitter-bg.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg-grabber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/nav-bg-grabber.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/nav-bg-reverse.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg-selected.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/nav-bg-selected.gif -------------------------------------------------------------------------------- /static/admin/img/nav-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/nav-bg.gif -------------------------------------------------------------------------------- /static/admin/img/selector-icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/selector-icons.gif -------------------------------------------------------------------------------- /static/admin/img/selector-search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/selector-search.gif -------------------------------------------------------------------------------- /static/admin/img/sorting-icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/sorting-icons.gif -------------------------------------------------------------------------------- /static/admin/img/tooltag-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/tooltag-add.png -------------------------------------------------------------------------------- /static/admin/img/tooltag-arrowright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/admin/img/tooltag-arrowright.png -------------------------------------------------------------------------------- /static/admin/js/LICENSE-JQUERY.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 John Resig, http://jquery.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /static/admin/js/SelectBox.js: -------------------------------------------------------------------------------- 1 | var SelectBox = { 2 | cache: new Object(), 3 | init: function(id) { 4 | var box = document.getElementById(id); 5 | var node; 6 | SelectBox.cache[id] = new Array(); 7 | var cache = SelectBox.cache[id]; 8 | for (var i = 0; (node = box.options[i]); i++) { 9 | cache.push({value: node.value, text: node.text, displayed: 1}); 10 | } 11 | }, 12 | redisplay: function(id) { 13 | // Repopulate HTML select box from cache 14 | var box = document.getElementById(id); 15 | box.options.length = 0; // clear all options 16 | for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) { 17 | var node = SelectBox.cache[id][i]; 18 | if (node.displayed) { 19 | var new_option = new Option(node.text, node.value, false, false); 20 | // Shows a tooltip when hovering over the option 21 | new_option.setAttribute("title", node.text); 22 | box.options[box.options.length] = new_option; 23 | } 24 | } 25 | }, 26 | filter: function(id, text) { 27 | // Redisplay the HTML select box, displaying only the choices containing ALL 28 | // the words in text. (It's an AND search.) 29 | var tokens = text.toLowerCase().split(/\s+/); 30 | var node, token; 31 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 32 | node.displayed = 1; 33 | for (var j = 0; (token = tokens[j]); j++) { 34 | if (node.text.toLowerCase().indexOf(token) == -1) { 35 | node.displayed = 0; 36 | } 37 | } 38 | } 39 | SelectBox.redisplay(id); 40 | }, 41 | delete_from_cache: function(id, value) { 42 | var node, delete_index = null; 43 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 44 | if (node.value == value) { 45 | delete_index = i; 46 | break; 47 | } 48 | } 49 | var j = SelectBox.cache[id].length - 1; 50 | for (var i = delete_index; i < j; i++) { 51 | SelectBox.cache[id][i] = SelectBox.cache[id][i+1]; 52 | } 53 | SelectBox.cache[id].length--; 54 | }, 55 | add_to_cache: function(id, option) { 56 | SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1}); 57 | }, 58 | cache_contains: function(id, value) { 59 | // Check if an item is contained in the cache 60 | var node; 61 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 62 | if (node.value == value) { 63 | return true; 64 | } 65 | } 66 | return false; 67 | }, 68 | move: function(from, to) { 69 | var from_box = document.getElementById(from); 70 | var to_box = document.getElementById(to); 71 | var option; 72 | for (var i = 0; (option = from_box.options[i]); i++) { 73 | if (option.selected && SelectBox.cache_contains(from, option.value)) { 74 | SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 75 | SelectBox.delete_from_cache(from, option.value); 76 | } 77 | } 78 | SelectBox.redisplay(from); 79 | SelectBox.redisplay(to); 80 | }, 81 | move_all: function(from, to) { 82 | var from_box = document.getElementById(from); 83 | var to_box = document.getElementById(to); 84 | var option; 85 | for (var i = 0; (option = from_box.options[i]); i++) { 86 | if (SelectBox.cache_contains(from, option.value)) { 87 | SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 88 | SelectBox.delete_from_cache(from, option.value); 89 | } 90 | } 91 | SelectBox.redisplay(from); 92 | SelectBox.redisplay(to); 93 | }, 94 | sort: function(id) { 95 | SelectBox.cache[id].sort( function(a, b) { 96 | a = a.text.toLowerCase(); 97 | b = b.text.toLowerCase(); 98 | try { 99 | if (a > b) return 1; 100 | if (a < b) return -1; 101 | } 102 | catch (e) { 103 | // silently fail on IE 'unknown' exception 104 | } 105 | return 0; 106 | } ); 107 | }, 108 | select_all: function(id) { 109 | var box = document.getElementById(id); 110 | for (var i = 0; i < box.options.length; i++) { 111 | box.options[i].selected = 'selected'; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /static/admin/js/actions.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var lastChecked; 3 | 4 | $.fn.actions = function(opts) { 5 | var options = $.extend({}, $.fn.actions.defaults, opts); 6 | var actionCheckboxes = $(this); 7 | var list_editable_changed = false; 8 | var checker = function(checked) { 9 | if (checked) { 10 | showQuestion(); 11 | } else { 12 | reset(); 13 | } 14 | $(actionCheckboxes).prop("checked", checked) 15 | .parent().parent().toggleClass(options.selectedClass, checked); 16 | }, 17 | updateCounter = function() { 18 | var sel = $(actionCheckboxes).filter(":checked").length; 19 | // _actions_icnt is defined in the generated HTML 20 | // and contains the total amount of objects in the queryset 21 | $(options.counterContainer).html(interpolate( 22 | ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { 23 | sel: sel, 24 | cnt: _actions_icnt 25 | }, true)); 26 | $(options.allToggle).prop("checked", function() { 27 | var value; 28 | if (sel == actionCheckboxes.length) { 29 | value = true; 30 | showQuestion(); 31 | } else { 32 | value = false; 33 | clearAcross(); 34 | } 35 | return value; 36 | }); 37 | }, 38 | showQuestion = function() { 39 | $(options.acrossClears).hide(); 40 | $(options.acrossQuestions).show(); 41 | $(options.allContainer).hide(); 42 | }, 43 | showClear = function() { 44 | $(options.acrossClears).show(); 45 | $(options.acrossQuestions).hide(); 46 | $(options.actionContainer).toggleClass(options.selectedClass); 47 | $(options.allContainer).show(); 48 | $(options.counterContainer).hide(); 49 | }, 50 | reset = function() { 51 | $(options.acrossClears).hide(); 52 | $(options.acrossQuestions).hide(); 53 | $(options.allContainer).hide(); 54 | $(options.counterContainer).show(); 55 | }, 56 | clearAcross = function() { 57 | reset(); 58 | $(options.acrossInput).val(0); 59 | $(options.actionContainer).removeClass(options.selectedClass); 60 | }; 61 | // Show counter by default 62 | $(options.counterContainer).show(); 63 | // Check state of checkboxes and reinit state if needed 64 | $(this).filter(":checked").each(function(i) { 65 | $(this).parent().parent().toggleClass(options.selectedClass); 66 | updateCounter(); 67 | if ($(options.acrossInput).val() == 1) { 68 | showClear(); 69 | } 70 | }); 71 | $(options.allToggle).show().click(function() { 72 | checker($(this).prop("checked")); 73 | updateCounter(); 74 | }); 75 | $("a", options.acrossQuestions).click(function(event) { 76 | event.preventDefault(); 77 | $(options.acrossInput).val(1); 78 | showClear(); 79 | }); 80 | $("a", options.acrossClears).click(function(event) { 81 | event.preventDefault(); 82 | $(options.allToggle).prop("checked", false); 83 | clearAcross(); 84 | checker(0); 85 | updateCounter(); 86 | }); 87 | lastChecked = null; 88 | $(actionCheckboxes).click(function(event) { 89 | if (!event) { event = window.event; } 90 | var target = event.target ? event.target : event.srcElement; 91 | if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey === true) { 92 | var inrange = false; 93 | $(lastChecked).prop("checked", target.checked) 94 | .parent().parent().toggleClass(options.selectedClass, target.checked); 95 | $(actionCheckboxes).each(function() { 96 | if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) { 97 | inrange = (inrange) ? false : true; 98 | } 99 | if (inrange) { 100 | $(this).prop("checked", target.checked) 101 | .parent().parent().toggleClass(options.selectedClass, target.checked); 102 | } 103 | }); 104 | } 105 | $(target).parent().parent().toggleClass(options.selectedClass, target.checked); 106 | lastChecked = target; 107 | updateCounter(); 108 | }); 109 | $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() { 110 | list_editable_changed = true; 111 | }); 112 | $('form#changelist-form button[name="index"]').click(function(event) { 113 | if (list_editable_changed) { 114 | return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); 115 | } 116 | }); 117 | $('form#changelist-form input[name="_save"]').click(function(event) { 118 | var action_changed = false; 119 | $('select option:selected', options.actionContainer).each(function() { 120 | if ($(this).val()) { 121 | action_changed = true; 122 | } 123 | }); 124 | if (action_changed) { 125 | if (list_editable_changed) { 126 | return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")); 127 | } else { 128 | return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button.")); 129 | } 130 | } 131 | }); 132 | }; 133 | /* Setup plugin defaults */ 134 | $.fn.actions.defaults = { 135 | actionContainer: "div.actions", 136 | counterContainer: "span.action-counter", 137 | allContainer: "div.actions span.all", 138 | acrossInput: "div.actions input.select-across", 139 | acrossQuestions: "div.actions span.question", 140 | acrossClears: "div.actions span.clear", 141 | allToggle: "#action-toggle", 142 | selectedClass: "selected" 143 | }; 144 | })(django.jQuery); 145 | -------------------------------------------------------------------------------- /static/admin/js/actions.min.js: -------------------------------------------------------------------------------- 1 | (function(a){var f;a.fn.actions=function(q){var b=a.extend({},a.fn.actions.defaults,q),g=a(this),e=!1,m=function(c){c?k():l();a(g).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},h=function(){var c=a(g).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).prop("checked",function(){var a;c==g.length?(a=!0,k()):(a=!1,n());return a})},k=function(){a(b.acrossClears).hide(); 2 | a(b.acrossQuestions).show();a(b.allContainer).hide()},p=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},l=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},n=function(){l();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();a(this).filter(":checked").each(function(c){a(this).parent().parent().toggleClass(b.selectedClass); 3 | h();1==a(b.acrossInput).val()&&p()});a(b.allToggle).show().click(function(){m(a(this).prop("checked"));h()});a("a",b.acrossQuestions).click(function(c){c.preventDefault();a(b.acrossInput).val(1);p()});a("a",b.acrossClears).click(function(c){c.preventDefault();a(b.allToggle).prop("checked",!1);n();m(0);h()});f=null;a(g).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(f&&a.data(f)!=a.data(d)&&!0===c.shiftKey){var e=!1;a(f).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass, 4 | d.checked);a(g).each(function(){if(a.data(this)==a.data(f)||a.data(this)==a.data(d))e=e?!1:!0;e&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);f=d;h()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(a){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))}); 5 | a('form#changelist-form input[name="_save"]').click(function(c){var d=!1;a("select option:selected",b.actionContainer).each(function(){a(this).val()&&(d=!0)});if(d)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})}; 6 | a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery); 7 | -------------------------------------------------------------------------------- /static/admin/js/admin/RelatedObjectLookups.js: -------------------------------------------------------------------------------- 1 | // Handles related-objects functionality: lookup link for raw_id_fields 2 | // and Add Another links. 3 | 4 | function html_unescape(text) { 5 | // Unescape a string that was escaped using django.utils.html.escape. 6 | text = text.replace(/</g, '<'); 7 | text = text.replace(/>/g, '>'); 8 | text = text.replace(/"/g, '"'); 9 | text = text.replace(/'/g, "'"); 10 | text = text.replace(/&/g, '&'); 11 | return text; 12 | } 13 | 14 | // IE doesn't accept periods or dashes in the window name, but the element IDs 15 | // we use to generate popup window names may contain them, therefore we map them 16 | // to allowed characters in a reversible way so that we can locate the correct 17 | // element when the popup window is dismissed. 18 | function id_to_windowname(text) { 19 | text = text.replace(/\./g, '__dot__'); 20 | text = text.replace(/\-/g, '__dash__'); 21 | return text; 22 | } 23 | 24 | function windowname_to_id(text) { 25 | text = text.replace(/__dot__/g, '.'); 26 | text = text.replace(/__dash__/g, '-'); 27 | return text; 28 | } 29 | 30 | function showRelatedObjectLookupPopup(triggeringLink) { 31 | var name = triggeringLink.id.replace(/^lookup_/, ''); 32 | name = id_to_windowname(name); 33 | var href; 34 | if (triggeringLink.href.search(/\?/) >= 0) { 35 | href = triggeringLink.href + '&_popup=1'; 36 | } else { 37 | href = triggeringLink.href + '?_popup=1'; 38 | } 39 | var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 40 | win.focus(); 41 | return false; 42 | } 43 | 44 | function dismissRelatedLookupPopup(win, chosenId) { 45 | var name = windowname_to_id(win.name); 46 | var elem = document.getElementById(name); 47 | if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 48 | elem.value += ',' + chosenId; 49 | } else { 50 | document.getElementById(name).value = chosenId; 51 | } 52 | win.close(); 53 | } 54 | 55 | function showAddAnotherPopup(triggeringLink) { 56 | var name = triggeringLink.id.replace(/^add_/, ''); 57 | name = id_to_windowname(name); 58 | var href = triggeringLink.href; 59 | if (href.indexOf('?') == -1) { 60 | href += '?_popup=1'; 61 | } else { 62 | href += '&_popup=1'; 63 | } 64 | var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 65 | win.focus(); 66 | return false; 67 | } 68 | 69 | function dismissAddAnotherPopup(win, newId, newRepr) { 70 | // newId and newRepr are expected to have previously been escaped by 71 | // django.utils.html.escape. 72 | newId = html_unescape(newId); 73 | newRepr = html_unescape(newRepr); 74 | var name = windowname_to_id(win.name); 75 | var elem = document.getElementById(name); 76 | var o; 77 | if (elem) { 78 | var elemName = elem.nodeName.toUpperCase(); 79 | if (elemName == 'SELECT') { 80 | o = new Option(newRepr, newId); 81 | elem.options[elem.options.length] = o; 82 | o.selected = true; 83 | } else if (elemName == 'INPUT') { 84 | if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 85 | elem.value += ',' + newId; 86 | } else { 87 | elem.value = newId; 88 | } 89 | } 90 | } else { 91 | var toId = name + "_to"; 92 | o = new Option(newRepr, newId); 93 | SelectBox.add_to_cache(toId, o); 94 | SelectBox.redisplay(toId); 95 | } 96 | win.close(); 97 | } 98 | -------------------------------------------------------------------------------- /static/admin/js/calendar.js: -------------------------------------------------------------------------------- 1 | /* 2 | calendar.js - Calendar functions by Adrian Holovaty 3 | depends on core.js for utility functions like removeChildren or quickElement 4 | */ 5 | 6 | // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions 7 | var CalendarNamespace = { 8 | monthsOfYear: gettext('January February March April May June July August September October November December').split(' '), 9 | daysOfWeek: gettext('S M T W T F S').split(' '), 10 | firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')), 11 | isLeapYear: function(year) { 12 | return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0)); 13 | }, 14 | getDaysInMonth: function(month,year) { 15 | var days; 16 | if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) { 17 | days = 31; 18 | } 19 | else if (month==4 || month==6 || month==9 || month==11) { 20 | days = 30; 21 | } 22 | else if (month==2 && CalendarNamespace.isLeapYear(year)) { 23 | days = 29; 24 | } 25 | else { 26 | days = 28; 27 | } 28 | return days; 29 | }, 30 | draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999 31 | var today = new Date(); 32 | var todayDay = today.getDate(); 33 | var todayMonth = today.getMonth()+1; 34 | var todayYear = today.getFullYear(); 35 | var todayClass = ''; 36 | 37 | // Use UTC functions here because the date field does not contain time 38 | // and using the UTC function variants prevent the local time offset 39 | // from altering the date, specifically the day field. For example: 40 | // 41 | // ``` 42 | // var x = new Date('2013-10-02'); 43 | // var day = x.getDate(); 44 | // ``` 45 | // 46 | // The day variable above will be 1 instead of 2 in, say, US Pacific time 47 | // zone. 48 | var isSelectedMonth = false; 49 | if (typeof selected != 'undefined') { 50 | isSelectedMonth = (selected.getUTCFullYear() == year && (selected.getUTCMonth()+1) == month); 51 | } 52 | 53 | month = parseInt(month); 54 | year = parseInt(year); 55 | var calDiv = document.getElementById(div_id); 56 | removeChildren(calDiv); 57 | var calTable = document.createElement('table'); 58 | quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year); 59 | var tableBody = quickElement('tbody', calTable); 60 | 61 | // Draw days-of-week header 62 | var tableRow = quickElement('tr', tableBody); 63 | for (var i = 0; i < 7; i++) { 64 | quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]); 65 | } 66 | 67 | var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay(); 68 | var days = CalendarNamespace.getDaysInMonth(month, year); 69 | 70 | // Draw blanks before first of month 71 | tableRow = quickElement('tr', tableBody); 72 | for (var i = 0; i < startingPos; i++) { 73 | var _cell = quickElement('td', tableRow, ' '); 74 | _cell.className = "nonday"; 75 | } 76 | 77 | // Draw days of month 78 | var currentDay = 1; 79 | for (var i = startingPos; currentDay <= days; i++) { 80 | if (i%7 == 0 && currentDay != 1) { 81 | tableRow = quickElement('tr', tableBody); 82 | } 83 | if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) { 84 | todayClass='today'; 85 | } else { 86 | todayClass=''; 87 | } 88 | 89 | // use UTC function; see above for explanation. 90 | if (isSelectedMonth && currentDay == selected.getUTCDate()) { 91 | if (todayClass != '') todayClass += " "; 92 | todayClass += "selected"; 93 | } 94 | 95 | var cell = quickElement('td', tableRow, '', 'class', todayClass); 96 | 97 | quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));'); 98 | currentDay++; 99 | } 100 | 101 | // Draw blanks after end of month (optional, but makes for valid code) 102 | while (tableRow.childNodes.length < 7) { 103 | var _cell = quickElement('td', tableRow, ' '); 104 | _cell.className = "nonday"; 105 | } 106 | 107 | calDiv.appendChild(calTable); 108 | } 109 | } 110 | 111 | // Calendar -- A calendar instance 112 | function Calendar(div_id, callback, selected) { 113 | // div_id (string) is the ID of the element in which the calendar will 114 | // be displayed 115 | // callback (string) is the name of a JavaScript function that will be 116 | // called with the parameters (year, month, day) when a day in the 117 | // calendar is clicked 118 | this.div_id = div_id; 119 | this.callback = callback; 120 | this.today = new Date(); 121 | this.currentMonth = this.today.getMonth() + 1; 122 | this.currentYear = this.today.getFullYear(); 123 | if (typeof selected != 'undefined') { 124 | this.selected = selected; 125 | } 126 | } 127 | Calendar.prototype = { 128 | drawCurrent: function() { 129 | CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected); 130 | }, 131 | drawDate: function(month, year, selected) { 132 | this.currentMonth = month; 133 | this.currentYear = year; 134 | 135 | if(selected) { 136 | this.selected = selected; 137 | } 138 | 139 | this.drawCurrent(); 140 | }, 141 | drawPreviousMonth: function() { 142 | if (this.currentMonth == 1) { 143 | this.currentMonth = 12; 144 | this.currentYear--; 145 | } 146 | else { 147 | this.currentMonth--; 148 | } 149 | this.drawCurrent(); 150 | }, 151 | drawNextMonth: function() { 152 | if (this.currentMonth == 12) { 153 | this.currentMonth = 1; 154 | this.currentYear++; 155 | } 156 | else { 157 | this.currentMonth++; 158 | } 159 | this.drawCurrent(); 160 | }, 161 | drawPreviousYear: function() { 162 | this.currentYear--; 163 | this.drawCurrent(); 164 | }, 165 | drawNextYear: function() { 166 | this.currentYear++; 167 | this.drawCurrent(); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /static/admin/js/collapse.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $(document).ready(function() { 3 | // Add anchor tag for Show/Hide link 4 | $("fieldset.collapse").each(function(i, elem) { 5 | // Don't hide if fields in this fieldset have errors 6 | if ($(elem).find("div.errors").length == 0) { 7 | $(elem).addClass("collapsed").find("h2").first().append(' (' + gettext("Show") + 9 | ')'); 10 | } 11 | }); 12 | // Add toggle to anchor tag 13 | $("fieldset.collapse a.collapse-toggle").click(function(ev) { 14 | if ($(this).closest("fieldset").hasClass("collapsed")) { 15 | // Show 16 | $(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset", [$(this).attr("id")]); 17 | } else { 18 | // Hide 19 | $(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", [$(this).attr("id")]); 20 | } 21 | return false; 22 | }); 23 | }); 24 | })(django.jQuery); 25 | -------------------------------------------------------------------------------- /static/admin/js/collapse.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){a(b).find("div.errors").length==0&&a(b).addClass("collapsed").find("h2").first().append(' ('+gettext("Show")+")")});a("fieldset.collapse a.collapse-toggle").click(function(){a(this).closest("fieldset").hasClass("collapsed")?a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]):a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", 2 | [a(this).attr("id")]);return false})})})(django.jQuery); 3 | -------------------------------------------------------------------------------- /static/admin/js/core.js: -------------------------------------------------------------------------------- 1 | // Core javascript helper functions 2 | 3 | // basic browser identification & version 4 | var isOpera = (navigator.userAgent.indexOf("Opera")>=0) && parseFloat(navigator.appVersion); 5 | var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]); 6 | 7 | // Cross-browser event handlers. 8 | function addEvent(obj, evType, fn) { 9 | if (obj.addEventListener) { 10 | obj.addEventListener(evType, fn, false); 11 | return true; 12 | } else if (obj.attachEvent) { 13 | var r = obj.attachEvent("on" + evType, fn); 14 | return r; 15 | } else { 16 | return false; 17 | } 18 | } 19 | 20 | function removeEvent(obj, evType, fn) { 21 | if (obj.removeEventListener) { 22 | obj.removeEventListener(evType, fn, false); 23 | return true; 24 | } else if (obj.detachEvent) { 25 | obj.detachEvent("on" + evType, fn); 26 | return true; 27 | } else { 28 | return false; 29 | } 30 | } 31 | 32 | function cancelEventPropagation(e) { 33 | if (!e) e = window.event; 34 | e.cancelBubble = true; 35 | if (e.stopPropagation) e.stopPropagation(); 36 | } 37 | 38 | // quickElement(tagType, parentReference [, textInChildNode, attribute, attributeValue ...]); 39 | function quickElement() { 40 | var obj = document.createElement(arguments[0]); 41 | if (arguments[2]) { 42 | var textNode = document.createTextNode(arguments[2]); 43 | obj.appendChild(textNode); 44 | } 45 | var len = arguments.length; 46 | for (var i = 3; i < len; i += 2) { 47 | obj.setAttribute(arguments[i], arguments[i+1]); 48 | } 49 | arguments[1].appendChild(obj); 50 | return obj; 51 | } 52 | 53 | // "a" is reference to an object 54 | function removeChildren(a) { 55 | while (a.hasChildNodes()) a.removeChild(a.lastChild); 56 | } 57 | 58 | // ---------------------------------------------------------------------------- 59 | // Cross-browser xmlhttp object 60 | // from http://jibbering.com/2002/4/httprequest.html 61 | // ---------------------------------------------------------------------------- 62 | var xmlhttp; 63 | /*@cc_on @*/ 64 | /*@if (@_jscript_version >= 5) 65 | try { 66 | xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); 67 | } catch (e) { 68 | try { 69 | xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 70 | } catch (E) { 71 | xmlhttp = false; 72 | } 73 | } 74 | @else 75 | xmlhttp = false; 76 | @end @*/ 77 | if (!xmlhttp && typeof XMLHttpRequest != 'undefined') { 78 | xmlhttp = new XMLHttpRequest(); 79 | } 80 | 81 | // ---------------------------------------------------------------------------- 82 | // Find-position functions by PPK 83 | // See http://www.quirksmode.org/js/findpos.html 84 | // ---------------------------------------------------------------------------- 85 | function findPosX(obj) { 86 | var curleft = 0; 87 | if (obj.offsetParent) { 88 | while (obj.offsetParent) { 89 | curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft); 90 | obj = obj.offsetParent; 91 | } 92 | // IE offsetParent does not include the top-level 93 | if (isIE && obj.parentElement){ 94 | curleft += obj.offsetLeft - obj.scrollLeft; 95 | } 96 | } else if (obj.x) { 97 | curleft += obj.x; 98 | } 99 | return curleft; 100 | } 101 | 102 | function findPosY(obj) { 103 | var curtop = 0; 104 | if (obj.offsetParent) { 105 | while (obj.offsetParent) { 106 | curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop); 107 | obj = obj.offsetParent; 108 | } 109 | // IE offsetParent does not include the top-level 110 | if (isIE && obj.parentElement){ 111 | curtop += obj.offsetTop - obj.scrollTop; 112 | } 113 | } else if (obj.y) { 114 | curtop += obj.y; 115 | } 116 | return curtop; 117 | } 118 | 119 | //----------------------------------------------------------------------------- 120 | // Date object extensions 121 | // ---------------------------------------------------------------------------- 122 | 123 | Date.prototype.getTwelveHours = function() { 124 | hours = this.getHours(); 125 | if (hours == 0) { 126 | return 12; 127 | } 128 | else { 129 | return hours <= 12 ? hours : hours-12 130 | } 131 | } 132 | 133 | Date.prototype.getTwoDigitMonth = function() { 134 | return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1); 135 | } 136 | 137 | Date.prototype.getTwoDigitDate = function() { 138 | return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate(); 139 | } 140 | 141 | Date.prototype.getTwoDigitTwelveHour = function() { 142 | return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours(); 143 | } 144 | 145 | Date.prototype.getTwoDigitHour = function() { 146 | return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours(); 147 | } 148 | 149 | Date.prototype.getTwoDigitMinute = function() { 150 | return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes(); 151 | } 152 | 153 | Date.prototype.getTwoDigitSecond = function() { 154 | return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds(); 155 | } 156 | 157 | Date.prototype.getHourMinute = function() { 158 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute(); 159 | } 160 | 161 | Date.prototype.getHourMinuteSecond = function() { 162 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond(); 163 | } 164 | 165 | Date.prototype.strftime = function(format) { 166 | var fields = { 167 | c: this.toString(), 168 | d: this.getTwoDigitDate(), 169 | H: this.getTwoDigitHour(), 170 | I: this.getTwoDigitTwelveHour(), 171 | m: this.getTwoDigitMonth(), 172 | M: this.getTwoDigitMinute(), 173 | p: (this.getHours() >= 12) ? 'PM' : 'AM', 174 | S: this.getTwoDigitSecond(), 175 | w: '0' + this.getDay(), 176 | x: this.toLocaleDateString(), 177 | X: this.toLocaleTimeString(), 178 | y: ('' + this.getFullYear()).substr(2, 4), 179 | Y: '' + this.getFullYear(), 180 | '%' : '%' 181 | }; 182 | var result = '', i = 0; 183 | while (i < format.length) { 184 | if (format.charAt(i) === '%') { 185 | result = result + fields[format.charAt(i + 1)]; 186 | ++i; 187 | } 188 | else { 189 | result = result + format.charAt(i); 190 | } 191 | ++i; 192 | } 193 | return result; 194 | } 195 | 196 | // ---------------------------------------------------------------------------- 197 | // String object extensions 198 | // ---------------------------------------------------------------------------- 199 | String.prototype.pad_left = function(pad_length, pad_string) { 200 | var new_string = this; 201 | for (var i = 0; new_string.length < pad_length; i++) { 202 | new_string = pad_string + new_string; 203 | } 204 | return new_string; 205 | } 206 | 207 | // ---------------------------------------------------------------------------- 208 | // Get the computed style for and element 209 | // ---------------------------------------------------------------------------- 210 | function getStyle(oElm, strCssRule){ 211 | var strValue = ""; 212 | if(document.defaultView && document.defaultView.getComputedStyle){ 213 | strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); 214 | } 215 | else if(oElm.currentStyle){ 216 | strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ 217 | return p1.toUpperCase(); 218 | }); 219 | strValue = oElm.currentStyle[strCssRule]; 220 | } 221 | return strValue; 222 | } 223 | -------------------------------------------------------------------------------- /static/admin/js/inlines.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a.fn.formset=function(g){var b=a.extend({},a.fn.formset.defaults,g),i=a(this);g=i.parent();var m=function(e,k,h){var j=RegExp("("+k+"-(\\d+|__prefix__))");k=k+"-"+h;a(e).prop("for")&&a(e).prop("for",a(e).prop("for").replace(j,k));if(e.id)e.id=e.id.replace(j,k);if(e.name)e.name=e.name.replace(j,k)},l=a("#id_"+b.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),d=parseInt(l.val(),10),c=a("#id_"+b.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off");l=c.val()===""||c.val()-l.val()>0;i.each(function(){a(this).not("."+ 2 | b.emptyCssClass).addClass(b.formCssClass)});if(i.length&&l){var f;if(i.prop("tagName")=="TR"){i=this.eq(-1).children().length;g.append(''+b.addText+"");f=g.find("tr:last a")}else{i.filter(":last").after('");f=i.filter(":last").next().find("a")}f.click(function(e){e.preventDefault();var k=a("#id_"+b.prefix+"-TOTAL_FORMS");e=a("#"+ 3 | b.prefix+"-empty");var h=e.clone(true);h.removeClass(b.emptyCssClass).addClass(b.formCssClass).attr("id",b.prefix+"-"+d);if(h.is("tr"))h.children(":last").append('");else h.is("ul")||h.is("ol")?h.append('
  • '+b.deleteText+"
  • "):h.children(":first").append(''+b.deleteText+""); 4 | h.find("*").each(function(){m(this,b.prefix,k.val())});h.insertBefore(a(e));a(k).val(parseInt(k.val(),10)+1);d+=1;c.val()!==""&&c.val()-k.val()<=0&&f.parent().hide();h.find("a."+b.deleteCssClass).click(function(j){j.preventDefault();j=a(this).parents("."+b.formCssClass);j.remove();d-=1;b.removed&&b.removed(j);j=a("."+b.formCssClass);a("#id_"+b.prefix+"-TOTAL_FORMS").val(j.length);if(c.val()===""||c.val()-j.length>0)f.parent().show();for(var n=0,o=j.length;n 0) { 23 | values.push(field.val()); 24 | } 25 | }); 26 | prepopulatedField.val(URLify(values.join(' '), maxLength)); 27 | }; 28 | 29 | prepopulatedField.data('_changed', false); 30 | prepopulatedField.change(function() { 31 | prepopulatedField.data('_changed', true); 32 | }); 33 | 34 | if (!prepopulatedField.val()) { 35 | $(dependencies.join(',')).keyup(populate).change(populate).focus(populate); 36 | } 37 | }); 38 | }; 39 | })(django.jQuery); 40 | -------------------------------------------------------------------------------- /static/admin/js/prepopulate.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.prepopulate=function(e,g){return this.each(function(){var a=b(this),d=function(){if(!a.data("_changed")){var f=[];b.each(e,function(h,c){c=b(c);c.val().length>0&&f.push(c.val())});a.val(URLify(f.join(" "),g))}};a.data("_changed",false);a.change(function(){a.data("_changed",true)});a.val()||b(e.join(",")).keyup(d).change(d).focus(d)})}})(django.jQuery); 2 | -------------------------------------------------------------------------------- /static/admin/js/timeparse.js: -------------------------------------------------------------------------------- 1 | var timeParsePatterns = [ 2 | // 9 3 | { re: /^\d{1,2}$/i, 4 | handler: function(bits) { 5 | if (bits[0].length == 1) { 6 | return '0' + bits[0] + ':00'; 7 | } else { 8 | return bits[0] + ':00'; 9 | } 10 | } 11 | }, 12 | // 13:00 13 | { re: /^\d{2}[:.]\d{2}$/i, 14 | handler: function(bits) { 15 | return bits[0].replace('.', ':'); 16 | } 17 | }, 18 | // 9:00 19 | { re: /^\d[:.]\d{2}$/i, 20 | handler: function(bits) { 21 | return '0' + bits[0].replace('.', ':'); 22 | } 23 | }, 24 | // 3 am / 3 a.m. / 3am 25 | { re: /^(\d+)\s*([ap])(?:.?m.?)?$/i, 26 | handler: function(bits) { 27 | var hour = parseInt(bits[1]); 28 | if (hour == 12) { 29 | hour = 0; 30 | } 31 | if (bits[2].toLowerCase() == 'p') { 32 | if (hour == 12) { 33 | hour = 0; 34 | } 35 | return (hour + 12) + ':00'; 36 | } else { 37 | if (hour < 10) { 38 | return '0' + hour + ':00'; 39 | } else { 40 | return hour + ':00'; 41 | } 42 | } 43 | } 44 | }, 45 | // 3.30 am / 3:15 a.m. / 3.00am 46 | { re: /^(\d+)[.:](\d{2})\s*([ap]).?m.?$/i, 47 | handler: function(bits) { 48 | var hour = parseInt(bits[1]); 49 | var mins = parseInt(bits[2]); 50 | if (mins < 10) { 51 | mins = '0' + mins; 52 | } 53 | if (hour == 12) { 54 | hour = 0; 55 | } 56 | if (bits[3].toLowerCase() == 'p') { 57 | if (hour == 12) { 58 | hour = 0; 59 | } 60 | return (hour + 12) + ':' + mins; 61 | } else { 62 | if (hour < 10) { 63 | return '0' + hour + ':' + mins; 64 | } else { 65 | return hour + ':' + mins; 66 | } 67 | } 68 | } 69 | }, 70 | // noon 71 | { re: /^no/i, 72 | handler: function(bits) { 73 | return '12:00'; 74 | } 75 | }, 76 | // midnight 77 | { re: /^mid/i, 78 | handler: function(bits) { 79 | return '00:00'; 80 | } 81 | } 82 | ]; 83 | 84 | function parseTimeString(s) { 85 | for (var i = 0; i < timeParsePatterns.length; i++) { 86 | var re = timeParsePatterns[i].re; 87 | var handler = timeParsePatterns[i].handler; 88 | var bits = re.exec(s); 89 | if (bits) { 90 | return handler(bits); 91 | } 92 | } 93 | return s; 94 | } 95 | -------------------------------------------------------------------------------- /static/admin/js/urlify.js: -------------------------------------------------------------------------------- 1 | var LATIN_MAP = { 2 | 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 3 | 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 4 | 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 5 | 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U', 6 | 'Ý': 'Y', 'Þ': 'TH', 'Ÿ': 'Y', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 7 | 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 8 | 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 9 | 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 10 | 'ú': 'u', 'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y' 11 | }; 12 | var LATIN_SYMBOLS_MAP = { 13 | '©':'(c)' 14 | }; 15 | var GREEK_MAP = { 16 | 'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8', 17 | 'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p', 18 | 'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w', 19 | 'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s', 20 | 'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i', 21 | 'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8', 22 | 'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P', 23 | 'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W', 24 | 'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I', 25 | 'Ϋ':'Y' 26 | }; 27 | var TURKISH_MAP = { 28 | 'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U', 29 | 'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G' 30 | }; 31 | var RUSSIAN_MAP = { 32 | 'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh', 33 | 'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o', 34 | 'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c', 35 | 'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu', 36 | 'я':'ya', 37 | 'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh', 38 | 'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O', 39 | 'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C', 40 | 'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu', 41 | 'Я':'Ya' 42 | }; 43 | var UKRAINIAN_MAP = { 44 | 'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g' 45 | }; 46 | var CZECH_MAP = { 47 | 'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u', 48 | 'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T', 49 | 'Ů':'U', 'Ž':'Z' 50 | }; 51 | var POLISH_MAP = { 52 | 'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z', 53 | 'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'E', 'Ł':'L', 'Ń':'N', 'Ó':'O', 'Ś':'S', 54 | 'Ź':'Z', 'Ż':'Z' 55 | }; 56 | var LATVIAN_MAP = { 57 | 'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n', 58 | 'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'I', 59 | 'Ķ':'K', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'U', 'Ž':'Z' 60 | }; 61 | var ARABIC_MAP = { 62 | 'أ':'a', 'ب':'b', 'ت':'t', 'ث': 'th', 'ج':'g', 'ح':'h', 'خ':'kh', 'د':'d', 63 | 'ذ':'th', 'ر':'r', 'ز':'z', 'س':'s', 'ش':'sh', 'ص':'s', 'ض':'d', 'ط':'t', 64 | 'ظ':'th', 'ع':'aa', 'غ':'gh', 'ف':'f', 'ق':'k', 'ك':'k', 'ل':'l', 'م':'m', 65 | 'ن':'n', 'ه':'h', 'و':'o', 'ي':'y' 66 | }; 67 | var LITHUANIAN_MAP = { 68 | 'ą':'a', 'č':'c', 'ę':'e', 'ė':'e', 'į':'i', 'š':'s', 'ų':'u', 'ū':'u', 69 | 'ž':'z', 70 | 'Ą':'A', 'Č':'C', 'Ę':'E', 'Ė':'E', 'Į':'I', 'Š':'S', 'Ų':'U', 'Ū':'U', 71 | 'Ž':'Z' 72 | }; 73 | var SERBIAN_MAP = { 74 | 'ђ':'dj', 'ј':'j', 'љ':'lj', 'њ':'nj', 'ћ':'c', 'џ':'dz', 'đ':'dj', 75 | 'Ђ':'Dj', 'Ј':'j', 'Љ':'Lj', 'Њ':'Nj', 'Ћ':'C', 'Џ':'Dz', 'Đ':'Dj' 76 | }; 77 | var AZERBAIJANI_MAP = { 78 | 'ç':'c', 'ə':'e', 'ğ':'g', 'ı':'i', 'ö':'o', 'ş':'s', 'ü':'u', 79 | 'Ç':'C', 'Ə':'E', 'Ğ':'G', 'İ':'I', 'Ö':'O', 'Ş':'S', 'Ü':'U' 80 | }; 81 | 82 | var ALL_DOWNCODE_MAPS = [ 83 | LATIN_MAP, 84 | LATIN_SYMBOLS_MAP, 85 | GREEK_MAP, 86 | TURKISH_MAP, 87 | RUSSIAN_MAP, 88 | UKRAINIAN_MAP, 89 | CZECH_MAP, 90 | POLISH_MAP, 91 | LATVIAN_MAP, 92 | ARABIC_MAP, 93 | LITHUANIAN_MAP, 94 | SERBIAN_MAP, 95 | AZERBAIJANI_MAP 96 | ]; 97 | 98 | var Downcoder = { 99 | 'Initialize': function() { 100 | if (Downcoder.map) { // already made 101 | return; 102 | } 103 | Downcoder.map = {}; 104 | Downcoder.chars = []; 105 | for (var i=0; i section { 28 | margin-bottom: 3.04762em; } 29 | 30 | /* =============================================== 31 | * Generic Styles 32 | * =============================================== */ 33 | h1, h2, h3, h4, h5, h6 { 34 | font-weight: normal; } 35 | 36 | h1 { 37 | margin: 0 0 1em 0; 38 | font-size: 1.52381em; 39 | line-height: 1.5em; } 40 | 41 | h2 { 42 | margin: 0 0 1em 0; } 43 | 44 | h3 { 45 | margin: 1em 0 0 0; } 46 | 47 | p { 48 | margin: 0 0 1em 0; } 49 | 50 | p:last-child { 51 | margin-bottom: 0; } 52 | 53 | .section_separator { 54 | text-transform: lowercase; 55 | position: relative; 56 | margin: 0 0 1em 0; 57 | overflow: hidden; 58 | font-size: 1em; 59 | line-height: 1.14286em; 60 | text-align: center; } 61 | .section_separator a, 62 | .section_separator span { 63 | display: inline-block; 64 | position: relative; 65 | padding: 0 10px; 66 | z-index: 1; 67 | background-color: #fdfdfd; 68 | text-decoration: none; } 69 | .section_separator a:hover { 70 | text-decoration: underline; } 71 | 72 | .section_separator:before, 73 | .section_separator .before { 74 | text-transform: lowercase; 75 | position: absolute; 76 | top: 0; 77 | left: -1000px; 78 | z-index: -1; 79 | overflow: visible; 80 | text-decoration: none !important; 81 | color: #111; 82 | font-size: 1em; 83 | line-height: 1.14286em; 84 | letter-spacing: 2px; 85 | content: "µµµµµµµ"; } 86 | 87 | .symbol { 88 | text-transform: lowercase; 89 | line-height: 1em; 90 | /* improves symbols rendering */ 91 | -webkit-font-smoothing: antialiased; } 92 | 93 | blockquote { 94 | padding-left: 22px; 95 | margin: 1.14286em 54px 1.14286em 54px; 96 | font-style: italic; } 97 | blockquote ol, blockquote ul { 98 | margin-left: 0; } 99 | 100 | blockquote::before { 101 | content: ''; } 102 | 103 | blockquote::after { 104 | content: ''; } 105 | 106 | ol, 107 | ul { 108 | padding-left: 0; 109 | margin: 1.14286em 0 1.14286em 53px; } 110 | 111 | ul { 112 | list-style: none; } 113 | 114 | ol { 115 | padding-left: 22px; 116 | list-style-position: outside; } 117 | 118 | ul > li { 119 | padding-left: 22px; } 120 | 121 | ol li, 122 | ul li { 123 | margin-bottom: 0; } 124 | 125 | b, 126 | strong { 127 | font-style: italic; 128 | font-weight: normal; } 129 | 130 | i, em { 131 | font-style: italic; } 132 | 133 | a { 134 | color: #111; 135 | text-decoration: underline; } 136 | 137 | a:hover { 138 | color: #cc0000; } 139 | 140 | del { 141 | text-decoration: line-through; } 142 | 143 | mark { 144 | padding: 2px; 145 | background-color: #eee; } 146 | 147 | input, 148 | textarea, 149 | .customStyleSelectBox { 150 | font-weight: normal; 151 | font-style: normal; 152 | font-variant: normal; 153 | -webkit-box-sizing: content-box; 154 | -moz-box-sizing: content-box; 155 | box-sizing: content-box; 156 | border: 1px solid #D8D8D8; 157 | padding: 0 0 0 1em; 158 | background-color: #fff; } 159 | 160 | .customStyleSelectBoxInner { 161 | width: 100%; 162 | height: 100%; } 163 | 164 | textarea { 165 | padding-top: 5px; 166 | resize: none; } 167 | 168 | input::-webkit-input-placeholder { 169 | color: #bbb; } 170 | 171 | input:-moz-placeholder { 172 | color: #bbb; } 173 | 174 | input[type="submit"] { 175 | text-transform: lowercase; 176 | border: 1px solid #c0c0c0; 177 | padding: 0; 178 | color: #444; 179 | text-align: center; } 180 | 181 | .preload { 182 | display: none; 183 | position: absolute; 184 | top: 0; 185 | bottom: 0; 186 | left: 0; 187 | right: 0; 188 | text-align: center; } 189 | 190 | code { 191 | max-height: 1.3125em; 192 | padding: 0.5em; 193 | font-size: 0.7em; 194 | background-color: #f5f5f5; 195 | color: #444444; 196 | } 197 | -------------------------------------------------------------------------------- /static/reader/css/frame.css: -------------------------------------------------------------------------------- 1 | .content-frame { 2 | position: absolute; 3 | height: 100%; 4 | width: 100%; 5 | top: 0px; 6 | left: 0px; 7 | } 8 | 9 | body { 10 | height: 100vh; 11 | max-height: 100vh; 12 | } 13 | 14 | body { 15 | padding-top: 0px; 16 | padding-bottom: 0px; 17 | border-top: 0px; 18 | } -------------------------------------------------------------------------------- /static/reader/css/layouts/arrow-nav.scss: -------------------------------------------------------------------------------- 1 | .arrow_nav { 2 | -webkit-border-radius: 3px; 3 | -moz-border-radius: 3px; 4 | border-radius: 3px; 5 | position: fixed; 6 | background-color: #000; 7 | } 8 | 9 | .arrow_nav a { 10 | display: block; 11 | width: auto; 12 | height: 1.88235em; 13 | position: relative; 14 | float: left; 15 | padding: 0 8px; 16 | font-size: 0.80952em; 17 | line-height: 2.05882em; 18 | color: #fff; 19 | text-decoration: none; 20 | outline: 0px none; 21 | } 22 | 23 | .arrow_nav .fa { 24 | margin-top: 0.25em; 25 | } 26 | 27 | .arrow_nav a .symbol { 28 | position: absolute; 29 | top: 0; 30 | font-size: 1.47059em; 31 | line-height: 1.32em; 32 | } 33 | 34 | .arrow_nav a .tooltip { 35 | -webkit-border-radius: 3px; 36 | -moz-border-radius: 3px; 37 | border-radius: 3px; 38 | display: none; 39 | position: absolute; 40 | top: 40px; 41 | right: 0; 42 | padding: 3px 8px; 43 | line-height: 1em; 44 | white-space: nowrap; 45 | background-color: #eee; 46 | color: #333; 47 | } 48 | 49 | .arrow_nav a .tooltip:after { 50 | position: absolute; 51 | top: -8px; 52 | right: 13px; 53 | width: 0; 54 | height: 0; 55 | border: 4px solid transparent; 56 | border-bottom-color: #eee; 57 | content: " "; 58 | } 59 | 60 | .arrow_nav a.previous .label { 61 | padding-left: 16px; 62 | } 63 | 64 | .arrow_nav a.previous .symbol { 65 | left: 8px; 66 | } 67 | 68 | .arrow_nav a .yellow { 69 | color: #FF6 !important; 70 | } 71 | 72 | .arrow_nav a.next .label { 73 | padding-right: 16px; 74 | } 75 | 76 | .arrow_nav a.next .symbol { 77 | right: 8px; 78 | } 79 | 80 | .arrow_nav a:after { 81 | display: inline-block; 82 | width: 1px; 83 | position: absolute; 84 | right: 0; 85 | top: 4px; 86 | bottom: 4px; 87 | background-color: #555; 88 | content: '' !important; 89 | } 90 | 91 | .arrow_nav a:hover { 92 | -webkit-border-radius: 3px; 93 | -moz-border-radius: 3px; 94 | border-radius: 3px; 95 | color: #FF6; 96 | background-color: #000; 97 | } 98 | 99 | .arrow_nav a:hover .tooltip { 100 | display: block; 101 | } 102 | 103 | .arrow_nav.top { 104 | top: 8px; 105 | } 106 | 107 | .arrow_nav.bottom { 108 | bottom: 8px; 109 | } 110 | 111 | .arrow_nav.right { 112 | right: 20px; 113 | } 114 | 115 | .arrow_nav.left { 116 | left: 8px; 117 | } 118 | 119 | .arrow_nav.bottom a .tooltip { 120 | top: -30px; 121 | } 122 | 123 | .arrow_nav.bottom a .tooltip:after { 124 | top: auto; 125 | bottom: -8px; 126 | border-bottom-color: transparent; 127 | border-top-color: #eee; 128 | } 129 | -------------------------------------------------------------------------------- /static/reader/css/layouts/article.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | /* 4 | * ARTICLE 5 | */ 6 | #scrollbubble { 7 | display: none; 8 | position: fixed; 9 | top: 0; 10 | right: 20px; 11 | z-index: 500; 12 | padding: 3px 15px; 13 | text-align: center; 14 | white-space: no-wrap; 15 | background-color: #000; 16 | color: #eee; } 17 | 18 | #scrollbubble:after { 19 | position: absolute; 20 | top: 50%; 21 | right: -8px; 22 | width: 0; 23 | height: 0; 24 | margin-top: -4px; 25 | border: 4px solid transparent; 26 | border-left-color: #000; 27 | content: " "; } 28 | 29 | #bubbleLeft, 30 | #bubbleRight { 31 | display: inline-block; 32 | color: #eee; 33 | font-size: 1.42857em; 34 | line-height: 1.06667em; 35 | text-decoration: none; } 36 | 37 | #bubbleLeft { 38 | float: left; } 39 | 40 | #bubbleRight { 41 | float: right; } 42 | 43 | #scrollBubbleText { 44 | display: inline-block; 45 | font-size: 18px !important; } 46 | #scrollBubbleText a { 47 | color: #eee; } 48 | 49 | .post .post_title, 50 | .post > h1 ~ h1, 51 | #about .post_title, 52 | #about > h1 ~ h1, 53 | #client .post_title, 54 | #client > h1 ~ h1 { 55 | margin: 0; 56 | font-size: 1.71429em; 57 | font-style: italic; 58 | line-height: 1.33333em; 59 | text-align: center; } 60 | .post .post_author, 61 | #about .post_author, 62 | #client .post_author { 63 | font-family: 'Nitti iA SC', "Helvetica Neue", Helvetica, Arial, sans-serif; 64 | text-transform: lowercase; 65 | display: block; 66 | margin-top: 0; 67 | margin-bottom: 0; 68 | font-size: 1em; 69 | text-align: center; } 70 | .post h2, 71 | .post .project_summary h2, 72 | #about h2, 73 | #about .project_summary h2, 74 | #client h2, 75 | #client .project_summary h2 { 76 | font-family: 'Nitti iA', "Helvetica Neue", Helvetica, Arial, sans-serif; 77 | font-weight: normal; 78 | font-style: normal; 79 | font-variant: normal; 80 | display: block; 81 | margin-top: 1em; 82 | margin-bottom: 1em; 83 | text-align: left; 84 | font-size: 1.19048em; 85 | line-height: 1.4em; } 86 | .post h2 ~ h2, 87 | #about h2 ~ h2, 88 | #client h2 ~ h2 { 89 | /* targets the rest of the h2's */ 90 | margin-top: 2em; 91 | margin-bottom: 1em; 92 | font-size: 1.33333em; 93 | font-style: italic; 94 | text-align: center; } 95 | .post h3, 96 | #about h3, 97 | #client h3 { 98 | margin-top: 2em; 99 | margin-bottom: 0; 100 | text-align: left; } 101 | .post h2 + h3, 102 | #about h2 + h3, 103 | #client h2 + h3 { 104 | margin-top: -0.7619em; } 105 | .post img, 106 | .post object, 107 | .post embed, 108 | #about img, 109 | #about object, 110 | #about embed, 111 | #client img, 112 | #client object, 113 | #client embed { 114 | display: block; 115 | position: relative; 116 | max-width: 100%; 117 | height: auto; 118 | margin: 1.14286em auto; } 119 | .post iframe, 120 | #about iframe, 121 | #client iframe { 122 | display: block; 123 | position: relative; 124 | max-width: 100%; } 125 | .post hr, 126 | #about hr, 127 | #client hr { 128 | height: 1px; 129 | border: 0; 130 | margin: 2.66667em 54px 1.14286em 54px; 131 | background-color: #ccc; } 132 | .post .wp-caption, 133 | #about .wp-caption, 134 | #client .wp-caption { 135 | width: 100% !important; 136 | margin: 1.14286em 0; } 137 | .post .wp-caption iframe, 138 | .post .wp-caption img, 139 | .post .wp-caption object, 140 | .post .wp-caption embed, 141 | #about .wp-caption iframe, 142 | #about .wp-caption img, 143 | #about .wp-caption object, 144 | #about .wp-caption embed, 145 | #client .wp-caption iframe, 146 | #client .wp-caption img, 147 | #client .wp-caption object, 148 | #client .wp-caption embed { 149 | margin-top: 0; 150 | margin-bottom: 0; } 151 | .post .wp-caption-text, 152 | #about .wp-caption-text, 153 | #client .wp-caption-text { 154 | margin-top: 0.53333em; 155 | font-size: 0.71429em; 156 | font-style: italic; 157 | line-height: 1.6em; 158 | text-align: center; } 159 | .post .alert, 160 | #about .alert, 161 | #client .alert { 162 | font-style: italic; } 163 | .post .wp-smiley, 164 | #about .wp-smiley, 165 | #client .wp-smiley { 166 | display: inline; 167 | margin: 0; } 168 | .post .post_fleuron, 169 | #about .post_fleuron, 170 | #client .post_fleuron { 171 | margin-top: 2em; 172 | letter-spacing: .5em; 173 | display: block; 174 | line-height: 1em; 175 | text-align: center; } 176 | 177 | .post figure { 178 | margin-bottom: 0.7619em; } 179 | .post figure blockquote { 180 | margin-bottom: 0; } 181 | .post figure blockquote:before { 182 | content: "\201C"; } 183 | .post figure blockquote:after { 184 | content: "\201D"; } 185 | .post figure figcaption { 186 | padding-left: 22px; 187 | margin: 0.5em 54px 0 54px; 188 | font-size: 0.7619em; 189 | font-style: italic; 190 | line-height: 1.5em; } 191 | .post figure blockquote + figcaption:before { 192 | content: '—'; 193 | padding-right: 4px; } 194 | 195 | /* Fix FidVid objects */ 196 | .fluid-width-video-wrapper { 197 | margin: 1.14286em auto; } 198 | .fluid-width-video-wrapper object, 199 | .fluid-width-video-wrapper embed { 200 | margin-top: 0; 201 | margin-bottom: 0; } 202 | -------------------------------------------------------------------------------- /static/reader/css/layouts/blog.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * BLOG 3 | */ 4 | #blog_search { 5 | text-align: center; } 6 | 7 | #search_form { 8 | display: inline-block; } 9 | #search_form input[type="text"] { 10 | white-space: nowrap; 11 | overflow: hidden; 12 | -o-text-overflow: ellipsis; 13 | text-overflow: ellipsis; 14 | width: 370px; 15 | height: 1em; 16 | padding: 0.52381em 0 0.28571em 10px; 17 | float: left; 18 | line-height: 1em; } 19 | #search_form input::-webkit-input-placeholder { 20 | padding-top: 2px; } 21 | #search_form input:-moz-placeholder { 22 | padding-top: 2px; } 23 | #search_form input[type="submit"] { 24 | width: 117px; 25 | height: 2em; 26 | margin-left: 9px; 27 | padding: 0; 28 | float: left; 29 | font-size: 0.90476em; 30 | line-height: 2.10526em; } 31 | 32 | .blog_post { 33 | margin-bottom: 1em; 34 | margin-top: 2em; } 35 | .blog_post .blog_post_title { 36 | margin: 0 1em 1em 3em; 37 | text-align: center; 38 | font-style: italic; 39 | word-wrap: break-word; } 40 | .blog_post .blog_post_title > a { 41 | text-decoration: none; 42 | color: #111; } 43 | .blog_post .blog_post_title > a:hover { 44 | text-decoration: underline; 45 | color: #cc0000; } 46 | .blog_post .blog_post_content { 47 | /*display: inline;*/ } 48 | .blog_post .blog_post_content p:last-child { 49 | /*display: inline;*/ } 50 | -------------------------------------------------------------------------------- /static/reader/css/layouts/clients.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * CLIENTS 3 | */ 4 | /* 5 | * CLIENT 6 | */ 7 | #client > section { 8 | margin-bottom: 1em; } 9 | 10 | #project_summary * { 11 | margin: 0; 12 | padding: 0; } 13 | 14 | #project_briefing p { 15 | margin-bottom: 0; } 16 | 17 | #summary .client_quote { 18 | margin-bottom: 0; 19 | margin-top: 3em; } 20 | 21 | .project_image { 22 | margin: 1em 0; } 23 | .project_image img { 24 | margin: 0; } 25 | .project_image figcaption { 26 | font-style: italic; 27 | font-size: 0.71429em; 28 | line-height: 1.6em; 29 | text-align: center; } 30 | .project_image figcaption span { 31 | font-family: 'Nitti iA SC', "Helvetica Neue", Helvetica, Arial, sans-serif; 32 | text-transform: lowercase; } 33 | 34 | #all_clients .featured { 35 | color: #7e7e7e; } 36 | -------------------------------------------------------------------------------- /static/reader/css/layouts/footer.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * FOOTER 3 | */ 4 | footer { 5 | text-transform: lowercase; 6 | font-size: 0.90476em; 7 | line-height: 1.26316em; 8 | margin-top: 3em; } 9 | footer .section_separator { 10 | margin-bottom: 1.52381em; } 11 | footer > nav, 12 | footer > span { 13 | display: block; 14 | text-align: center; } 15 | footer > nav .lowercase, 16 | footer > span .lowercase { 17 | display: inline; 18 | font-variant: normal; 19 | text-transform: none; 20 | font-size: 0.94737em; } 21 | -------------------------------------------------------------------------------- /static/reader/css/layouts/header.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * HEADER 3 | */ 4 | header { 5 | margin-bottom: 2.5em; 6 | text-align: center; } 7 | 8 | #logo { 9 | color: #000; 10 | text-decoration: none; 11 | line-height: 1em; } 12 | 13 | #logo:hover { 14 | color: #000; } 15 | -------------------------------------------------------------------------------- /static/reader/css/layouts/next-articles.scss: -------------------------------------------------------------------------------- 1 | .next-articles { 2 | -webkit-border-radius: 3px; 3 | -moz-border-radius: 3px; 4 | border-radius: 3px; 5 | display: none; 6 | position: fixed; 7 | background-color: #000; 8 | color: #FFF; 9 | width: 500px; 10 | right: 1.3em; 11 | top: 8px; 12 | } 13 | 14 | .next-articles a { 15 | padding: 0 8px; 16 | font-size: 0.80952em; 17 | margin-bottom: 6px; 18 | color: #fff; 19 | text-decoration: none; 20 | outline: 0px none; 21 | } 22 | 23 | .next-articles .score { 24 | padding-left: 0.1em; 25 | padding-right: 0.1em; 26 | -webkit-border-radius: 5px; 27 | -moz-border-radius: 5px; 28 | border-radius: 5px; 29 | background-color: #eee; 30 | color: #000; 31 | } 32 | 33 | .next-articles .item { 34 | 35 | } 36 | 37 | .next-articles a:hover { 38 | -webkit-border-radius: 3px; 39 | -moz-border-radius: 3px; 40 | border-radius: 3px; 41 | color: #FF6; 42 | background-color: #000; 43 | } 44 | 45 | .next-articles ul { 46 | margin: 5px; 47 | padding-top: 4px; 48 | } 49 | 50 | .next-articles li { 51 | margin: 5px; 52 | padding-left: 3px; 53 | padding-bottom: 7px; 54 | line-height: 1.1em; 55 | } 56 | -------------------------------------------------------------------------------- /static/reader/css/layouts/products.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * PRODUCTS 3 | */ 4 | #products_page .product { 5 | margin-bottom: 1em; } 6 | #products_page .product h1 { 7 | margin-bottom: 1em; 8 | text-align: center; 9 | font-style: italic; } 10 | 11 | /* 12 | * Password protected form 13 | */ 14 | .fm-form input[type="text"], 15 | .fm-form input[type="password"], 16 | .passwordform input[type="text"], 17 | .passwordform input[type="password"] { 18 | /*@include ellipsis();*/ 19 | width: 370px; 20 | height: 1em; 21 | padding: 0.52381em 0 0.28571em 10px; 22 | float: left; 23 | line-height: 1em; } 24 | .fm-form input::-webkit-input-placeholder, 25 | .passwordform input::-webkit-input-placeholder { 26 | padding-top: 2px; } 27 | .fm-form input:-moz-placeholder, 28 | .passwordform input:-moz-placeholder { 29 | padding-top: 2px; } 30 | .fm-form input[type="submit"], 31 | .passwordform input[type="submit"] { 32 | width: 117px; 33 | height: 2em; 34 | margin-left: 9px; 35 | padding: 0; 36 | float: left; 37 | font-size: 0.90476em; 38 | line-height: 2.10526em; } 39 | .fm-form ul, 40 | .passwordform ul { 41 | margin-left: 0; } 42 | .fm-form ul li, 43 | .passwordform ul li { 44 | background: transparent; } 45 | 46 | .fm-form:before, 47 | .fm-form:after { 48 | content: ""; 49 | display: table; } 50 | 51 | .fm-form:after { 52 | clear: both; } 53 | 54 | /* For IE 6/7 (trigger hasLayout) */ 55 | .fm-form { 56 | *zoom: 1; } 57 | -------------------------------------------------------------------------------- /static/reader/css/media/ipad.css: -------------------------------------------------------------------------------- 1 | /* iPad 2 | * --------------------------------------- 3 | * same as normal displays 4 | */ 5 | @media screen and (min-device-width: 768px) and (max-device-width: 1024px) { 6 | #scrollbubble { 7 | position: absolute; } 8 | 9 | ul > li { 10 | background-position: left 12px; } } 11 | 12 | /*# sourceMappingURL=ipad.css.map */ 13 | -------------------------------------------------------------------------------- /static/reader/css/media/ipad.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": ";;;;AAIA,0EAA2E;EACzE,aAAc;IACV,QAAQ,EAAE,QAAQ;;EAGtB,OAAQ;IACJ,mBAAmB,EAAE,SAAS", 4 | "sources": ["ipad.scss"], 5 | "names": [], 6 | "file": "ipad.css" 7 | } -------------------------------------------------------------------------------- /static/reader/css/media/ipad.scss: -------------------------------------------------------------------------------- 1 | /* iPad 2 | * --------------------------------------- 3 | * same as normal displays 4 | */ 5 | @media screen and (min-device-width: 768px) and (max-device-width: 1024px) { 6 | #scrollbubble { 7 | position: absolute; 8 | } 9 | 10 | ul > li { 11 | background-position: left 12px; 12 | } 13 | } -------------------------------------------------------------------------------- /static/reader/css/media/iphone.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAAA,yCAA0C;EACvC,cAAe;IACb,KAAK,EAAE,gBAAgB;;EAG1B,QAAS;IACP,KAAK,EAAE,KAAK;IACZ,SAAS,EAAE,GAAG;;EAEhB,IAAK;IACH,WAAW,EAAE,GAAG;;;;;EAKlB,EAAG;IACD,aAAa,EAAE,GAAG;;EAEpB,EAAG;IACD,aAAa,EAAE,GAAG;;EAEpB,EAAG;IACD,aAAa,EAAE,GAAG;IAClB,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,KAAK;;EAEpB,CAAE;IACA,aAAa,EAAE,GAAG;;EAEpB,EAAG;IACD,WAAW,EAAE,GAAG;IAChB,YAAY,EAAE,CAAC;;EAEjB,EAAG;IACD,WAAW,EAAE,IAAI;;EAEnB,KAAM;IACJ,YAAY,EAAE,IAAI;;EAEpB,UAAW;IACT,MAAM,EAAE,6BAA6B;IACrC,YAAY,EAAE,GAAG;;EAEnB,kBAAmB;IACjB,UAAU,EAAE,GAAG;IACf,aAAa,EAAE,GAAG;;EAEpB,OAAQ;IACN,mBAAmB,EAAE,SAAS;;EAEhC,GAAI;IACF,SAAS,EAAE,IAAI;;EAEjB,IAAK;IACH,SAAS,EAAE,KAAK;IAChB,gBAAgB,EAAE,WAAW;;EAE/B,kBAAmB;IACjB,aAAa,EAAE,GAAG;IAClB,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;;EAExB,kBAAmB;IACjB,aAAa,EAAE,GAAG;;;;;EAKpB,MAAO;IACL,aAAa,EAAE,GAAG;;;;;EAKpB,iBAAkB;IAChB,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,KAAK;;EACpB,kBAAmB;IACjB,UAAU,EAAE,GAAG;IACf,aAAa,EAAE,GAAG;IAClB,SAAS,EAAE,SAAS;;EACtB,uBAAwB;IACtB,aAAa,EAAE,GAAG;IAClB,SAAS,EAAE,SAAS;;EACtB,SAAU;IACR,UAAU,EAAE,GAAG;IACf,aAAa,EAAE,GAAG;;EAEpB,aAAc;IACZ,QAAQ,EAAE,QAAQ;IAClB,SAAS,EAAE,QAAQ;;;;;EAKrB;UACS;IACP,SAAS,EAAE,SAAS;;EACtB,sBAAuB;IACrB,SAAS,EAAE,GAAG;;EAEhB,+BAAgC;IAC9B,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;IACT,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,CAAC;;EAEZ,0CAA2C;IACzC,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,KAAK;;;;;EAKhB,+BAAgC;IAC9B,MAAM,EAAE,SAAS;IACjB,WAAW,EAAE,CAAC;IACd,cAAc,EAAE,CAAC;IACjB,WAAW,EAAE,GAAG;;EAClB,iCAAkC;IAChC,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;IACpB,cAAc,EAAE,CAAC;;EAEnB,UAAW;IACT,aAAa,EAAE,GAAG;;EAEpB,2BAA4B;IAC1B,MAAM,EAAE,SAAS;;;;;EAKnB,YAAa;IACX,UAAU,EAAE,GAAG;;EACf,2BAA4B;IAC1B,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,GAAG;;EAEpB,iBAAkB;IAChB,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,IAAI;;EACX,qBAAsB;IACpB,MAAM,EAAE,MAAM;;EAElB,kBAAmB;IACjB,OAAO,EAAE,KAAK;;EAEhB,wBAAyB;IACvB,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,CAAC;IACd,KAAK,EAAE,IAAI;;EACX,0BAA2B;IACzB,SAAS,EAAE,GAAG;IACd,WAAW,EAAE,SAAS;;EACxB,mCAAoC;IAClC,SAAS,EAAE,GAAG;IACd,WAAW,EAAE,SAAS;;EAE1B,oCAAqC;IACnC,aAAa,EAAE,GAAG;IAClB,SAAS,EAAE,GAAG;;EAEhB,eAAgB;IACd,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,SAAS;IAChB,aAAa,EAAE,SAAS;;EAE1B,2BAA4B;IAC1B,GAAG,EAAE,IAAI;IACT,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,GAAG;IACV,UAAU,EAAE,CAAC;;EACf;+BAC8B;IAC5B,KAAK,EAAE,SAAS;;EAElB,yBAA0B;IACxB,SAAS,EAAE,QAAQ;IACnB,WAAW,EAAE,OAAO;;EAEtB,YAAa;IACX,KAAK,EAAE,IAAI;;EACX,+BAAgC;IAC9B,kBAAkB,EAAE,UAAU;IAC9B,eAAe,EAAE,UAAU;IAC3B,UAAU,EAAE,UAAU;IACtB,KAAK,EAAE,SAAS;;EAClB,iCAAkC;IAChC,kBAAkB,EAAE,UAAU;IAC9B,eAAe,EAAE,UAAU;IAC3B,UAAU,EAAE,UAAU;IACtB,KAAK,EAAE,SAAS;IAChB,WAAW,EAAE,QAAQ;;EAEzB,uBAAwB;IACtB,YAAY,EAAE,GAAG;IACjB,MAAM,EAAE,aAAa;;;;;AAKzB,yCAA0C;EACxC,cAAe;IACb,KAAK,EAAE,cAAc;;EAGvB,QAAS;IACP,KAAK,EAAE,KAAK;;;;;EAKd,EAAG;IACD,WAAW,EAAE,GAAG;IAChB,YAAY,EAAE,GAAG;;EAEnB,EAAG;IACD,WAAW,EAAE,GAAG;;EAElB,KAAM;IACJ,YAAY,EAAE,IAAI;;EAEpB,UAAW;IACT,MAAM,EAAE,6BAA6B;IACrC,YAAY,EAAE,GAAG;;;;;EAKnB,+BAAgC;IAC9B,KAAK,EAAE,IAAI;;EACb,iCAAkC;IAChC,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,CAAC;IACd,UAAU,EAAE,KAAK;;EAEnB,uBAAwB;IACtB,YAAY,EAAE,GAAG;IACjB,MAAM,EAAE,oBAAoB;;;;;EAK9B,QAAS;IACP,UAAU,EAAE,MAAM;;EAClB,iBAAkB;IAChB,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,CAAC;IACT,UAAU,EAAE,SAAS;IACrB,KAAK,EAAE,IAAI;;EACb,6BAA8B;IAC5B,UAAU,EAAE,CAAC;;EAEjB,4BAA6B;IAC3B,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,GAAG;;EAElB;+BAC8B;IAC5B,KAAK,EAAE,KAAK;;EACd,2BAA4B;IAC1B,GAAG,EAAE,IAAI;IACT,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,GAAG;IACV,UAAU,EAAE,CAAC;;EAEf,eAAgB;IACd,KAAK,EAAE,KAAK;;;;;EAKd,cAAe;IACb,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,KAAK;;;;AAIjB,aACwB;EAAtB,KAAK,EAAE,YAAY", 4 | "sources": ["iphone.scss"], 5 | "names": [], 6 | "file": "iphone.css" 7 | } -------------------------------------------------------------------------------- /static/reader/css/override.css: -------------------------------------------------------------------------------- 1 | ::-moz-selection { 2 | background: #000; 3 | color: #fff; } 4 | 5 | ::selection { 6 | background: #000; 7 | color: #fff; } 8 | 9 | article a:hover { 10 | color: black !important; } 11 | 12 | #logo { 13 | font-size: 60px !important; } 14 | 15 | article *, .label, .tooltip { 16 | font-style: normal !important; 17 | font-size: 23px !important; 18 | line-height: 31px !important; 19 | -webkit-hyphens: auto; 20 | hyphens: auto; } 21 | 22 | #work h1 + p, #contact p, #works-wrapper + p, .featured { 23 | font-weight: 400; 24 | text-align: left; } 25 | 26 | #message_wrapper .right label { 27 | width: 80px; } 28 | 29 | input:focus { 30 | outline: none; } 31 | 32 | #search_form input[type="text"] { 33 | padding-left: 10px !important; 34 | font-family: "Ubuntu" !important; } 35 | 36 | #footer_bottom #cancel_button:hover { 37 | background: white !important; } 38 | 39 | label.error { 40 | width: 100% !important; 41 | color: #cc0000 !important; 42 | font-size: 14px !important; 43 | left: 90px !important; } 44 | 45 | #message_wrapper { 46 | padding: 1em 0 !important; 47 | background: white !important; 48 | border: 1px dotted #ccc !important; } 49 | 50 | #message_wrapper .right, #message_wrapper textarea#message { 51 | -webkit-box-sizing: border-box; 52 | -moz-box-sizing: border-box; 53 | box-sizing: border-box; 54 | margin: 0; 55 | padding: 0 1em; } 56 | 57 | #submit-button { 58 | background: black !important; 59 | color: white !important; } 60 | 61 | #home_blog_post.blog_post .section_separator *, #message_wrapper .right label, h1 a, h1 span, footer *, a.more_link { 62 | text-transform: lowercase !important; } 63 | 64 | .blog_post .section_separator *, #post .section_separator * { 65 | text-transform: none !important; } 66 | 67 | article .post_author { 68 | font-style: italic !important; 69 | text-transform: none !important; } 70 | 71 | .post_title, h2, h3 { 72 | font-weight: bold !important; } 73 | 74 | .work-teaser + .work-teaser { 75 | margin-top: 1em; 76 | padding-top: 1em; } 77 | 78 | #content > section { 79 | margin-bottom: 2em; } 80 | 81 | .work-teaser-logo img { 82 | margin: 0 auto; 83 | max-height: 3em; 84 | width: auto !important; } 85 | 86 | .label { 87 | padding: 0 !important; } 88 | 89 | .arrow_nav a .tooltip { 90 | text-transform: none !important; } 91 | 92 | .work-page h1 { 93 | margin-top: 3em; } 94 | 95 | .work-teaser + .work-teaser:before { 96 | display: none; } 97 | 98 | #home_blog_post.blog_post .section_separator *, h1 a, h1 span { 99 | font-weight: bold; } 100 | 101 | #footer_bottom #required_label { 102 | height: auto !important; } 103 | 104 | #works-wrapper + p, .work-intro { 105 | font-weight: normal !important; } 106 | 107 | #home_blog_post { 108 | display: none; } 109 | 110 | h2.blog_post_title { 111 | text-align: left !important; 112 | margin-left: 0 !important; 113 | margin-bottom: 0 !important; } 114 | 115 | .date span { 116 | font-style: italic !important; } 117 | 118 | #post .date { 119 | text-align: center; } 120 | 121 | .post .date + p { 122 | margin-top: 1em; } 123 | 124 | .featured .active { 125 | background: black !important; 126 | color: white !important; } 127 | 128 | #works-wrapper .section_separator { 129 | display: block !important; } 130 | 131 | label { 132 | -webkit-hyphens: none !important; 133 | -moz-hyphens: none !important; 134 | hyphens: none !important; } 135 | 136 | @media only screen and (max-width: 807px) { 137 | #logo { 138 | font-size: 50px !important; } 139 | 140 | article * { 141 | font-size: 21px !important; 142 | line-height: 27px !important; } 143 | 144 | label.error { 145 | left: 0 !important; } 146 | 147 | .work-teaser-description { 148 | width: 100% !important; } 149 | 150 | #contact { 151 | width: 100% !important; 152 | margin: 0 !important; } } 153 | @media only screen and (max-width: 480px) { 154 | #offices .contact { 155 | width: 100% !important; } } 156 | @media only screen and (max-width: 480px) { 157 | article * { 158 | font-size: 19px !important; 159 | line-height: 25px !important; } 160 | 161 | article p { 162 | text-align: left; } 163 | 164 | .section_separator:before, .section_separator .before { 165 | top: 10px; } 166 | 167 | .work-teaser-logo { 168 | width: 40%; 169 | margin: 0 auto; } 170 | 171 | ul { 172 | margin-left: 0px !important; } } 173 | -------------------------------------------------------------------------------- /static/reader/css/views/account-form.scss: -------------------------------------------------------------------------------- 1 | @charset 'utf-8'; 2 | 3 | .account h2 { 4 | text-align: center !important; 5 | } 6 | 7 | .account-form { 8 | margin-top: 1em; 9 | } 10 | 11 | .account-form p { 12 | margin-bottom: 0.3em; 13 | } 14 | 15 | .account-form p label { 16 | display: none; 17 | } 18 | 19 | .account-form p input { 20 | margin: 0 auto; 21 | display: block; 22 | padding-top: 0.5em; 23 | padding-bottom: 0.5em; 24 | } 25 | 26 | .account-form button { 27 | margin-top: 0.7em !important; 28 | margin: 0 auto; 29 | display: block; 30 | } 31 | 32 | #id_remember { 33 | display: none; 34 | } -------------------------------------------------------------------------------- /static/reader/css/views/homepage.scss: -------------------------------------------------------------------------------- 1 | @charset 'utf-8'; 2 | 3 | .featured { 4 | margin-bottom: 1em; 5 | } 6 | 7 | .featured * { 8 | text-align: left !important; 9 | } 10 | 11 | .log-in a { 12 | padding: 15px; 13 | text-transform: lowercase !important; 14 | font-weight: bold; 15 | background-color: #FFF; 16 | color: #000; 17 | display: inline-block; 18 | } 19 | -------------------------------------------------------------------------------- /static/reader/css/views/subscriptions.scss: -------------------------------------------------------------------------------- 1 | @charset 'utf-8'; 2 | 3 | .new-rrs h2 { 4 | text-align: center !important; 5 | } 6 | 7 | .new-rrs-form { 8 | margin-top: 1em; 9 | } 10 | 11 | .new-rrs-form p { 12 | margin-bottom: 0.3em; 13 | } 14 | 15 | .new-rrs-form p label { 16 | display: none; 17 | } 18 | 19 | .new-rrs-form p input { 20 | margin: 0 auto; 21 | display: block; 22 | padding-top: 0.5em; 23 | padding-bottom: 0.5em; 24 | } 25 | 26 | .new-rrs-form button { 27 | margin-top: 0.5em !important; 28 | margin: 0 auto; 29 | display: block; 30 | } 31 | 32 | #start-reading { 33 | text-align: center; 34 | } -------------------------------------------------------------------------------- /static/reader/css/windows.css: -------------------------------------------------------------------------------- 1 | /* FONT-FACE DEFINITIONS */ 2 | @font-face { 3 | font-family: 'iABCRegularSC'; 4 | src: url("./fonts/smallcaps/0/iabc-sc-webfont.eot"); 5 | src: url("./fonts/smallcaps/0/iabc-sc-webfont.eot?#iefix") format("embedded-opentype"), url("./fonts/smallcaps/0/iabc-sc-webfont.svg#iABCRegular") format("svg"), url("./fonts/smallcaps/0/iabc-sc-webfont.ttf") format("truetype"); 6 | font-weight: normal; 7 | font-style: normal; } 8 | 9 | /* WINDOWS DEFINITIONS */ 10 | body, 11 | input, 12 | textarea, 13 | .customStyleSelect, 14 | #message_wrapper input, 15 | #message_wrapper textarea { 16 | font-family: Georgia, serif; 17 | font-weight: normal; 18 | font-style: normal; 19 | font-variant: normal; } 20 | 21 | /* 22 | blockquote, 23 | i, 24 | em, 25 | h3, 26 | .client_quote, 27 | .product_quote, 28 | .project_image figcaption, 29 | .project_slideshow_wrapper figcaption, 30 | .slideshow .slide_title, 31 | .blog_post .blog_post_title, 32 | .post .post_title, 33 | .post h2 ~ h2 { 34 | @include windows-type-italic; 35 | } 36 | */ 37 | .section_separator, 38 | input[type="submit"], 39 | footer, 40 | .more_link, 41 | .client_description_anchors, 42 | .client_description span, 43 | #other_clients .chrono_clients, 44 | #offices .contact h2, 45 | #footer_bottom #cancel_button, 46 | #footer_bottom input[type="submit"], 47 | #search_form input[type="submit"], 48 | #message_wrapper .right label, 49 | #scrollbubble, 50 | .post .post_author, 51 | .project_image figcaption span, 52 | .arrow_nav a { 53 | font-family: Georgia, serif; 54 | font-size: 75%; 55 | text-transform: uppercase; } 56 | 57 | .section_separator, 58 | .section_separator:before { 59 | line-height: 24px; 60 | before: "µµµµµµµ"; } 61 | .section_separator .before, 62 | .section_separator:before .before { 63 | line-height: 24px; } 64 | 65 | .client_description_anchors * { 66 | font-size: 1em; } 67 | 68 | .symbol { 69 | visibility: hidden; } 70 | 71 | #scrollbubble { 72 | filter: none; 73 | font-size: 15px; } 74 | 75 | .post_fleuron { 76 | display: none; } 77 | 78 | .arrow_nav a { 79 | font-size: 0.571em; 80 | line-height: 2.917em; 81 | height: 2.667em; } 82 | .arrow_nav a .symbol { 83 | font-size: 25px; 84 | line-height: 1.28em; } 85 | 86 | #offices .contact h2 { 87 | font-size: 1em; } 88 | 89 | #message_wrapper input, 90 | #message_wrapper textarea { 91 | filter: none; } 92 | #message_wrapper .right label { 93 | width: 80px; 94 | font-size: 0.571em; 95 | height: 2.667em; 96 | line-height: 3em; } 97 | #message_wrapper .right input { 98 | width: 195px; 99 | min-width: 195px; 100 | max-width: 402px; 101 | height: 1.524em; 102 | line-height: 1.524em; } 103 | 104 | #footer_bottom input[type="submit"], 105 | #footer_bottom #cancel_button { 106 | font-size: 0.571em; 107 | height: 2.5em; 108 | line-height: 2.5em; } 109 | 110 | #search_form input[type="submit"] { 111 | font-size: 0.571em; 112 | height: 3.083em; 113 | line-height: 3.083em; } 114 | 115 | footer { 116 | font-size: 0.667em; 117 | line-height: 1.714em; } 118 | 119 | .ie .slideshow_nav_left { 120 | display: block; 121 | position: relative; } 122 | .ie .slideshow_nav_middle { 123 | position: absolute; 124 | top: 50%; } 125 | .ie .slideshow_nav_button { 126 | position: absolute; 127 | top: -50%; } 128 | .ie #scrollbubble { 129 | display: none !important; } 130 | .ie #search_form { 131 | display: block; 132 | width: 510px; 133 | margin: 0 auto; } 134 | .ie #search_form input[type="text"] { 135 | height: 18px; } 136 | .ie #message_wrapper .right input, 137 | .ie #message_wrapper textarea { 138 | font-size: 0.8em; 139 | overflow-x: visible; } 140 | .ie #message_wrapper .right input { 141 | height: 32px; 142 | line-height: 32px; } 143 | .ie #message_wrapper .message.wpcf7-form-control-wrap { 144 | width: 100%; } 145 | .ie #footer_bottom input[type="submit"], 146 | .ie #footer_bottom #cancel_button { 147 | padding: 0; } 148 | .ie #footer_bottom input[type="submit"] { 149 | height: 32px !important; 150 | line-height: 27px !important; } 151 | 152 | @media only screen and (max-width: 480px) { 153 | .arrow_nav a { 154 | text-indent: -9999px; } 155 | .arrow_nav a .tooltip { 156 | text-indent: 0; } 157 | 158 | .arrow_nav a.previous { 159 | background: url("../images/wp7/black_nav_arrow_left.png") no-repeat center center; } 160 | 161 | .arrow_nav a.next { 162 | background: url("../images/wp7/black_nav_arrow_right.png") no-repeat center center; } 163 | 164 | .slideshow_nav_button { 165 | text-indent: -9999px; } 166 | 167 | .slideshow_nav_left .slideshow_nav_button { 168 | background: url("../images/wp7/nav_arrow_left.png") no-repeat center 22px; } 169 | 170 | .slideshow_nav_right .slideshow_nav_button { 171 | background: url("../images/wp7/nav_arrow_right.png") no-repeat center 22px; } 172 | 173 | .section_separator { 174 | background: url("../images/wp7/dotted_bar.png") repeat-x; } 175 | 176 | .section_separator:before { 177 | content: ''; } } 178 | -------------------------------------------------------------------------------- /static/reader/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/reader/img/logo.png -------------------------------------------------------------------------------- /static/reader/js/bubble.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Scroll bubble 3 | * based on http://jsfiddle.net/michaelhue/7NAvm/7/light/ 4 | */ 5 | 6 | $(window).load(function() { 7 | var total_reading_time = new Time(), 8 | scrollTimer = null, 9 | touch = 'ontouchstart' in window, 10 | totalScroll = 0, 11 | previousScroll = 0, 12 | viewportHeight = $(window).height(), 13 | documentHeight = $(document).height(), 14 | bubble = $('#scrollbubble'), 15 | post = $("#content"), 16 | bubbleText = $('#scrollBubbleText'); 17 | 18 | // calculate total reading time 19 | total_reading_time.setTime(timeToRead(post.children())); 20 | // show initial bubble 21 | //staticBubble(total_reading_time.firstString()); 22 | 23 | 24 | /* event listeners */ 25 | 26 | // detect starts and stops to evaluate if we need to show the bubble or not 27 | $(window).bind('scrollstart', function(e) { 28 | previousScroll = null; 29 | }); 30 | 31 | $(window).bind('scrollstop', function(e) { 32 | totalScroll = 0; 33 | }); 34 | 35 | if(touch) 36 | bubble.css('webkit-transition', '-webkit-transform 0.2s ease-out'); 37 | 38 | // main scroll function 39 | $(window).scroll(function() { 40 | if($(document).scrollTop() < 100) { 41 | // post start 42 | //staticBubble(total_reading_time.firstString()); 43 | bubble.fadeOut(100); 44 | } else if(((post.offset().top + post.height() - viewportHeight) - $(document).scrollTop()) < 100) { 45 | // post end 46 | //staticBubble(total_reading_time.lastString()); 47 | bubble.fadeOut(100); 48 | 49 | } else if(!touch && $(document).scrollTop() > 300) { 50 | // normal cases 51 | scrollBubble(); 52 | } else { 53 | bubble.fadeOut(100); 54 | } 55 | }); 56 | 57 | function staticBubble(text) { 58 | if (scrollTimer !== null) 59 | clearTimeout(scrollTimer); 60 | 61 | var distance = distanceBubbleTop(); 62 | 63 | if(touch) 64 | bubble.css('-webkit-transform', 'translate3d(0, ' + (distance + window.pageYOffset) + 'px, 0)'); 65 | else 66 | bubble.css('top', distance); 67 | 68 | bubbleText.html(text) 69 | bubble.fadeIn(100); 70 | } 71 | 72 | function scrollBubble() { 73 | var progressPost = $(document).scrollTop() / post.height(); 74 | distanceTop = $(document).scrollTop(); 75 | 76 | var distance = distanceBubbleTop(); 77 | 78 | bubble.css('top', distance); 79 | 80 | if(previousScroll != null) 81 | totalScroll += Math.abs(previousScroll - distanceTop); 82 | 83 | previousScroll = distanceTop; 84 | 85 | var final_time = new Time(); 86 | final_time.setTime(total_reading_time.seconds*(1-progressPost)); 87 | 88 | bubbleText.html(final_time.readingString()); 89 | 90 | if(totalScroll >= viewportHeight*1.5 && bubble.css('display') == 'none') 91 | bubble.fadeIn(100); 92 | 93 | // Fade out the annotation after 1 second of no scrolling. 94 | if (scrollTimer !== null) { 95 | clearTimeout(scrollTimer); 96 | } 97 | 98 | scrollTimer = setTimeout(function() { 99 | bubble.fadeOut(100); 100 | }, 1000); 101 | } 102 | 103 | // helper function to calculate where to draw the bubble 104 | function distanceBubbleTop() { 105 | var progress = $(document).scrollTop() / (documentHeight - viewportHeight), 106 | scrollbarHeight = viewportHeight / documentHeight * viewportHeight, 107 | distance = 0; 108 | 109 | return progress * (viewportHeight - scrollbarHeight) + scrollbarHeight/2 - bubble.height()/2; 110 | } 111 | 112 | 113 | // helper function to count the number of words in an array 114 | function timeToRead(array) { 115 | var total = 0; 116 | 117 | array.each(function() { 118 | total += Math.round(60*$(this).text().split(' ').length/200); // 200 = number of words per minute 119 | }); 120 | 121 | return total; 122 | } 123 | }); 124 | 125 | 126 | 127 | /* 128 | * Time Object 129 | */ 130 | 131 | function Time() { 132 | this.m = 0; 133 | this.s = 0; 134 | this.seconds = 0; 135 | this.strings = { 136 | 'en': { 137 | 'Thank you.': 'Thank you.', 138 | 'less than 1 minute left': 'less than 1 minute left', 139 | '1 minute left': '1 minute left', 140 | ' minutes left':' minutes left', 141 | '1 minute reading time': '1 minute reading time', 142 | ' minutes reading time': ' minutes reading time' 143 | }, 144 | 'ch': { 145 | 'Thank you.': 'Danke.', 146 | 'less than 1 minute left': '< 1 Minute verbleibend', 147 |      '1 minute left': '1 Minute verbleibend', 148 |      ' minutes left':' Minuten verbleibend', 149 |      '1 minute reading time': '1 Minute Lesezeit', 150 |      ' minutes reading time': ' Minuten Lesezeit' 151 | }, 152 | 'de': { 153 | 'Thank you.': 'Danke.', 154 | 'less than 1 minute left': '< 1 Minute verbleibend', 155 |      '1 minute left': '1 Minute verbleibend', 156 |      ' minutes left':' Minuten verbleibend', 157 |      '1 minute reading time': '1 Minute Lesezeit', 158 |      ' minutes reading time': ' Minuten Lesezeit' 159 | }, 160 | 'ja': { 161 | 'Thank you.': 'Thank you.', 162 | 'less than 1 minute left': 'less than 1 minute left', 163 | '1 minute left': '1 minute left', 164 | ' minutes left':' minutes left', 165 | '1 minute reading time': '1 minute reading time', 166 | ' minutes reading time': ' minutes reading time' 167 | } 168 | } 169 | 170 | // country detection mechanism 171 | var href = window.location.href; 172 | if(href.indexOf('/ch/') != -1) 173 | this.country = 'ch'; 174 | else if(href.indexOf('/ja/') != -1) 175 | this.country = 'ja'; 176 | else if(href.indexOf('/de/') != -1) 177 | this.country = 'de'; 178 | else 179 | this.country = 'en'; 180 | } 181 | 182 | Time.prototype.toString = function() { 183 | var m = (this.m < 10) ? '0' + this.m : this.m, 184 | s = (this.s < 10) ? '0' + this.s : this.s; 185 | return m + ':' + s; 186 | } 187 | 188 | Time.prototype.readingString = function() { 189 | if(this.seconds < 0 || (this.m == 0 && this.s <= 20)) 190 | return this.lang('Thank you.'); 191 | else if(this.m == 0 && this.s <= 60) 192 | return this.lang('less than 1 minute left'); 193 | else if(this.m == 1) 194 | return this.lang('1 minute left'); 195 | else 196 | return this.m + this.lang(' minutes left'); 197 | } 198 | 199 | Time.prototype.firstString = function() { 200 | if(this.m <= 1) 201 | return this.lang('1 minute reading time'); 202 | else 203 | return this.m + this.lang(' minutes reading time'); 204 | } 205 | 206 | Time.prototype.lastString = function() { 207 | return this.lang('Thank you.'); 208 | } 209 | 210 | Time.prototype.setTime = function(seconds) { 211 | this.m = Math.floor(seconds / 60); 212 | this.s = seconds % 60; 213 | 214 | this.seconds = seconds; 215 | } 216 | 217 | Time.prototype.lang = function(string) { 218 | return this.strings[this.country][string]; 219 | } 220 | -------------------------------------------------------------------------------- /static/reader/js/jquery.fitvids.js: -------------------------------------------------------------------------------- 1 | /*global jQuery */ 2 | /*jshint multistr:true browser:true */ 3 | /*! 4 | * FitVids 1.0 5 | * 6 | * Copyright 2011, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com 7 | * Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ 8 | * Released under the WTFPL license - http://sam.zoy.org/wtfpl/ 9 | * 10 | * Date: Thu Sept 01 18:00:00 2011 -0500 11 | */ 12 | 13 | (function( $ ){ 14 | 15 | "use strict"; 16 | 17 | $.fn.fitVids = function( options ) { 18 | var settings = { 19 | customSelector: null 20 | }; 21 | 22 | if(!document.getElementById('fit-vids-style')) { 23 | 24 | var div = document.createElement('div'), 25 | ref = document.getElementsByTagName('base')[0] || document.getElementsByTagName('script')[0]; 26 | 27 | div.className = 'fit-vids-style'; 28 | div.id = 'fit-vids-style'; 29 | div.style.display = 'none'; 30 | div.innerHTML = '­'; 47 | 48 | ref.parentNode.insertBefore(div,ref); 49 | 50 | } 51 | 52 | if ( options ) { 53 | $.extend( settings, options ); 54 | } 55 | 56 | return this.each(function(){ 57 | var selectors = [ 58 | "iframe[src*='player.vimeo.com']", 59 | "iframe[src*='youtube.com']", 60 | "iframe[src*='youtube-nocookie.com']", 61 | "iframe[src*='kickstarter.com'][src*='video.html']", 62 | "object", 63 | "embed" 64 | ]; 65 | 66 | if (settings.customSelector) { 67 | selectors.push(settings.customSelector); 68 | } 69 | 70 | var $allVideos = $(this).find(selectors.join(',')); 71 | $allVideos = $allVideos.not("object object"); // SwfObj conflict patch 72 | 73 | $allVideos.each(function(){ 74 | var $this = $(this); 75 | if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; } 76 | var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(), 77 | width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(), 78 | aspectRatio = height / width; 79 | if(!$this.attr('id')){ 80 | var videoID = 'fitvid' + Math.floor(Math.random()*999999); 81 | $this.attr('id', videoID); 82 | } 83 | $this.wrap('
    ').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+"%"); 84 | $this.removeAttr('height').removeAttr('width'); 85 | }); 86 | }); 87 | }; 88 | })( jQuery ); 89 | -------------------------------------------------------------------------------- /static/reader/js/jquery.scroll.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | var special = jQuery.event.special, 4 | uid1 = 'D' + (+new Date()), 5 | uid2 = 'D' + (+new Date() + 1); 6 | 7 | special.scrollstart = { 8 | setup: function() { 9 | 10 | var timer, 11 | handler = function(evt) { 12 | 13 | var _self = this, 14 | _args = arguments; 15 | 16 | if (timer) { 17 | clearTimeout(timer); 18 | } else { 19 | evt.type = 'scrollstart'; 20 | jQuery.event.handle.apply(_self, _args); 21 | } 22 | 23 | timer = setTimeout( function(){ 24 | timer = null; 25 | }, special.scrollstop.latency); 26 | 27 | }; 28 | 29 | jQuery(this).bind('scroll', handler).data(uid1, handler); 30 | 31 | }, 32 | teardown: function(){ 33 | jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) ); 34 | } 35 | }; 36 | 37 | special.scrollstop = { 38 | latency: 300, 39 | setup: function() { 40 | 41 | var timer, 42 | handler = function(evt) { 43 | 44 | var _self = this, 45 | _args = arguments; 46 | 47 | if (timer) { 48 | clearTimeout(timer); 49 | } 50 | 51 | timer = setTimeout( function(){ 52 | 53 | timer = null; 54 | evt.type = 'scrollstop'; 55 | jQuery.event.handle.apply(_self, _args); 56 | 57 | }, special.scrollstop.latency); 58 | 59 | }; 60 | 61 | jQuery(this).bind('scroll', handler).data(uid2, handler); 62 | 63 | }, 64 | teardown: function() { 65 | jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) ); 66 | } 67 | }; 68 | 69 | })(); 70 | -------------------------------------------------------------------------------- /static/reader/js/main-1.1.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | mobile = ($(window).width() <= 480); 3 | 4 | // change links behaviour if on a touch device (most of these impose a 300ms delay when the user clicks something - we want to avoid that) 5 | var touch = "ontouchend" in document; 6 | 7 | if(typeof(windows) != 'undefined') { 8 | /* we want the font-face elements to be hidden until we are sure the font has loaded */ 9 | $('.symbol').each(function() { 10 | $(this).css('visibility', 'visible'); 11 | }); 12 | } 13 | 14 | }); 15 | 16 | 17 | /* PSEUDO EVENTS FOR INTERNET EXPLORER */ 18 | // by Luke Lutman - http://jquery.lukelutman.com/plugins/pseudo/ 19 | (function($){ 20 | 21 | var patterns = { 22 | text: /^['"]?(.+?)["']?$/, 23 | url: /^url\(["']?(.+?)['"]?\)$/ 24 | }; 25 | 26 | function clean(content) { 27 | if(content && content.length) { 28 | var text = content.match(patterns.text)[1], 29 | url = text.match(patterns.url); 30 | return url ? '': text; 31 | } 32 | } 33 | 34 | function inject(prop, elem, content) { 35 | if(prop != 'after') prop = 'before'; 36 | if(content = clean(elem.currentStyle[prop])) { 37 | $(elem)[prop == 'before' ? 'prepend' : 'append']( 38 | $(document.createElement('span')).addClass(prop).html(content) 39 | ); 40 | } 41 | } 42 | 43 | $.pseudo = function(elem) { 44 | inject('before', elem); 45 | inject('after', elem); 46 | elem.runtimeStyle.behavior = null; 47 | }; 48 | 49 | if(document.createStyleSheet) { 50 | var o = document.createStyleSheet(null, 0); 51 | o.addRule('.dummy','display: static;'); 52 | o.cssText = 'html, head, head *, body, *.before, *.after, *.before *, *.after * { behavior: none; } * { behavior: expression($.pseudo(this)); }'; 53 | } 54 | 55 | })(jQuery); 56 | 57 | 58 | /*! A fix for the iOS orientationchange zoom bug. 59 | Script by @scottjehl, rebound by @wilto. 60 | MIT License. 61 | */ 62 | (function(w){ 63 | 64 | // This fix addresses an iOS bug, so return early if the UA claims it's something else. 65 | if( !( /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1 ) ){ 66 | return; 67 | } 68 | 69 | var doc = w.document; 70 | 71 | if( !doc.querySelector ){ return; } 72 | 73 | var meta = doc.querySelector( "meta[name=viewport]" ), 74 | initialContent = meta && meta.getAttribute( "content" ), 75 | disabledZoom = initialContent + ",maximum-scale=1", 76 | enabledZoom = initialContent + ",maximum-scale=10", 77 | enabled = true, 78 | x, y, z, aig; 79 | 80 | if( !meta ){ return; } 81 | 82 | function restoreZoom(){ 83 | meta.setAttribute( "content", enabledZoom ); 84 | enabled = true; 85 | } 86 | 87 | function disableZoom(){ 88 | meta.setAttribute( "content", disabledZoom ); 89 | enabled = false; 90 | } 91 | 92 | function checkTilt( e ){ 93 | aig = e.accelerationIncludingGravity; 94 | x = Math.abs( aig.x ); 95 | y = Math.abs( aig.y ); 96 | z = Math.abs( aig.z ); 97 | 98 | // If portrait orientation and in one of the danger zones 99 | if( !w.orientation && ( x > 7 || ( ( z > 6 && y < 8 || z < 8 && y > 6 ) && x > 5 ) ) ){ 100 | if( enabled ){ 101 | disableZoom(); 102 | } 103 | } 104 | else if( !enabled ){ 105 | restoreZoom(); 106 | } 107 | } 108 | 109 | w.addEventListener( "orientationchange", restoreZoom, false ); 110 | w.addEventListener( "devicemotion", checkTilt, false ); 111 | 112 | })( this ); 113 | -------------------------------------------------------------------------------- /static/reader/js/next-articles.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | var arrowNav = $('.arrow_nav.top.right'); 3 | var topOffset = arrowNav.offset().top; 4 | var rightOffset = ($(window).width() - (arrowNav.offset().left + arrowNav.outerWidth())); 5 | 6 | var nextArticles = $('.next-articles'); 7 | nextArticles.css('top', (topOffset + arrowNav.outerHeight()) + 'px'); 8 | nextArticles.css('right', rightOffset + 'px'); 9 | 10 | var DEBUG = false; 11 | if(DEBUG) { 12 | nextArticles.show(); 13 | return; 14 | } 15 | 16 | var naShowing = false; 17 | var naAbortHiding = false; 18 | var hoverIn = function() { 19 | naAbortHiding = true; 20 | if(naShowing) { 21 | return; 22 | } 23 | nextArticles.show(100); 24 | naShowing = true; 25 | }; 26 | var hoverOut = function() { 27 | if(!naShowing) { 28 | return; 29 | } 30 | 31 | naAbortHiding = false; 32 | setInterval(function() { 33 | if(naAbortHiding) { 34 | return; 35 | } 36 | nextArticles.hide(100); 37 | naShowing = false; 38 | }, 750); 39 | }; 40 | 41 | var nextButton = $('#next-button'); 42 | nextButton.hover(hoverIn, hoverOut); 43 | nextArticles.hover(hoverIn, hoverOut); 44 | }); 45 | -------------------------------------------------------------------------------- /static/reader/js/post.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('.post').fitVids(); 3 | }); 4 | -------------------------------------------------------------------------------- /static/reader/js/statistics.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var articleId = $('meta[name="article_id"]').attr('content'); 3 | var userId = $('meta[name="user_id"]').attr('content'); 4 | var recordId = null; 5 | var isLiked = false; 6 | 7 | $.ajax({ 8 | url: '/reader/query/read_records/create/' + articleId 9 | }).done(function(data) { 10 | recordId = data.record_id; 11 | isLiked = data.is_liked; 12 | 13 | $('.like-this').click(function() { 14 | if (isLiked) { unlike(); } 15 | else { like(); } 16 | }); 17 | 18 | if (isLiked) { stateLiked(); } 19 | else { stateNotLiked(); } 20 | 21 | }); 22 | 23 | function stateNotLiked() { 24 | $('.like-this span i').removeClass('yellow'); 25 | $('.like-this span i').removeClass('fa-heart'); 26 | $('.like-this span i').addClass('fa-heart-o'); 27 | isLiked = false; 28 | } 29 | 30 | function stateLiking() { 31 | $('.like-this span i').addClass('yellow'); 32 | $('.like-this span i').removeClass('fa-heart'); 33 | $('.like-this span i').addClass('fa-heart-o'); 34 | } 35 | 36 | function stateLiked() { 37 | $('.like-this span i').addClass('yellow'); 38 | $('.like-this span i').addClass('fa-heart'); 39 | $('.like-this span i').removeClass('fa-heart-o'); 40 | isLiked = true; 41 | } 42 | 43 | function like() { 44 | stateLiking(); 45 | $.ajax({ 46 | url: '/reader/query/read_records/like/' + recordId 47 | }).done(function(data) { 48 | if(data.status == "ok") { 49 | stateLiked(); 50 | } else { 51 | like(); 52 | } 53 | }); 54 | } 55 | 56 | function unlike() { 57 | stateLiking(); 58 | $.ajax({ 59 | url: '/reader/query/read_records/unlike/' + recordId 60 | }).done(function(data) { 61 | if(data.status == "ok") { 62 | stateNotLiked(); 63 | } else { 64 | unlike(); 65 | } 66 | }); 67 | } 68 | 69 | })(); 70 | -------------------------------------------------------------------------------- /static/reader/js/subscriptions.js: -------------------------------------------------------------------------------- 1 | function subscribe_or_unsubscribe(source_id) { 2 | if(subscribed_info[source_id]) { 3 | console.log('Unsubscribing ...'); 4 | $('#source-button-' + source_id).text("Unsubscribing"); 5 | $.ajax({ 6 | url: '/reader/query/subscriptions/unsubscribe/' + source_id 7 | }).done(function(data) { 8 | $('#source-button-' + source_id).text("Subscribe"); 9 | subscribed_info[source_id] = false; 10 | }); 11 | } else { 12 | console.log('Subscribing ...'); 13 | $('#source-button-' + source_id).text("Subscribing"); 14 | $.ajax({ 15 | url: '/reader/query/subscriptions/subscribe/' + source_id 16 | }).done(function(data) { 17 | $('#source-button-' + source_id).text("Unsubscribe"); 18 | subscribed_info[source_id] = true; 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/reader/vendor/font-awesome-4.2.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/reader/vendor/font-awesome-4.2.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/reader/vendor/font-awesome-4.2.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/static/reader/vendor/font-awesome-4.2.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "spinning.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | .fa-icon-rotate(@degrees, @rotation) { 14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 15 | -webkit-transform: rotate(@degrees); 16 | -ms-transform: rotate(@degrees); 17 | transform: rotate(@degrees); 18 | } 19 | 20 | .fa-icon-flip(@horiz, @vert, @rotation) { 21 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 22 | -webkit-transform: scale(@horiz, @vert); 23 | -ms-transform: scale(@horiz, @vert); 24 | transform: scale(@horiz, @vert); 25 | } 26 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 9 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 10 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 11 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/spinning.less: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | @-webkit-keyframes fa-spin { 10 | 0% { 11 | -webkit-transform: rotate(0deg); 12 | transform: rotate(0deg); 13 | } 14 | 100% { 15 | -webkit-transform: rotate(359deg); 16 | transform: rotate(359deg); 17 | } 18 | } 19 | 20 | @keyframes fa-spin { 21 | 0% { 22 | -webkit-transform: rotate(0deg); 23 | transform: rotate(0deg); 24 | } 25 | 100% { 26 | -webkit-transform: rotate(359deg); 27 | transform: rotate(359deg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal 14px/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | @mixin fa-icon-rotate($degrees, $rotation) { 14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 15 | -webkit-transform: rotate($degrees); 16 | -ms-transform: rotate($degrees); 17 | transform: rotate($degrees); 18 | } 19 | 20 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 21 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 22 | -webkit-transform: scale($horiz, $vert); 23 | -ms-transform: scale($horiz, $vert); 24 | transform: scale($horiz, $vert); 25 | } 26 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 9 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 10 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 11 | //src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_spinning.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | @-webkit-keyframes fa-spin { 10 | 0% { 11 | -webkit-transform: rotate(0deg); 12 | transform: rotate(0deg); 13 | } 14 | 100% { 15 | -webkit-transform: rotate(359deg); 16 | transform: rotate(359deg); 17 | } 18 | } 19 | 20 | @keyframes fa-spin { 21 | 0% { 22 | -webkit-transform: rotate(0deg); 23 | transform: rotate(0deg); 24 | } 25 | 100% { 26 | -webkit-transform: rotate(359deg); 27 | transform: rotate(359deg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /static/reader/vendor/font-awesome-4.2.0/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "spinning"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | -------------------------------------------------------------------------------- /storycafe/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /storycafe/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorpaas/reread/eea830145ca6b8db16cc4923d12b6cf2b49dc773/storycafe/settings/__init__.py -------------------------------------------------------------------------------- /storycafe/settings/development.py: -------------------------------------------------------------------------------- 1 | from storycafe.settings.production import * 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = True 5 | COMPRESS_ENABLED = False 6 | -------------------------------------------------------------------------------- /storycafe/settings/production.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for storycafe project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.7/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.7/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 14 | 15 | # Quick-start development settings - unsuitable for production 16 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ 17 | 18 | # SECURITY WARNING: keep the secret key used in production secret! 19 | SECRET_KEY = os.environ['SECRET_KEY'] 20 | 21 | # SECURITY WARNING: don't run with debug turned on in production! 22 | BASE_URL = 'http://' + os.environ.get('REREAD_HOST', 'reread.io') 23 | SITE_ID = 1 24 | DEBUG = False 25 | TEMPLATE_DEBUG = False 26 | ALLOWED_HOSTS = [os.environ.get('REREAD_HOST', 'reread.io')] 27 | 28 | # Application definition 29 | 30 | INSTALLED_APPS = ( 31 | 'django.contrib.sites', 32 | 'django.contrib.admin', 33 | 'django.contrib.auth', 34 | 'django.contrib.contenttypes', 35 | 'django.contrib.sessions', 36 | 'django.contrib.messages', 37 | 'django.contrib.staticfiles', 38 | 'compressor', 39 | 'allauth', 40 | 'allauth.account', 41 | 'guardian', 42 | 'reader', 43 | 'home', 44 | ) 45 | 46 | AUTHENTICATION_BACKENDS = ( 47 | 'django.contrib.auth.backends.ModelBackend', # default 48 | 'guardian.backends.ObjectPermissionBackend', 49 | "allauth.account.auth_backends.AuthenticationBackend", 50 | ) 51 | 52 | MIDDLEWARE_CLASSES = ( 53 | 'django.contrib.sessions.middleware.SessionMiddleware', 54 | 'django.middleware.common.CommonMiddleware', 55 | 'django.middleware.csrf.CsrfViewMiddleware', 56 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 57 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 58 | 'django.contrib.messages.middleware.MessageMiddleware', 59 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 60 | ) 61 | 62 | ROOT_URLCONF = 'storycafe.urls' 63 | 64 | WSGI_APPLICATION = 'storycafe.wsgi.application' 65 | 66 | ANONYMOUS_USER_ID = -1 67 | 68 | # Database 69 | 70 | DATABASES = { 71 | 'default': { 72 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 73 | 'NAME': 'reread', 74 | 'USER': 'reread', 75 | 'PASSWORD': 'postgres', 76 | 'HOST': 'db', 77 | 'PORT': '5432' 78 | } 79 | } 80 | 81 | from mongoengine import connect 82 | connect('reread', host='mongo', port=27017) 83 | 84 | # Internationalization 85 | 86 | LANGUAGE_CODE = 'en-us' 87 | TIME_ZONE = 'UTC' 88 | USE_I18N = True 89 | USE_L10N = True 90 | USE_TZ = True 91 | 92 | # Compress 93 | 94 | COMPRESS_ENABLED = True 95 | COMPRESS_PRECOMPILERS = ( 96 | ('text/scss', 'sass --scss --compass {infile} {outfile}'), 97 | ) 98 | 99 | # Static files 100 | 101 | STATIC_URL = '/assets/' 102 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 103 | STATICFILES_FINDERS = ( 104 | 'django.contrib.staticfiles.finders.FileSystemFinder', 105 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 106 | 'compressor.finders.CompressorFinder', 107 | ) 108 | 109 | # Templates 110 | 111 | TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] 112 | TEMPLATE_CONTEXT_PROCESSORS = ( 113 | "django.contrib.auth.context_processors.auth", 114 | "django.core.context_processors.request", 115 | "django.core.context_processors.debug", 116 | "django.core.context_processors.i18n", 117 | "django.core.context_processors.media", 118 | "django.core.context_processors.static", 119 | "django.core.context_processors.tz", 120 | ) 121 | 122 | TEMPLATES = [ 123 | { 124 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 125 | 'DIRS': TEMPLATE_DIRS, 126 | 'APP_DIRS': True, 127 | 'OPTIONS': { 128 | 'context_processors': TEMPLATE_CONTEXT_PROCESSORS, 129 | }, 130 | }, 131 | ] 132 | 133 | 134 | # Allauth 135 | 136 | ACCOUNT_AUTHENTICATION_METHOD = "username_email" 137 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 138 | -------------------------------------------------------------------------------- /storycafe/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from django.contrib import admin, auth 3 | from django.views.generic.base import RedirectView 4 | 5 | urlpatterns = [ 6 | url(r'^admin/', include(admin.site.urls)), 7 | url(r'^accounts/', include('allauth.urls')), 8 | url(r'^accounts/profile/$', RedirectView.as_view(url='/reader/')), 9 | url(r'^accounts/$', RedirectView.as_view(url='/reader/')), 10 | url(r'^reader/', include('reader.urls')), 11 | url(r'^', include('home.urls')), 12 | ] 13 | -------------------------------------------------------------------------------- /storycafe/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for storycafe project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "storycafe.settings.production") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /templates/account/account_inactive.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Account Inactive" %}{% endblock %} 6 | 7 | {% block content %} 8 |

    {% trans "Account Inactive" %}

    9 | 10 |

    {% trans "This account is inactive." %}

    11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/account/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load staticfiles %} 3 | {% load compress %} 4 | 5 | {% block extra_head %} 6 | {% compress css %} 7 | 8 | {% endcompress %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/account/email.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Account" %}{% endblock %} 6 | 7 | {% block content %} 8 |

    {% trans "E-mail Addresses" %}

    9 | {% if user.emailaddress_set.all %} 10 |

    {% trans 'The following e-mail addresses are associated with your account:' %}

    11 | 12 | 41 | 42 | {% else %} 43 |

    {% trans 'Warning:'%} {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}

    44 | 45 | {% endif %} 46 | 47 | 48 |

    {% trans "Add E-mail Address" %}

    49 | 50 |
    51 | {% csrf_token %} 52 | {{ form.as_p}} 53 | 54 |
    55 | 56 | {% endblock %} 57 | 58 | 59 | {% block extra_body %} 60 | 73 | {% endblock %} 74 | -------------------------------------------------------------------------------- /templates/account/email/email_confirmation_message.txt: -------------------------------------------------------------------------------- 1 | {% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktrans with current_site.name as site_name %}User {{ user_display }} at {{ site_name }} has given this as an email address. 2 | 3 | To confirm this is correct, go to {{ activate_url }} 4 | {% endblocktrans %}{% endautoescape %} 5 | -------------------------------------------------------------------------------- /templates/account/email/email_confirmation_signup_message.txt: -------------------------------------------------------------------------------- 1 | {% include "account/email/email_confirmation_message.txt" %} 2 | -------------------------------------------------------------------------------- /templates/account/email/email_confirmation_signup_subject.txt: -------------------------------------------------------------------------------- 1 | {% include "account/email/email_confirmation_subject.txt" %} 2 | -------------------------------------------------------------------------------- /templates/account/email/email_confirmation_subject.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% autoescape off %} 3 | {% blocktrans %}Confirm E-mail Address{% endblocktrans %} 4 | {% endautoescape %} 5 | -------------------------------------------------------------------------------- /templates/account/email/password_reset_key_message.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% blocktrans with site.domain as site_domain %}You're receiving this e-mail because you or someone else has requested a password for your user account at {{site_domain}}. 2 | It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %} 3 | 4 | {{ password_reset_url }} 5 | 6 | {% if username %}{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %} 7 | 8 | {% endif %}{% trans 'Thanks for using our site!' %} 9 | -------------------------------------------------------------------------------- /templates/account/email/password_reset_key_subject.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% autoescape off %} 3 | {% blocktrans %}Password Reset E-mail{% endblocktrans %} 4 | {% endautoescape %} -------------------------------------------------------------------------------- /templates/account/email_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %} 7 | 8 | 9 | {% block content %} 10 |

    {% trans "Confirm E-mail Address" %}

    11 | 12 | {% if confirmation %} 13 | 14 | {% user_display confirmation.email_address.user as user_display %} 15 | 16 |

    {% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}

    17 | 18 |
    19 | {% csrf_token %} 20 | 21 |
    22 | 23 | {% else %} 24 | 25 | {% url 'account_email' as email_url %} 26 | 27 |

    {% blocktrans %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktrans %}

    28 | 29 | {% endif %} 30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /templates/account/email_confirmed.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %} 7 | 8 | 9 | {% block content %} 10 | 11 |

    {% trans "Confirm E-mail Address" %}

    12 | 13 | {% user_display confirmation.email_address.user as user_display %} 14 | 15 |

    {% blocktrans with confirmation.email_address.email as email %}You have confirmed that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}

    16 | 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /templates/account/login.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | {% load staticfiles %} 3 | {% load compress %} 4 | {% load i18n %} 5 | {% load account %} 6 | 7 | {% block head_title %}{% trans "Sign In" %} | Reread{% endblock %} 8 | 9 | {% block content %} 10 | 11 |
    12 | 40 |
    41 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /templates/account/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Sign Out" %} | Reread{% endblock %} 6 | 7 | {% block content %} 8 |
    9 | 22 |
    23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /templates/account/messages/cannot_delete_primary_email.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}You cannot remove your primary e-mail address ({{email}}).{% endblocktrans %} 3 | -------------------------------------------------------------------------------- /templates/account/messages/email_confirmation_sent.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}Confirmation e-mail sent to {{email}}.{% endblocktrans %} 3 | -------------------------------------------------------------------------------- /templates/account/messages/email_confirmed.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}You have confirmed {{email}}.{% endblocktrans %} 3 | -------------------------------------------------------------------------------- /templates/account/messages/email_deleted.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}Removed e-mail address {{email}}.{% endblocktrans %} 3 | -------------------------------------------------------------------------------- /templates/account/messages/logged_in.txt: -------------------------------------------------------------------------------- 1 | {% load account %} 2 | {% load i18n %} 3 | {% user_display user as name %} 4 | {% blocktrans %}Successfully signed in as {{name}}.{% endblocktrans %} 5 | -------------------------------------------------------------------------------- /templates/account/messages/logged_out.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}You have signed out.{% endblocktrans %} 3 | -------------------------------------------------------------------------------- /templates/account/messages/password_changed.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}Password successfully changed.{% endblocktrans %} 3 | 4 | -------------------------------------------------------------------------------- /templates/account/messages/password_set.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}Password successfully set.{% endblocktrans %} 3 | 4 | -------------------------------------------------------------------------------- /templates/account/messages/primary_email_set.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}Primary e-mail address set.{% endblocktrans %} 3 | -------------------------------------------------------------------------------- /templates/account/messages/unverified_primary_email.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}Your primary e-mail address must be verified.{% endblocktrans %} 3 | -------------------------------------------------------------------------------- /templates/account/password_change.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 6 | 7 | {% block content %} 8 |

    {% trans "Change Password" %}

    9 | 10 |
    11 | {% csrf_token %} 12 | {{ form.as_p }} 13 | 14 |
    15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /templates/account/password_reset.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Password Reset" %}{% endblock %} 7 | 8 | {% block content %} 9 | 10 |

    {% trans "Password Reset" %}

    11 | {% if user.is_authenticated %} 12 | {% include "account/snippets/already_logged_in.html" %} 13 | {% endif %} 14 | 15 |

    {% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

    16 | 17 |
    18 | {% csrf_token %} 19 | {{ form.as_p }} 20 | 21 |
    22 | 23 |

    {% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}

    24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /templates/account/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Password Reset" %}{% endblock %} 7 | 8 | {% block content %} 9 |

    {% trans "Password Reset" %}

    10 | 11 | {% if user.is_authenticated %} 12 | {% include "account/snippets/already_logged_in.html" %} 13 | {% endif %} 14 | 15 |

    {% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

    16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /templates/account/password_reset_from_key.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 5 | 6 | {% block content %} 7 |

    {% if token_fail %}{% trans "Bad Token" %}{% else %}{% trans "Change Password" %}{% endif %}

    8 | 9 | {% if token_fail %} 10 | {% url 'account_reset_password' as passwd_reset_url %} 11 |

    {% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktrans %}

    12 | {% else %} 13 | {% if form %} 14 |
    15 | {% csrf_token %} 16 | {{ form.as_p }} 17 | 18 |
    19 | {% else %} 20 |

    {% trans 'Your password is now changed.' %}

    21 | {% endif %} 22 | {% endif %} 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/account/password_reset_from_key_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 5 | 6 | {% block content %} 7 |

    {% trans "Change Password" %}

    8 |

    {% trans 'Your password is now changed.' %}

    9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/account/password_set.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Set Password" %}{% endblock %} 6 | 7 | {% block content %} 8 |

    {% trans "Set Password" %}

    9 | 10 |
    11 | {% csrf_token %} 12 | {{ form.as_p }} 13 | 14 |
    15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /templates/account/signup.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | {% load staticfiles %} 3 | {% load compress %} 4 | {% load i18n %} 5 | 6 | {% block head_title %}{% trans "Signup" %} | Reread{% endblock %} 7 | 8 | {% block content %} 9 |
    10 | 26 |
    27 | 28 | 29 | {% endblock %} 30 | 31 | 32 | -------------------------------------------------------------------------------- /templates/account/signup_closed.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Sign Up Closed" %}{% endblock %} 6 | 7 | {% block content %} 8 |

    {% trans "Sign Up Closed" %}

    9 | 10 |

    {% trans "We are sorry, but the sign up is currently closed." %}

    11 | {% endblock %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/account/snippets/already_logged_in.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load account %} 3 | 4 | {% user_display user as user_display %} 5 |

    {% trans "Note" %}: {% blocktrans %}you are already logged in as {{ user_display }}.{% endblocktrans %}

    6 | -------------------------------------------------------------------------------- /templates/account/verification_sent.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} 6 | 7 | {% block content %} 8 |

    {% trans "Verify Your E-mail Address" %}

    9 | 10 |

    {% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

    11 | 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /templates/account/verified_email_required.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} 6 | 7 | {% block content %} 8 |

    {% trans "Verify Your E-mail Address" %}

    9 | 10 | {% url 'account_email' as email_url %} 11 | 12 |

    {% blocktrans %}This part of the site requires us to verify that 13 | you are who you claim to be. For this purpose, we require that you 14 | verify ownership of your e-mail address. {% endblocktrans %}

    15 | 16 |

    {% blocktrans %}We have sent an e-mail to you for 17 | verification. Please click on the link inside this e-mail. Please 18 | contact us if you do not receive it within a few minutes.{% endblocktrans %}

    19 | 20 |

    {% blocktrans %}Note: you can still change your e-mail address.{% endblocktrans %}

    21 | 22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | {% load compress %} 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% block head_title %}Reread{% endblock %} 17 | 18 | 19 | 20 | {% compress css %} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | {% endcompress %} 41 | 42 | 45 | 46 | {% block extra_head %} 47 | {% endblock %} 48 | 49 | 50 | {% block body %} 51 | 52 | {% block outer_content %} 53 |
    54 |
    55 | 56 |
    57 | {% if messages %} 58 |
    59 | Messages: 60 |
      61 | {% for message in messages %} 62 |
    • {{message}}
    • 63 | {% endfor %} 64 |
    65 |
    66 | {% endif %} 67 | {% block content %} {% endblock %} 68 | 74 |
    75 | {% endblock %} 76 | 77 | 80 | 81 | {% compress js %} 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | {% endcompress %} 90 | {% block extra_body %}{% endblock %} 91 | {% endblock %} 92 | 93 | 94 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /templates/home/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load staticfiles %} 3 | {% load compress %} 4 | 5 | {% block title %} Welcome | Reread {% endblock %} 6 | 7 | {% block content %} 8 |
    9 |
    10 |

    About

    11 |

    Reread is a machine learning news reader. Fork the source code.

    12 |
    13 |
    14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /templates/home/welcome.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load staticfiles %} 3 | {% load compress %} 4 | 5 | {% block title %} Welcome | Reread {% endblock %} 6 | 7 | {% block extra_head %} 8 | {% compress css %} 9 | 10 | {% endcompress %} 11 | {% endblock %} 12 | 13 | {% block content %} 14 |
    15 |
    16 | 19 |
    20 | {% if user.is_authenticated %} 21 | Start Reading 22 | Manage Subscriptions 23 | {% else %} 24 | Sign Up 25 | 26 | {% endif %} 27 |
    28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /templates/reader/article.html: -------------------------------------------------------------------------------- 1 | {% extends "reader/base.html" %} 2 | 3 | {% block content %} 4 | 5 |
    6 |
    7 |
    8 |

    {{ article.title }}

    9 |
    10 | {{ article.published_at }} 11 |
    12 | {{ article.content_html | safe}} 13 |
    14 |
    15 | 16 | {% endblock %} 17 | 18 | {% block extra_arrownav %} 19 | {% if article.is_framing_allowed %} 20 | 21 | 22 | See in Frame 23 | 24 | {% else %} 25 | 26 | 27 | Original Page 28 | 29 | {% endif %} 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /templates/reader/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load compress %} 3 | {% load staticfiles %} 4 | {% load articles %} 5 | 6 | {% block head_title %}{{ article.title }} | Reread {% endblock %} 7 | 8 | {% block extra_head %} 9 | 10 | 11 | {% compress css %} 12 | 13 | {% endcompress %} 14 | {% if history_link %} 15 | 18 | {% endif %} 19 | {% block reader_extra_head %} 20 | {% endblock %} 21 | {% endblock %} 22 | 23 | {% block content %} {% endblock %} 24 | 25 | {% block arrow_topright %} 26 | 27 | {% if user.is_authenticated %} 28 | 32 | {% endif %} 33 | 34 | {% block extra_arrownav %} 35 | {% endblock %} 36 | 37 | {% if article|is_hacker_news_article %} 38 | 39 | 40 | Hacker News Comments 41 | 42 | {% endif %} 43 | 44 | 53 | 54 | {% endblock %} 55 | 56 | 57 | {% block extra_body %} 58 | 59 | {% if user.is_authenticated %} 60 | 71 | {% endif %} 72 | 73 | {% compress js %} 74 | 75 | 76 | {% endcompress %} 77 | 78 | {% block reader_extra_body %} {% endblock %} 79 | {% endblock %} 80 | -------------------------------------------------------------------------------- /templates/reader/frame.html: -------------------------------------------------------------------------------- 1 | {% extends "reader/base.html" %} 2 | {% load staticfiles %} 3 | {% load compress %} 4 | 5 | {% block reader_extra_head %} 6 | {% compress css %} 7 | 8 | {% endcompress %} 9 | {% endblock %} 10 | 11 | {% block outer_content %} 12 | 13 | {% endblock %} 14 | 15 | {% block extra_arrownav %} 16 | 17 | 18 | See In Reader 19 | 20 | {% endblock %} 21 | 22 | {% block reader_extra_body %} 23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /templates/reader/no-more.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load staticfiles %} 3 | 4 | {% block content %} 5 | 6 |
    7 |

    No more articles

    8 | Come back later! 9 |
    10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/reader/subscriptions.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load staticfiles %} 3 | {% load compress %} 4 | {% load subscriptions %} 5 | 6 | {% block title %} Subscriptions | Reread {% endblock %} 7 | 8 | {% block extra_head %} 9 | {% compress css %} 10 | 11 | {% endcompress %} 12 | {% endblock %} 13 | 14 | {% block content %} 15 | 22 |
    23 |
    24 |

    Subscriptions

    25 |

    You can find all your subscriptions here. You can also add new RRS feed and make good use of them. Or if you get bored,

    26 | 27 |
    28 |
    29 |
    30 |

    31 | Add New RSS Feed 32 |

    33 |
    34 |
    35 | {% csrf_token %} 36 | {{ rss_form.as_p }} 37 | 38 |
    39 |
    40 |
    41 |
    42 |
    43 | {% for source in source_list %} 44 |
    45 |

    46 | {{ source.title }} 47 |

    48 | 49 | 50 | 51 | 52 |
    53 | {% endfor %} 54 |
    55 | 56 | {% endblock %} 57 | 58 | {% block extra_body %} 59 | 60 | {% endblock %} 61 | --------------------------------------------------------------------------------