├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── alpha ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── pau ├── __init__.py ├── bridge.py ├── constants.py ├── context_processors.py ├── forms.py ├── gulpfile.js ├── middleware.py ├── models.py ├── package.json ├── presenters │ ├── __init__.py │ ├── feed.py │ ├── messages.py │ ├── user.py │ └── widgets.py ├── static │ ├── build_output │ │ ├── css │ │ │ └── pau.css │ │ └── js │ │ │ └── pau.js │ ├── css │ │ ├── adn_base.scss │ │ ├── adn_base_variables.scss │ │ ├── bold_buttons.scss │ │ ├── bootstrap │ │ │ ├── README │ │ │ ├── _accordion.scss │ │ │ ├── _alerts.scss │ │ │ ├── _breadcrumbs.scss │ │ │ ├── _button-groups.scss │ │ │ ├── _buttons.scss │ │ │ ├── _carousel.scss │ │ │ ├── _close.scss │ │ │ ├── _code.scss │ │ │ ├── _component-animations.scss │ │ │ ├── _dropdowns.scss │ │ │ ├── _forms.scss │ │ │ ├── _grid.scss │ │ │ ├── _hero-unit.scss │ │ │ ├── _labels-badges.scss │ │ │ ├── _layouts.scss │ │ │ ├── _mixins.scss │ │ │ ├── _modals.scss │ │ │ ├── _navbar.scss │ │ │ ├── _navs.scss │ │ │ ├── _pager.scss │ │ │ ├── _pagination.scss │ │ │ ├── _popovers.scss │ │ │ ├── _progress-bars.scss │ │ │ ├── _reset.scss │ │ │ ├── _responsive-1200px-min.scss │ │ │ ├── _responsive-767px-max.scss │ │ │ ├── _responsive-768px-979px.scss │ │ │ ├── _responsive-navbar.scss │ │ │ ├── _responsive-utilities.scss │ │ │ ├── _scaffolding.scss │ │ │ ├── _sprites.scss │ │ │ ├── _tables.scss │ │ │ ├── _thumbnails.scss │ │ │ ├── _tooltip.scss │ │ │ ├── _type.scss │ │ │ ├── _utilities.scss │ │ │ ├── _wells.scss │ │ │ ├── bootstrap.scss │ │ │ └── responsive.scss │ │ ├── feed.scss │ │ ├── file_uploads.scss │ │ ├── font-awesome.scss │ │ ├── message_create.scss │ │ ├── mixin │ │ │ ├── css3.scss │ │ │ ├── media_block.scss │ │ │ ├── textarea.scss │ │ │ └── yui_grid.scss │ │ ├── nav.scss │ │ ├── pau.scss │ │ └── screen │ │ │ └── grids.scss │ ├── images │ │ ├── alpha-index-image-chat.png │ │ ├── alpha-index-image-chat@2x.png │ │ ├── alpha-index-image-photos.png │ │ ├── alpha-index-image-photos@2x.png │ │ ├── alpha-index-image-topics.png │ │ ├── alpha-index-image-topics@2x.png │ │ ├── alpha-index-logo.png │ │ ├── alpha-index-logo@2x.png │ │ ├── alpha-logo-light.png │ │ ├── alpha-logo-light@2x.png │ │ ├── alpha-logo.png │ │ ├── alpha-logo@2x.png │ │ ├── apple-touch-icon.png │ │ ├── favicon.ico │ │ ├── no-icon.png │ │ ├── no-icon@2x.png │ │ ├── post-container-divider.png │ │ ├── sample_cover.jpg │ │ ├── sample_cover@2x.jpg │ │ ├── social-buttons-chat-bubbles-bg.jpg │ │ ├── social-buttons-chat-bubbles-bg@2x.jpg │ │ ├── social-buttons-chat-bubbles.png │ │ └── social-buttons-chat-bubbles@2x.png │ └── js │ │ ├── core │ │ ├── csrf.js │ │ ├── feature_detection.js │ │ ├── init.js │ │ └── module.js │ │ ├── deps │ │ ├── bootstrap │ │ │ ├── README │ │ │ ├── bootstrap-collapse.js │ │ │ ├── bootstrap-dropdown.js │ │ │ ├── bootstrap-modal.js │ │ │ ├── bootstrap-popover.js │ │ │ ├── bootstrap-tab.js │ │ │ ├── bootstrap-tooltip.js │ │ │ └── bootstrap-transition.js │ │ ├── cookie │ │ │ └── jquery.cookie.js │ │ ├── imagesloaded │ │ │ ├── README │ │ │ └── jquery.imagesloaded.js │ │ ├── json │ │ │ ├── json2.js │ │ │ └── tapp_json2.js │ │ ├── modernizr │ │ │ ├── README │ │ │ └── modernizr.js │ │ ├── pjax │ │ │ ├── README │ │ │ └── jquery.pjax.js │ │ ├── placeholder │ │ │ └── jquery.placeholder.js │ │ ├── pubsub │ │ │ └── pubsub.js │ │ ├── twitter-typeahead │ │ │ ├── README │ │ │ └── typeahead.js │ │ └── underscore │ │ │ ├── README │ │ │ └── underscore.js │ │ └── modules │ │ ├── appdotnet_api.js │ │ ├── autocomplete.js │ │ ├── dialogs.js │ │ ├── event_tracking.js │ │ ├── file_uploads.js │ │ ├── follow.js │ │ ├── message_create.js │ │ ├── pau.js │ │ ├── photo.js │ │ ├── stream_view.js │ │ └── utils.js ├── templates-jinja2 │ ├── 429.html │ ├── base.html │ ├── blank.html │ ├── common │ │ ├── footer.html │ │ ├── macros.html │ │ └── nav.html │ ├── explore.html │ ├── global.html │ ├── hashtags.html │ ├── logout.html │ ├── macros │ │ └── forms.html │ ├── oauth_error.html │ ├── pjax_base.html │ ├── pjax_switch.html │ ├── post │ │ ├── create.html │ │ ├── create_done.html │ │ ├── detail.html │ │ ├── photo.html │ │ ├── reposters.html │ │ └── starred_by.html │ ├── splash.html │ ├── stream.html │ ├── two_column_base.html │ └── user │ │ ├── detail.html │ │ ├── follow.html │ │ ├── follow_done.html │ │ ├── follows.html │ │ ├── interactions.html │ │ ├── mentions.html │ │ ├── starred.html │ │ ├── stream.html │ │ ├── subscribe.html │ │ └── subscribe_done.html ├── utils │ ├── __init__.py │ ├── annotations.py │ └── urls.py └── views │ ├── __init__.py │ ├── alpha.py │ ├── auth.py │ ├── base.py │ ├── mixins.py │ └── proxy.py ├── paucore ├── __init__.py ├── data │ ├── __init__.py │ ├── fields.py │ └── pack2.py ├── stats │ ├── __init__.py │ ├── slowjam.py │ └── statsd_client.py ├── utils │ ├── __init__.py │ ├── data.py │ ├── date.py │ ├── htmlgen.py │ ├── image.py │ ├── presenters.py │ ├── python.py │ ├── string.py │ └── web.py └── web │ ├── __init__.py │ ├── auth_backend.py │ ├── context_processors.py │ ├── decorators.py │ ├── jinja2_django.py │ ├── middleware.py │ ├── template.py │ ├── validate_jsonp.py │ └── views.py ├── requirements.txt └── runtime.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | venv 3 | *.pyc 4 | staticfiles 5 | pau/.sass-cache/ 6 | pau/node_modules 7 | alpha/local_settings.py 8 | *.DS_Store 9 | 10 | # Byte-compiled / optimized / DLL files 11 | __pycache__/ 12 | *.py[cod] 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | env/ 20 | build/ 21 | develop-eggs/ 22 | dist/ 23 | eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | *.pot 48 | 49 | # Django stuff: 50 | *.log 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | .ropeproject/ 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mixed Media Labs 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn alpha.wsgi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alpha 2 | 3 | This is an open-source version of alpha.app.net, the web microblogging client for App.net. 4 | 5 | ## Getting Started 6 | 7 | There are a couple of prerequisites for getting Alpha up and running. 8 | 9 | * **An App.net Developer account** 10 | * **Python** - Alpha is a Django app, so you'll need a Python 2.7 environment. We suggest running it inside a virtualenv. 11 | * **Node.js, npm** - You will need Node.js and npm installed to build the static files. If you don't already have Node.js and npm installed, you can follow this [installation guide](http://www.joyent.com/blog/installing-node-and-npm). 12 | * **Sass** - We are also using [Sass](http://sass-lang.com/) to compile CSS. To install Sass, follow the [Install Sass Guide](http://sass-lang.com/install). 13 | 14 | ### Create an App.net App 15 | 16 | Create an App.net application by visiting https://account.app.net/developer/apps/ and choosing "Create An App." Be sure to note your Client ID and Client Secret -- you'll need them in a second. 17 | 18 | ### Set up your environment 19 | 20 | Alpha must be configured with your Client ID and Client Secret as well as an App Access Token for unauthenticated views. Care must be taken to ensure that you do not check in your secrets, so we recommend exporting them as environment variables. 21 | 22 | Start by exporting your Client ID and Client Secret: 23 | 24 | ```sh 25 | export SOCIAL_AUTH_APPDOTNET_KEY=client_id 26 | export SOCIAL_AUTH_APPDOTNET_SECRET=client_secret 27 | ``` 28 | 29 | Now generate an App Access Token: 30 | 31 | ```sh 32 | curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=client_credentials" \ 33 | -d "client_id=$SOCIAL_AUTH_APPDOTNET_KEY" -d "client_secret=$SOCIAL_AUTH_APPDOTNET_SECRET" \ 34 | "https://account.app.net/oauth/access_token" 35 | ``` 36 | 37 | You'll see something like the following: 38 | 39 | ```js 40 | {"access_token":"YOUR_NEW_APP_ACCESS_TOKEN","token":{"scopes":[],"app":{"link":"http://alpha.app.net","name":"Pau","client_id":"YOUR_CLIENT_ID"},"client_id":"YOUR_CLIENT_ID","is_app_token":true}} 41 | ``` 42 | 43 | Copy out the access token part and export that into your environment as well. 44 | 45 | ```sh 46 | export APP_TOKEN=YOUR_NEW_APP_ACCESS_TOKEN 47 | ``` 48 | 49 | Finally, you'll need to make sure that Django is using a unique, secret value for SECRET_KEY. In this skeleton project, we've made it so that SECRET_KEY comes from an environment variable. 50 | 51 | Generate a key with, e.g., `pwgen -N 1 -s 64`, and set it as an environment variable: 52 | 53 | ```sh 54 | pwgen -N 1 -s 64 55 | long-random-key-hello 56 | export SECRET_KEY=long-random-key-hello 57 | ``` 58 | 59 | When running the application, you'll need to export these variables every time (or export them in a shell script which wraps them!) 60 | 61 | Next, create a virtualenv into which we can install our Python requirements and get a dev server up and running: 62 | 63 | ```sh 64 | virtualenv venv 65 | source venv/bin/activate 66 | pip install -r requirements.txt 67 | # Create the database 68 | python manage.py syncdb 69 | # Answer no when it asks you if you would like to create a superuser 70 | ``` 71 | 72 | That should be it! 73 | 74 | ## Running Alpha in development mode 75 | 76 | To run the dev server, do the following: 77 | 78 | ```sh 79 | python manage.py runserver 80 | ``` 81 | 82 | You should now be able to visit http://127.0.0.1:8000 and see the main splash screen for the Alpha project. If everything is setup you should be able to login and start using your own version of Alpha. 83 | 84 | ## Start the static asset compiler 85 | 86 | Alpha uses a Node.js-based static build tool called gulp to compile static assets and recompile them on changes. If you want to make changes to the static assets (CSS, Javascript, etc.), you'll need to run the following: 87 | 88 | ```sh 89 | cd pau 90 | npm install 91 | ./node_modules/.bin/gulp 92 | ``` 93 | 94 | gulp will start watching your files for changes. When you save any static asset file, gulp will recompile the static assets. 95 | 96 | You can run devserver simultaneously in another shell and you should be able to make changes to your static assets and see your changes reflected when you refresh the page. 97 | 98 | ## Deploying Alpha 99 | 100 | There are a number of ways to deploy Alpha. Since this is a pretty normal Django app you can find instructions [here](https://docs.djangoproject.com/en/1.6/howto/deployment/) 101 | 102 | You can even deploy to heroku, here's how. Make sure you have the heroku-toolbelt installed. 103 | 104 | ```sh 105 | heroku create 106 | heroku config:set SOCIAL_AUTH_APPDOTNET_KEY="$SOCIAL_AUTH_APPDOTNET_KEY" SOCIAL_AUTH_APPDOTNET_SECRET="$SOCIAL_AUTH_APPDOTNET_SECRET" APP_TOKEN="$APP_TOKEN" SECRET_KEY="$SECRET_KEY" 107 | git push heroku master 108 | heroku run python manage.py syncdb 109 | ``` 110 | 111 | You should now be able to visit your new heroku instance and use Alpha. 112 | -------------------------------------------------------------------------------- /alpha/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/alpha/__init__.py -------------------------------------------------------------------------------- /alpha/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls import * 3 | 4 | from pau.constants import USERNAME_RE, explore_slug_url_regex 5 | 6 | user_patterns = patterns( 7 | '', 8 | # Because user detail has an optional trailing slash, it is below. 9 | url(r'^post/(?P\d+)$', 'pau.views.alpha.post_detail', name='post_detail_view'), 10 | url(r'^post/(?P\d+)/stars/$', 'pau.views.alpha.starred_by', name='starred_by'), 11 | url(r'^post/(?P\d+)/reposters/$', 'pau.views.alpha.reposters', name='reposters'), 12 | url(r'^post/(?P\d+)/photo/(?P\d+)$', 'pau.views.alpha.photo', name='photo'), 13 | url(r'^post/(?P\d+)/attachment/(?P\d+)$', 'pau.views.alpha.attachment', name='attachment'), 14 | url(r'^followers/$', 'pau.views.alpha.follows_to', name='follows_to'), 15 | url(r'^following/$', 'pau.views.alpha.follows_from', name='follows_from'), 16 | url(r'^stars/$', 'pau.views.alpha.stars_from_user', name='stars_from_user'), 17 | ) 18 | 19 | urlpatterns = patterns( 20 | '', 21 | url(r'^$', 'pau.views.alpha.index_router', name='home'), 22 | 23 | # oauth/access_token is a POST endpoint so we can't just redirect it 24 | # url(r'^oauth/access_token$', 'moku.views.auth.access_token', name='access_token'), 25 | 26 | # social auth 27 | url(r'^login/$', 'social.apps.django_app.views.auth', {'backend': 'appdotnet'}, name='login'), 28 | url(r'^logout/$', 'pau.views.auth.logout', name='logout'), 29 | url(r'^complete/(?P[^/]+)/$', 'pau.views.auth.complete', name='complete'), 30 | # I'd like to kill this since I'm mostly overriding what I want but it wants this to url resolve things like social:complete 31 | url('', include('social.apps.django_app.urls', namespace='social')), 32 | 33 | # alpha URLs 34 | url(r'^global/$', 'pau.views.alpha.global_stream', name='global'), 35 | 36 | url(r'^omo-api-proxy/posts$', 'pau.views.alpha.create_post'), 37 | url(r'^omo-api-proxy/(?P.+)?$', 'pau.views.proxy.ajax_api_proxy', name='omo_api_proxy'), 38 | url(r'^mentions/$', 'pau.views.alpha.mentions', name='mentions'), 39 | url(r'^interactions/$', 'pau.views.alpha.interactions', name='interactions'), 40 | url(r'^browse/%s/$' % explore_slug_url_regex, 'pau.views.alpha.explore_stream', name='explore'), 41 | url(r'^hashtags/(?P.+)$', 'pau.views.alpha.hashtags', name='hashtags'), 42 | 43 | url(r'^\.well-known/webfinger$', 'pau.views.alpha.well_known_webfinger'), 44 | # Because the trailing slash on user detail is optional, I'm special-casing this. But 45 | # otherwise, we can stop c/p'ing the username regular expression below. 46 | # Add views that should be under usernames to user_patterns above. 47 | url(r'^(?P%s)/?$' % (USERNAME_RE), 'pau.views.alpha.user_detail', name='user_detail_view'), 48 | url(r'^(?P%s)/' % (USERNAME_RE), include(user_patterns)), 49 | ) 50 | 51 | if settings.DEBUG: 52 | urlpatterns += patterns( 53 | '', 54 | (r'^pau-static/(?P.*)$', 'django.contrib.staticfiles.views.serve'), 55 | (r'^static/pau/(?P.*)$', 'django.contrib.staticfiles.views.serve'), 56 | ) 57 | -------------------------------------------------------------------------------- /alpha/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for alpha project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks 19 | # if running multiple sites in the same mod_wsgi process. To fix this, use 20 | # mod_wsgi daemon mode with each site in its own daemon process, or use 21 | # os.environ["DJANGO_SETTINGS_MODULE"] = "alpha.settings" 22 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "alpha.settings") 23 | 24 | # This application object is used by any WSGI server configured to use this 25 | # file. This includes Django's development server, if the WSGI_APPLICATION 26 | # setting points here. 27 | from django.core.wsgi import get_wsgi_application 28 | from dj_static import Cling 29 | application = Cling(get_wsgi_application()) 30 | 31 | # Apply WSGI middleware here. 32 | # from helloworld.wsgi import HelloWorldApplication 33 | # application = HelloWorldApplication(application) 34 | -------------------------------------------------------------------------------- /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", "alpha.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /pau/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/__init__.py -------------------------------------------------------------------------------- /pau/constants.py: -------------------------------------------------------------------------------- 1 | 2 | POST_LIMIT = 256 3 | CORE_OEMBED = "net.app.core.oembed" 4 | EXPLORE_SLUG_RE = r'[a-z0-9\-]{,255}' 5 | USERNAME_RE = r'[A-Za-z0-9_]{1,20}' 6 | explore_slug_url_regex = r'(?P' + EXPLORE_SLUG_RE + ')' 7 | -------------------------------------------------------------------------------- /pau/context_processors.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from paucore.utils.web import smart_reverse 4 | 5 | 6 | def url_for(request): 7 | return { 8 | 'url_for': partial(smart_reverse, request) 9 | } 10 | -------------------------------------------------------------------------------- /pau/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from paucore.data.fields import Choices 4 | 5 | REPORT_POST_CHOICES = Choices( 6 | (0, 'UNKNOWN', 'Unknown'), 7 | (1, 'UNWANTED_MENTION', 'Unwanted mention'), 8 | (2, 'EXCESSIVE_POSTING', 'Excessive posting'), 9 | (3, 'EXPLICIT_MATERIAL', 'Explicit/offensive material'), 10 | (4, 'MARKETING_SPAM', 'Marketing spam'), 11 | (5, 'THREATS', 'Threats'), 12 | ) 13 | 14 | REPORT_STATUS = Choices( 15 | (0, 'UNREVIEWED', 'Unreviewed'), 16 | (1, 'RESOLVED', 'Resolved'), 17 | (2, 'ESCALATE', 'Escalated'), 18 | ) 19 | 20 | AVAILABLE_REPORT_POST_CHOICES = [(REPORT_POST_CHOICES.to_enum_dict[key].lower(), REPORT_POST_CHOICES.str_dict[key]) for key in REPORT_POST_CHOICES.key_list if key != REPORT_POST_CHOICES.UNKNOWN] 21 | 22 | 23 | class ReportPostForm(forms.Form): 24 | report_post_data = forms.ChoiceField(label='Reason for reporting post', choices=AVAILABLE_REPORT_POST_CHOICES, widget=forms.Select()) 25 | -------------------------------------------------------------------------------- /pau/gulpfile.js: -------------------------------------------------------------------------------- 1 | /* global require: true */ 2 | 'use strict'; 3 | 4 | var gulp = require('gulp'); 5 | 6 | var sass = require('gulp-ruby-sass'); 7 | var uglify = require('gulp-uglify'); 8 | var concat = require('gulp-concat'); 9 | 10 | var paths = { 11 | css: 'static/css/pau.scss', 12 | js: [ 13 | 'static/js/deps/modernizr/modernizr.js', 14 | 'static/js/deps/bootstrap/bootstrap-collapse.js', 15 | 'static/js/deps/bootstrap/bootstrap-dropdown.js', 16 | 'static/js/deps/bootstrap/bootstrap-modal.js', 17 | 'static/js/deps/cookie/jquery.cookie.js', 18 | 'static/js/deps/placeholder/jquery.placeholder.js', 19 | 'static/js/deps/twitter-typeahead/typeahead.js', 20 | 'static/js/deps/json/json2.js', 21 | 'static/js/deps/pubsub/pubsub.js', 22 | 'static/js/deps/underscore/underscore.js', 23 | 'static/js/deps/pjax/jquery.pjax.js', 24 | 'static/js/deps/imagesloaded/jquery.imagesloaded.js', 25 | 'static/js/core/csrf.js', 26 | 'static/js/core/init.js', 27 | 'static/js/core/feature_detection.js', 28 | 'static/js/core/module.js', 29 | 'static/js/modules/appdotnet_api.js', 30 | 'static/js/modules/utils.js', 31 | 'static/js/modules/dialogs.js', 32 | 'static/js/modules/event_tracking.js', 33 | 'static/js/modules/file_uploads.js', 34 | 'static/js/modules/autocomplete.js', 35 | 'static/js/modules/pau.js', 36 | 'static/js/modules/message_create.js', 37 | 'static/js/modules/stream_view.js', 38 | 'static/js/modules/follow.js', 39 | 'static/js/modules/photo.js', 40 | ] 41 | }; 42 | 43 | gulp.task('javascript', function() { 44 | 45 | return gulp.src(paths.js) 46 | .pipe(uglify()) 47 | .pipe(concat('pau.js')) 48 | .pipe(gulp.dest('static/build_output/js/')); 49 | }); 50 | 51 | gulp.task('css', function() { 52 | 53 | return gulp.src(paths.css) 54 | .pipe(sass()) 55 | .pipe(gulp.dest('static/build_output/css/')); 56 | }); 57 | 58 | // Rerun the task when a file changes 59 | gulp.task('watch', function() { 60 | gulp.watch(paths.js, ['javascript']); 61 | gulp.watch(['static/css/*.scss'], ['css']); 62 | }); 63 | 64 | // The default task (called when you run `gulp` from cli) 65 | gulp.task('default', ['javascript', 'css', 'watch']); 66 | -------------------------------------------------------------------------------- /pau/middleware.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.contrib.auth.middleware import AuthenticationMiddleware 3 | 4 | import adnpy 5 | 6 | 7 | class LazyApi(object): 8 | migrations = set() 9 | 10 | def __init__(self): 11 | self.enabled_migrations = '&'.join('%s=1' % m for m in self.migrations) 12 | 13 | @staticmethod 14 | # stolen from paniolo 15 | def get_adn_api(access_token=None, headers=None): 16 | verify_ssl = True 17 | 18 | extra_headers = { 19 | 'Host': 'api.%s' % settings.PARENT_HOST, 20 | 'X-ADN-Proxied': '1', # we never want to allow our server to make jsonp or CORS requests to the api 21 | } 22 | 23 | if headers: 24 | extra_headers.update(headers) 25 | 26 | return adnpy.api.build_api(api_root=settings.ALPHA_API_ROOT, access_token=access_token, verify_ssl=verify_ssl, 27 | extra_headers=extra_headers) 28 | 29 | def __get__(self, request, obj_type=None): 30 | if not request: 31 | return 32 | 33 | if not hasattr(request, '_cached_api'): 34 | # Build an adnpy api object. We really should save this on the request object so we're not recreating this object for every 35 | # api request we make in one alpha request 36 | try: 37 | token = request.session['OMG_NEW_TOKEN_SPOT_omo_oauth2_token'] 38 | except: 39 | token = settings.APP_TOKEN 40 | 41 | headers = { 42 | 'X-ADN-Migration-Overrides': self.enabled_migrations 43 | } 44 | 45 | api = self.get_adn_api(access_token=token, headers=headers) 46 | 47 | request._cached_api = api 48 | 49 | return request._cached_api 50 | 51 | 52 | class AlphaAuthenticationMiddleware(AuthenticationMiddleware): 53 | def process_request(self, request): 54 | super(AlphaAuthenticationMiddleware, self).process_request(request) 55 | request.__class__.omo_api = LazyApi() 56 | -------------------------------------------------------------------------------- /pau/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractBaseUser, UserManager 3 | 4 | from paucore.data.fields import DictField, CreateDateTimeField, LastModifiedDateTimeField 5 | from paucore.data.pack2 import BoolPackField, Pack2, SinglePack2Container 6 | from paucore.utils.python import memoized_property 7 | 8 | from pau.bridge import APIUser 9 | 10 | 11 | class UserPreferencesPack(Pack2): 12 | use_stream_markers = BoolPackField(key='usm', docstring='Use stream markers in pau?', default=False) 13 | show_unified_in_pau = BoolPackField(key='sup', docstring='Show the unified timeline for "my stream"?', default=False) 14 | disable_leading_mentions_filter = BoolPackField(key='dlmf', docstring='Disable twitter-style leading mentions filter', 15 | default=False) 16 | 17 | 18 | class PauUserManager(UserManager): 19 | def _create_user(self, username, *args, **kwargs): 20 | user = self.model(username=username) 21 | user.save() 22 | return user 23 | 24 | 25 | class User(AbstractBaseUser): 26 | username = models.CharField(max_length=20, unique=True) 27 | 28 | # packs for prefs 29 | preferences = SinglePack2Container(pack_class=UserPreferencesPack, field_name='extra_info', pack_key='p') 30 | # jsonfield for full adn user object 31 | 32 | create_date = CreateDateTimeField() 33 | last_modified_date = LastModifiedDateTimeField() 34 | extra_info = DictField(blank=True, default=lambda: {}) 35 | 36 | objects = PauUserManager() 37 | 38 | USERNAME_FIELD = 'username' 39 | 40 | def __unicode__(self): 41 | return "pk: %s, username: %s" % (self.pk, self.username) 42 | 43 | def get_full_name(self): 44 | return self.username 45 | 46 | def get_short_name(self): 47 | return self.get_full_name() 48 | 49 | @memoized_property() 50 | def adn_user(self): 51 | social_user = self.social_auth.get(provider="appdotnet") 52 | if not social_user: 53 | return 54 | 55 | adn_user = social_user.extra_data.get('user') 56 | if not adn_user: 57 | return 58 | 59 | return APIUser.from_response_data(adn_user) 60 | -------------------------------------------------------------------------------- /pau/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pau-static-asset-build", 3 | "preferGlobal": false, 4 | "version": "0.0.1", 5 | "devDependencies": { 6 | "gulp": "~3.6.2", 7 | "gulp-rev": "~0.3.2", 8 | "gulp-ruby-sass": "~0.4.3", 9 | "gulp-sass": "~0.7.1", 10 | "gulp-uglify": "~0.2.1", 11 | "gulp-concat": "~2.2.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pau/presenters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/presenters/__init__.py -------------------------------------------------------------------------------- /pau/presenters/messages.py: -------------------------------------------------------------------------------- 1 | from paucore.utils.presenters import AbstractPresenter, html 2 | 3 | from pau.constants import POST_LIMIT 4 | from pau.presenters.widgets import file_upload_progress 5 | 6 | 7 | class PostCreatePresenter(AbstractPresenter): 8 | textarea_name = 'post' 9 | char_count = POST_LIMIT 10 | success_message = 'Your post has been created.' 11 | 12 | @classmethod 13 | def from_data(cls, request, btn_action='Post', post_create_pre_text='', reply_to=None, appended_post_url=None, *args, **kwargs): 14 | presenter = cls(*args, **kwargs) 15 | presenter.request = request 16 | presenter.btn_action = btn_action 17 | presenter.in_reply_to_post_id = reply_to.id if reply_to else None 18 | if not post_create_pre_text and reply_to and reply_to.get('user') and request.user.is_authenticated() and request.user.adn_user.id != reply_to.user.id: 19 | post_create_pre_text = '@%s ' % (reply_to.user.username) 20 | 21 | presenter.post_create_pre_text = post_create_pre_text 22 | 23 | presenter.appended_post_url = appended_post_url 24 | 25 | return presenter 26 | 27 | def photo_url_template(self): 28 | photo_url_template = 'https://photo.app.net/' 29 | photo_url_template += '{post_id}/1' 30 | return photo_url_template 31 | 32 | def textarea_data_attrs(self): 33 | 34 | data_attrs = { 35 | 'main-message': 1, 36 | 'text-line-height': 18, 37 | 'current-height': 80, 38 | 'include-attachment': 1, 39 | 'photo-url': self.photo_url_template() 40 | } 41 | 42 | if self.in_reply_to_post_id: 43 | data_attrs['in-reply-to-post-id'] = self.in_reply_to_post_id 44 | 45 | return data_attrs 46 | 47 | def generate_textarea(self, placeholder=''): 48 | data_attrs = self.textarea_data_attrs() 49 | return html.textarea(class_='editable input-flex', name=self.textarea_name, placeholder=placeholder, tabindex='1', data=data_attrs, *[self.post_create_pre_text]) 50 | 51 | def generate_textarea_container(self): 52 | return html.div(class_='text-area layout-like-p subpixel"', *[ 53 | self.generate_textarea() 54 | ]) 55 | 56 | def generate_in_reply_to_container(self): 57 | return html.div(class_='hide in-reply-to layout-like-p subpixel', data={'in-reply-to': 1}, *[ 58 | html.div(html.em(*['In Reply To:'])), 59 | html.div(class_='well-style reply-to', *[ 60 | html.a(href='#', class_='close relative space-5', data={'remove-reply': 1}, *[ 61 | html.i(class_='icon-remove') 62 | ]), 63 | html.div(class_='post-container subpixel', data={'post-copy': 1}) 64 | ]) 65 | ]) 66 | 67 | def generate_append_post_url(self): 68 | return html.div(class_='ta-left', *[ 69 | html.p(*[ 70 | '%s will automatically be appended to your post.' % (self.appended_post_url) 71 | ]) 72 | ]) 73 | 74 | def generate_char_count(self): 75 | return html.span(class_='char-count', data={'char-counter-for': 'message', 'total-chars': self.char_count, 'current-chars': 0}, *[ 76 | unicode(self.char_count) 77 | ]) 78 | 79 | def generate_bottom_row(self): 80 | file_upload = file_upload_progress() 81 | success = html.span(class_='text-success hide', data={'success-text': 1}, *[self.success_message]) 82 | add_photo = html.button(class_='btn-attach-file file-related transition-color', data={'attach-btn': 1}, *[ 83 | html.i(class_='icon-picture'), 84 | u'Add photo\u2026' 85 | ]) 86 | char_count = self.generate_char_count() 87 | create_button = html.button(tabindex='2', data={'submit-button': 1}, class_='btn btn-primary %s-button btn-small disabled' % (self.btn_action.lower()), *[ 88 | self.btn_action 89 | ]) 90 | 91 | return html.grid(*[ 92 | html.div(class_='yui3-u-1-4 ta-left m-yui3-u-none', *[char_count]), 93 | html.div(class_='yui3-u-3-4 ta-right m-yui3-u-1', *[ 94 | file_upload, 95 | success, 96 | add_photo, 97 | create_button, 98 | ]) 99 | ]) 100 | 101 | def generate_html(self): 102 | if not self.request.user.is_authenticated(): 103 | return '' 104 | 105 | parts = [self.generate_textarea_container()] 106 | parts += [self.generate_in_reply_to_container()] 107 | if self.appended_post_url: 108 | parts += [self.generate_append_post_url()] 109 | 110 | parts += [self.generate_bottom_row()] 111 | 112 | return html.div(class_='well-plain well-elevated newpost', data={'message-create': 1}, *parts) 113 | -------------------------------------------------------------------------------- /pau/presenters/widgets.py: -------------------------------------------------------------------------------- 1 | from paucore.utils.presenters import html 2 | 3 | 4 | def file_upload_progress(image_name='', uploaded=False): 5 | hide_progress = 'hide' 6 | text_class = '' 7 | if uploaded: 8 | hide_progress = 'plain' 9 | text_class = ' text-success' 10 | 11 | return html.div(class_='file-related yui3-u', *[ 12 | html.div(class_='hide', *[( 13 | html.input(type_='file', name='upload_file', data={'file-upload-input': 1}) 14 | )]), 15 | html.div(class_='file-preview', data={'image-preview-container': 1}, *[ 16 | html.div(class_=hide_progress, data={'upload-progress': 1}, *[ 17 | html.div(class_='progress', *[ 18 | html.span(class_='image-name', data={'img-name': 1}, *[ 19 | html.i(class_='icon icon-remove-sign transition-color muted' + hide_progress, data={'remove-attachment': 1}), 20 | html.span(class_='image-name-text' + text_class, data={'text': 1}, *[image_name]) 21 | ]), 22 | html.div(class_='bar', style={'width': '0%'}) 23 | ]) 24 | ]) 25 | ]) 26 | ]) 27 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/README: -------------------------------------------------------------------------------- 1 | from http://git/?p=thirdparty/sass-twitter-bootstrap.git;a=commit;h=cd0703b8aa335d01130d7e9d581cbb12013b9ad0 2 | from github: https://github.com/jlong/sass-twitter-bootstrap -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_accordion.scss: -------------------------------------------------------------------------------- 1 | // ACCORDION 2 | // --------- 3 | 4 | 5 | // Parent container 6 | .accordion { 7 | margin-bottom: $baseLineHeight; 8 | } 9 | 10 | // Group == heading + body 11 | .accordion-group { 12 | margin-bottom: 2px; 13 | border: 1px solid #e5e5e5; 14 | @include border-radius(4px); 15 | } 16 | .accordion-heading { 17 | border-bottom: 0; 18 | } 19 | .accordion-heading .accordion-toggle { 20 | display: block; 21 | padding: 8px 15px; 22 | } 23 | 24 | // General toggle styles 25 | .accordion-toggle { 26 | cursor: pointer; 27 | } 28 | 29 | // Inner needs the styles because you can't animate properly with any styles on the element 30 | .accordion-inner { 31 | padding: 9px 15px; 32 | border-top: 1px solid #e5e5e5; 33 | } 34 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_alerts.scss: -------------------------------------------------------------------------------- 1 | // ALERT STYLES 2 | // ------------ 3 | 4 | // Base alert styles 5 | .alert { 6 | padding: 8px 35px 8px 14px; 7 | margin-bottom: $baseLineHeight; 8 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 9 | background-color: $warningBackground; 10 | border: 1px solid $warningBorder; 11 | @include border-radius(4px); 12 | color: $warningText; 13 | p:last-child { 14 | margin-bottom: 0; 15 | } 16 | } 17 | .alert-heading { 18 | color: inherit; 19 | } 20 | 21 | // Adjust close link position 22 | .alert .close { 23 | position: relative; 24 | top: -2px; 25 | right: -21px; 26 | line-height: 18px; 27 | } 28 | 29 | // Alternate styles 30 | // ---------------- 31 | 32 | .alert-success { 33 | background-color: $successBackground; 34 | border-color: $successBorder; 35 | color: $successText; 36 | } 37 | .alert-danger, 38 | .alert-error { 39 | background-color: $errorBackground; 40 | border-color: $errorBorder; 41 | color: $errorText; 42 | } 43 | .alert-info { 44 | background-color: $infoBackground; 45 | border-color: $infoBorder; 46 | color: $infoText; 47 | } 48 | 49 | // Block alerts 50 | // ------------------------ 51 | .alert-block { 52 | padding-top: 14px; 53 | padding-bottom: 14px; 54 | } 55 | .alert-block > p, 56 | .alert-block > ul { 57 | margin-bottom: 0; 58 | } 59 | .alert-block p + p { 60 | margin-top: 5px; 61 | } 62 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | // BREADCRUMBS 2 | // ----------- 3 | 4 | .breadcrumb { 5 | padding: 7px 14px; 6 | margin: 0 0 $baseLineHeight; 7 | list-style: none; 8 | @include gradient-vertical($white, #f5f5f5); 9 | border: 1px solid #ddd; 10 | @include border-radius(3px); 11 | @include box-shadow(inset 0 1px 0 $white); 12 | li { 13 | display: inline-block; 14 | @include ie7-inline-block(); 15 | text-shadow: 0 1px 0 $white; 16 | } 17 | .divider { 18 | padding: 0 5px; 19 | color: $grayLight; 20 | } 21 | .active a { 22 | color: $grayDark; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_carousel.scss: -------------------------------------------------------------------------------- 1 | // CAROUSEL 2 | // -------- 3 | 4 | .carousel { 5 | position: relative; 6 | margin-bottom: $baseLineHeight; 7 | line-height: 1; 8 | } 9 | 10 | .carousel-inner { 11 | overflow: hidden; 12 | width: 100%; 13 | position: relative; 14 | } 15 | 16 | .carousel { 17 | 18 | .item { 19 | display: none; 20 | position: relative; 21 | @include transition(.6s ease-in-out left); 22 | } 23 | 24 | // Account for jankitude on images 25 | .item > img { 26 | display: block; 27 | line-height: 1; 28 | } 29 | 30 | .active, 31 | .next, 32 | .prev { display: block; } 33 | 34 | .active { 35 | left: 0; 36 | } 37 | 38 | .next, 39 | .prev { 40 | position: absolute; 41 | top: 0; 42 | width: 100%; 43 | } 44 | 45 | .next { 46 | left: 100%; 47 | } 48 | .prev { 49 | left: -100%; 50 | } 51 | .next.left, 52 | .prev.right { 53 | left: 0; 54 | } 55 | 56 | .active.left { 57 | left: -100%; 58 | } 59 | .active.right { 60 | left: 100%; 61 | } 62 | 63 | } 64 | 65 | // Left/right controls for nav 66 | // --------------------------- 67 | 68 | .carousel-control { 69 | position: absolute; 70 | top: 40%; 71 | left: 15px; 72 | width: 40px; 73 | height: 40px; 74 | margin-top: -20px; 75 | font-size: 60px; 76 | font-weight: 100; 77 | line-height: 30px; 78 | color: $white; 79 | text-align: center; 80 | background: $grayDarker; 81 | border: 3px solid $white; 82 | @include border-radius(23px); 83 | @include opacity(50); 84 | 85 | // we can't have this transition here 86 | // because webkit cancels the carousel 87 | // animation if you trip this while 88 | // in the middle of another animation 89 | // ;_; 90 | // .transition(opacity .2s linear); 91 | 92 | // Reposition the right one 93 | &.right { 94 | left: auto; 95 | right: 15px; 96 | } 97 | 98 | // Hover state 99 | &:hover { 100 | color: $white; 101 | text-decoration: none; 102 | @include opacity(90); 103 | } 104 | } 105 | 106 | // Caption for text below images 107 | // ----------------------------- 108 | 109 | .carousel-caption { 110 | position: absolute; 111 | left: 0; 112 | right: 0; 113 | bottom: 0; 114 | padding: 10px 15px 5px; 115 | background: $grayDark; 116 | background: rgba(0,0,0,.75); 117 | } 118 | .carousel-caption h4, 119 | .carousel-caption p { 120 | color: $white; 121 | } 122 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_close.scss: -------------------------------------------------------------------------------- 1 | // CLOSE ICONS 2 | // ----------- 3 | 4 | .close { 5 | float: right; 6 | font-size: 20px; 7 | font-weight: bold; 8 | line-height: $baseLineHeight; 9 | color: $black; 10 | text-shadow: 0 1px 0 rgba(255,255,255,1); 11 | @include opacity(20); 12 | 13 | &.relative { 14 | z-index: 2; 15 | position: relative; 16 | } 17 | 18 | &.space-5 { 19 | margin: 5px; 20 | } 21 | 22 | &:hover { 23 | color: $black; 24 | text-decoration: none; 25 | cursor: pointer; 26 | @include opacity(40); 27 | } 28 | } 29 | 30 | // Additional properties for button version 31 | // iOS requires the button element instead of an anchor tag. 32 | // If you want the anchor version, it requires `href="#"`. 33 | button.close { 34 | padding: 0; 35 | cursor: pointer; 36 | background: transparent; 37 | border: 0; 38 | -webkit-appearance: none; 39 | } -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_code.scss: -------------------------------------------------------------------------------- 1 | // Code 2 | // Code typography styles for the and
 elements
 3 | // --------------------------------------------------------
 4 | 
 5 | // Inline and block code styles
 6 | code,
 7 | pre {
 8 |   padding: 0 3px 2px;
 9 |   @include font-family-monospace;
10 |   font-size: $baseFontSize - 1;
11 |   color: $grayDark;
12 |   @include border-radius(3px);
13 | }
14 | 
15 | // Inline code
16 | code {
17 |   padding: 2px 4px;
18 |   color: #d14;
19 |   background-color: #f7f7f9;
20 |   border: 1px solid #e1e1e8;
21 | }
22 | 
23 | // Blocks of code
24 | pre {
25 |   display: block;
26 |   padding: ($baseLineHeight - 1) / 2;
27 |   margin: 0 0 $baseLineHeight / 2;
28 |   font-size: $baseFontSize * .925; // 13px to 12px
29 |   line-height: $baseLineHeight;
30 |   word-break: break-all;
31 |   word-wrap: break-word;
32 |   white-space: pre;
33 |   white-space: pre-wrap;
34 |   background-color: #f5f5f5;
35 |   border: 1px solid #ccc; // fallback for IE7-8
36 |   border: 1px solid rgba(0,0,0,.15);
37 |   @include border-radius(4px);
38 | 
39 |   // Make prettyprint styles more spaced out for readability
40 |   &.prettyprint {
41 |     margin-bottom: $baseLineHeight;
42 |   }
43 | 
44 |   // Account for some code outputs that place code tags in pre tags
45 |   code {
46 |     padding: 0;
47 |     color: inherit;
48 |     background-color: transparent;
49 |     border: 0;
50 |   }
51 | }
52 | 
53 | // Enable scrollable blocks of code
54 | .pre-scrollable {
55 |   max-height: 340px;
56 |   overflow-y: scroll;
57 | }


--------------------------------------------------------------------------------
/pau/static/css/bootstrap/_component-animations.scss:
--------------------------------------------------------------------------------
 1 | // COMPONENT ANIMATIONS
 2 | // --------------------
 3 | 
 4 | .fade {
 5 |   @include opacity(0);
 6 |   @include transition(opacity .15s linear);
 7 |   &.in {
 8 |     @include opacity(100);
 9 |   }
10 | }
11 | 
12 | .collapse {
13 |   position: relative;
14 |   height: 0;
15 |   overflow: hidden;
16 |   @include transition(height .35s ease);
17 |   &.in {
18 |     height: auto;
19 |   }
20 | }


--------------------------------------------------------------------------------
/pau/static/css/bootstrap/_dropdowns.scss:
--------------------------------------------------------------------------------
  1 | // DROPDOWN MENUS
  2 | // --------------
  3 | 
  4 | // Use the .menu class on any 
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns 5 | .dropup, 6 | .dropdown { 7 | position: relative; 8 | } 9 | .dropdown-toggle { 10 | // The caret makes the toggle a bit too tall in IE7 11 | *margin-bottom: -3px; 12 | } 13 | .dropdown-toggle:active, 14 | .open .dropdown-toggle { 15 | outline: 0; 16 | } 17 | 18 | // Dropdown arrow/caret 19 | // -------------------- 20 | .caret { 21 | display: inline-block; 22 | width: 0; 23 | height: 0; 24 | vertical-align: top; 25 | border-top: 4px solid $black; 26 | border-right: 4px solid transparent; 27 | border-left: 4px solid transparent; 28 | content: ""; 29 | @include opacity(30); 30 | } 31 | 32 | // Place the caret 33 | .dropdown .caret { 34 | margin-top: 8px; 35 | margin-left: 2px; 36 | } 37 | .dropdown:hover .caret, 38 | .open .caret { 39 | @include opacity(100); 40 | } 41 | 42 | // The dropdown menu (ul) 43 | // ---------------------- 44 | .dropdown-menu { 45 | position: absolute; 46 | top: 100%; 47 | left: 0; 48 | z-index: $zindexDropdown; 49 | display: none; // none by default, but block on "open" of the menu 50 | float: left; 51 | min-width: 160px; 52 | padding: 4px 0; 53 | margin: 1px 0 0; // override default ul 54 | list-style: none; 55 | background-color: $dropdownBackground; 56 | border: 1px solid #ccc; 57 | border: 1px solid rgba(0,0,0,.2); 58 | *border-right-width: 2px; 59 | *border-bottom-width: 2px; 60 | @include border-radius(5px); 61 | @include box-shadow(0 5px 10px rgba(0,0,0,.2)); 62 | -webkit-background-clip: padding-box; 63 | -moz-background-clip: padding; 64 | background-clip: padding-box; 65 | 66 | // Aligns the dropdown menu to right 67 | &.pull-right { 68 | right: 0; 69 | left: auto; 70 | } 71 | 72 | // Dividers (basically an hr) within the dropdown 73 | .divider { 74 | @include nav-divider(); 75 | } 76 | 77 | // Links within the dropdown menu 78 | a { 79 | display: block; 80 | padding: 3px 15px; 81 | clear: both; 82 | font-weight: normal; 83 | line-height: $baseLineHeight; 84 | color: $dropdownLinkColor; 85 | white-space: nowrap; 86 | } 87 | } 88 | 89 | // Hover state 90 | // ----------- 91 | .dropdown-menu li > a:hover, 92 | .dropdown-menu .active > a, 93 | .dropdown-menu .active > a:hover { 94 | color: $dropdownLinkColorHover; 95 | text-decoration: none; 96 | background-color: $dropdownLinkBackgroundHover; 97 | } 98 | 99 | // Open state for the dropdown 100 | // --------------------------- 101 | .open { 102 | // IE7's z-index only goes to the nearest positioned ancestor, which would 103 | // make the menu appear below buttons that appeared later on the page 104 | *z-index: $zindexDropdown; 105 | 106 | .dropdown-menu { 107 | display: block; 108 | } 109 | } 110 | 111 | // Right aligned dropdowns 112 | // --------------------------- 113 | .pull-right .dropdown-menu { 114 | right: 0; 115 | left: auto; 116 | } 117 | 118 | // Allow for dropdowns to go bottom up (aka, dropup-menu) 119 | // ------------------------------------------------------ 120 | // Just add .dropup after the standard .dropdown class and you're set, bro. 121 | // TODO: abstract this so that the navbar fixed styles are not placed here? 122 | .dropup, 123 | .navbar-fixed-bottom .dropdown { 124 | // Reverse the caret 125 | .caret { 126 | border-top: 0; 127 | border-bottom: 4px solid $black; 128 | content: "\2191"; 129 | } 130 | // Different positioning for bottom up menu 131 | .dropdown-menu { 132 | top: auto; 133 | bottom: 100%; 134 | margin-bottom: 1px; 135 | } 136 | } 137 | 138 | // Typeahead 139 | // --------- 140 | .typeahead { 141 | margin-top: 2px; // give it some space to breathe 142 | @include border-radius(4px); 143 | } 144 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_grid.scss: -------------------------------------------------------------------------------- 1 | // Fixed (940px) 2 | @include grid-core($gridColumnWidth, $gridGutterWidth); 3 | 4 | // Fluid (940px) 5 | @include grid-fluid($fluidGridColumnWidth, $fluidGridGutterWidth); -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_hero-unit.scss: -------------------------------------------------------------------------------- 1 | // HERO UNIT 2 | // --------- 3 | 4 | .hero-unit { 5 | padding: 60px; 6 | margin-bottom: 30px; 7 | background-color: $heroUnitBackground; 8 | @include border-radius(6px); 9 | h1 { 10 | margin-bottom: 0; 11 | font-size: 60px; 12 | line-height: 1; 13 | color: $heroUnitHeadingColor; 14 | letter-spacing: -1px; 15 | } 16 | p { 17 | font-size: 18px; 18 | font-weight: 200; 19 | line-height: $baseLineHeight * 1.5; 20 | color: $heroUnitLeadColor; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_labels-badges.scss: -------------------------------------------------------------------------------- 1 | // LABELS & BADGES 2 | // --------------- 3 | 4 | // Base classes 5 | .label, 6 | .badge { 7 | font-size: $baseFontSize * .846; 8 | font-weight: bold; 9 | line-height: 14px; // ensure proper line-height if floated 10 | color: $white; 11 | vertical-align: baseline; 12 | white-space: nowrap; 13 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 14 | background-color: $grayLight; 15 | } 16 | // Set unique padding and border-radii 17 | .label { 18 | padding: 1px 4px 2px; 19 | @include border-radius(3px); 20 | } 21 | .badge { 22 | padding: 1px 9px 2px; 23 | @include border-radius(9px); 24 | } 25 | 26 | // Hover state, but only for links 27 | a { 28 | &.label:hover, 29 | &.badge:hover { 30 | color: $white; 31 | text-decoration: none; 32 | cursor: pointer; 33 | } 34 | } 35 | 36 | // Colors 37 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) 38 | // Important (red) 39 | .label-important, .badge-important { background-color: $errorText; } 40 | .label-important[href], .badge-important[href] { background-color: darken($errorText, 10%); } 41 | // Warnings (orange) 42 | .label-warning, .badge-warning { background-color: $orange; } 43 | .label-warning[href], .badge-warning[href] { background-color: darken($orange, 10%); } 44 | // Success (green) 45 | .label-success, .badge-success { background-color: $successText; } 46 | .label-success[href], .badge-success[href] { background-color: darken($successText, 10%); } 47 | // Info (turquoise) 48 | .label-info, .badge-info { background-color: $infoText; } 49 | .label-info[href], .badge-info[href] { background-color: darken($infoText, 10%); } 50 | // Inverse (black) 51 | .label-inverse, .badge-inverse { background-color: $grayDark; } 52 | .label-inverse[href], .badge-inverse[href] { background-color: darken($grayDark, 10%); } 53 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_layouts.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Layouts 3 | // Fixed-width and fluid (with sidebar) layouts 4 | // -------------------------------------------- 5 | 6 | 7 | // Container (centered, fixed-width layouts) 8 | .container { 9 | width: $containerWidth; 10 | @include container-fixed(); 11 | } 12 | 13 | // Fluid layouts (left aligned, with sidebar, min- & max-width content) 14 | .container-fluid { 15 | padding-right: $gridGutterWidth; 16 | padding-left: $gridGutterWidth; 17 | @include clearfix(); 18 | } -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_modals.scss: -------------------------------------------------------------------------------- 1 | // MODALS 2 | // ------ 3 | 4 | // Recalculate z-index where appropriate 5 | .modal-open { 6 | .dropdown-menu { z-index: $zindexDropdown + $zindexModal; } 7 | .dropdown.open { *z-index: $zindexDropdown + $zindexModal; } 8 | .popover { z-index: $zindexPopover + $zindexModal; } 9 | .tooltip { z-index: $zindexTooltip + $zindexModal; } 10 | } 11 | 12 | // Background 13 | .modal-backdrop { 14 | position: fixed; 15 | top: 0; 16 | right: 0; 17 | bottom: 0; 18 | left: 0; 19 | z-index: $zindexModalBackdrop; 20 | background-color: $black; 21 | // Fade for backdrop 22 | &.fade { opacity: 0; } 23 | } 24 | 25 | .modal-backdrop, 26 | .modal-backdrop.fade.in { 27 | @include opacity(80); 28 | } 29 | 30 | // Base modal 31 | .modal { 32 | position: fixed; 33 | top: 50%; 34 | left: 50%; 35 | z-index: $zindexModal; 36 | overflow: auto; 37 | width: 560px; 38 | margin: -250px 0 0 -280px; 39 | background-color: $white; 40 | border: 1px solid #999; 41 | border: 1px solid rgba(0,0,0,.3); 42 | *border: 1px solid #999; /* IE6-7 */ 43 | @include border-radius(6px); 44 | @include box-shadow(0 3px 7px rgba(0,0,0,0.3)); 45 | @include background-clip(padding-box); 46 | &.fade { 47 | @include transition(#{opacity .3s linear, top .3s ease-out}); 48 | top: -25%; 49 | } 50 | &.fade.in { top: 50%; } 51 | } 52 | .modal-header { 53 | padding: 9px 15px; 54 | border-bottom: 1px solid #eee; 55 | // Close icon 56 | .close { margin-top: 2px; } 57 | } 58 | 59 | // Body (where all modal content resides) 60 | .modal-body { 61 | overflow-y: auto; 62 | max-height: 400px; 63 | padding: 15px; 64 | } 65 | // Remove bottom margin if need be 66 | .modal-form { 67 | margin-bottom: 0; 68 | } 69 | 70 | // Footer (for actions) 71 | .modal-footer { 72 | padding: 14px 15px 15px; 73 | margin-bottom: 0; 74 | text-align: right; // right align buttons 75 | background-color: #f5f5f5; 76 | border-top: 1px solid #ddd; 77 | @include border-radius(0 0 6px 6px); 78 | @include box-shadow(inset 0 1px 0 $white); 79 | @include clearfix(); // clear it in case folks use .pull-* classes on buttons 80 | 81 | // Properly space out buttons 82 | .btn + .btn { 83 | margin-left: 5px; 84 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 85 | } 86 | // but override that for button groups 87 | .btn-group .btn + .btn { 88 | margin-left: -1px; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_pager.scss: -------------------------------------------------------------------------------- 1 | // PAGER 2 | // ----- 3 | 4 | .pager { 5 | margin-left: 0; 6 | margin-bottom: $baseLineHeight; 7 | list-style: none; 8 | text-align: center; 9 | @include clearfix(); 10 | } 11 | .pager li { 12 | display: inline; 13 | } 14 | .pager a { 15 | display: inline-block; 16 | padding: 5px 14px; 17 | background-color: #fff; 18 | border: 1px solid #ddd; 19 | @include border-radius(15px); 20 | } 21 | .pager a:hover { 22 | text-decoration: none; 23 | background-color: #f5f5f5; 24 | } 25 | .pager .next a { 26 | float: right; 27 | } 28 | .pager .previous a { 29 | float: left; 30 | } 31 | .pager .disabled a, 32 | .pager .disabled a:hover { 33 | color: $grayLight; 34 | background-color: #fff; 35 | cursor: default; 36 | } -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_pagination.scss: -------------------------------------------------------------------------------- 1 | // PAGINATION 2 | // ---------- 3 | 4 | .pagination { 5 | height: $baseLineHeight * 2; 6 | margin: $baseLineHeight 0; 7 | } 8 | .pagination ul { 9 | display: inline-block; 10 | @include ie7-inline-block(); 11 | margin-left: 0; 12 | margin-bottom: 0; 13 | @include border-radius(3px); 14 | @include box-shadow(0 1px 2px rgba(0,0,0,.05)); 15 | } 16 | .pagination li { 17 | display: inline; 18 | } 19 | .pagination a { 20 | float: left; 21 | padding: 0 14px; 22 | line-height: ($baseLineHeight * 2) - 2; 23 | text-decoration: none; 24 | border: 1px solid #ddd; 25 | border-left-width: 0; 26 | } 27 | .pagination a:hover, 28 | .pagination .active a { 29 | background-color: #f5f5f5; 30 | } 31 | .pagination .active a { 32 | color: $grayLight; 33 | cursor: default; 34 | } 35 | .pagination .disabled span, 36 | .pagination .disabled a, 37 | .pagination .disabled a:hover { 38 | color: $grayLight; 39 | background-color: transparent; 40 | cursor: default; 41 | } 42 | .pagination li:first-child a { 43 | border-left-width: 1px; 44 | @include border-radius(3px 0 0 3px); 45 | } 46 | .pagination li:last-child a { 47 | @include border-radius(0 3px 3px 0); 48 | } 49 | 50 | // Centered 51 | .pagination-centered { 52 | text-align: center; 53 | } 54 | .pagination-right { 55 | text-align: right; 56 | } 57 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_popovers.scss: -------------------------------------------------------------------------------- 1 | // POPOVERS 2 | // -------- 3 | 4 | .popover { 5 | position: absolute; 6 | top: 0; 7 | left: 0; 8 | z-index: $zindexPopover; 9 | display: none; 10 | padding: 5px; 11 | &.top { margin-top: -5px; } 12 | &.right { margin-left: 5px; } 13 | &.bottom { margin-top: 5px; } 14 | &.left { margin-left: -5px; } 15 | &.top .arrow { @include popoverArrow-top(); } 16 | &.right .arrow { @include popoverArrow-right(); } 17 | &.bottom .arrow { @include popoverArrow-bottom(); } 18 | &.left .arrow { @include popoverArrow-left(); } 19 | .arrow { 20 | position: absolute; 21 | width: 0; 22 | height: 0; 23 | } 24 | } 25 | .popover-inner { 26 | padding: 3px; 27 | width: 280px; 28 | overflow: hidden; 29 | background: $black; // has to be full background declaration for IE fallback 30 | background: rgba(0,0,0,.8); 31 | @include border-radius(6px); 32 | @include box-shadow(0 3px 7px rgba(0,0,0,0.3)); 33 | 34 | html.breakpoint-phone & { 35 | width: 220px; 36 | } 37 | } 38 | .popover-title { 39 | padding: 9px 15px; 40 | line-height: 1; 41 | background-color: #f5f5f5; 42 | border-bottom:1px solid #eee; 43 | @include border-radius(3px 3px 0 0); 44 | } 45 | .popover-content { 46 | padding: 14px; 47 | background-color: $white; 48 | @include border-radius(0 0 3px 3px); 49 | @include background-clip(padding-box); 50 | p, ul, ol { 51 | margin-bottom: 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_progress-bars.scss: -------------------------------------------------------------------------------- 1 | // PROGRESS BARS 2 | // ------------- 3 | 4 | 5 | // ANIMATIONS 6 | // ---------- 7 | 8 | // Webkit 9 | @-webkit-keyframes progress-bar-stripes { 10 | from { background-position: 40px 0; } 11 | to { background-position: 0 0; } 12 | } 13 | 14 | // Firefox 15 | @-moz-keyframes progress-bar-stripes { 16 | from { background-position: 40px 0; } 17 | to { background-position: 0 0; } 18 | } 19 | 20 | // IE9 21 | @-ms-keyframes progress-bar-stripes { 22 | from { background-position: 40px 0; } 23 | to { background-position: 0 0; } 24 | } 25 | 26 | // Opera 27 | @-o-keyframes progress-bar-stripes { 28 | from { background-position: 0 0; } 29 | to { background-position: 40px 0; } 30 | } 31 | 32 | // Spec 33 | @keyframes progress-bar-stripes { 34 | from { background-position: 40px 0; } 35 | to { background-position: 0 0; } 36 | } 37 | 38 | 39 | 40 | // THE BARS 41 | // -------- 42 | 43 | // Outer container 44 | .progress { 45 | overflow: hidden; 46 | height: 18px; 47 | margin-bottom: 18px; 48 | @include gradient-vertical(#f5f5f5, #f9f9f9); 49 | @include box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); 50 | @include border-radius(4px); 51 | } 52 | 53 | // Bar of progress 54 | .progress .bar { 55 | width: 0%; 56 | height: 18px; 57 | color: $white; 58 | font-size: 12px; 59 | text-align: center; 60 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 61 | @include gradient-vertical(#149bdf, #0480be); 62 | @include box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 63 | @include box-sizing(border-box); 64 | @include transition(width .6s ease); 65 | } 66 | 67 | // Striped bars 68 | .progress-striped .bar { 69 | @include gradient-striped(#149bdf); 70 | @include background-size(40px 40px); 71 | } 72 | 73 | // Call animation for the active one 74 | .progress.active .bar { 75 | -webkit-animation: progress-bar-stripes 2s linear infinite; 76 | -moz-animation: progress-bar-stripes 2s linear infinite; 77 | -ms-animation: progress-bar-stripes 2s linear infinite; 78 | -o-animation: progress-bar-stripes 2s linear infinite; 79 | animation: progress-bar-stripes 2s linear infinite; 80 | } 81 | 82 | 83 | 84 | // COLORS 85 | // ------ 86 | 87 | // Danger (red) 88 | .progress-danger .bar { 89 | @include gradient-vertical(#ee5f5b, #c43c35); 90 | } 91 | .progress-danger.progress-striped .bar { 92 | @include gradient-striped(#ee5f5b); 93 | } 94 | 95 | // Success (green) 96 | .progress-success .bar { 97 | @include gradient-vertical(#62c462, #57a957); 98 | } 99 | .progress-success.progress-striped .bar { 100 | @include gradient-striped(#62c462); 101 | } 102 | 103 | // Info (teal) 104 | .progress-info .bar { 105 | @include gradient-vertical(#5bc0de, #339bb9); 106 | } 107 | .progress-info.progress-striped .bar { 108 | @include gradient-striped(#5bc0de); 109 | } 110 | 111 | // Warning (orange) 112 | .progress-warning .bar { 113 | @include gradient-vertical(lighten($orange, 15%), $orange); 114 | } 115 | .progress-warning.progress-striped .bar { 116 | @include gradient-striped(lighten($orange, 15%)); 117 | } 118 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_reset.scss: -------------------------------------------------------------------------------- 1 | // Reset 2 | // Adapted from Normalize.css http://github.com/necolas/normalize.css 3 | // ------------------------------------------------------------------------ 4 | 5 | // Display in IE6-9 and FF3 6 | // ------------------------- 7 | 8 | article, 9 | aside, 10 | details, 11 | figcaption, 12 | figure, 13 | footer, 14 | header, 15 | hgroup, 16 | nav, 17 | section { 18 | display: block; 19 | } 20 | 21 | // Display block in IE6-9 and FF3 22 | // ------------------------- 23 | 24 | audio, 25 | canvas, 26 | video { 27 | display: inline-block; 28 | *display: inline; 29 | *zoom: 1; 30 | } 31 | 32 | // Prevents modern browsers from displaying 'audio' without controls 33 | // ------------------------- 34 | 35 | audio:not([controls]) { 36 | display: none; 37 | } 38 | 39 | // Base settings 40 | // ------------------------- 41 | 42 | html { 43 | font-size: 100%; 44 | -webkit-text-size-adjust: 100%; 45 | -ms-text-size-adjust: 100%; 46 | } 47 | // Focus states 48 | a:focus { 49 | @include tab-focus(); 50 | } 51 | // Hover & Active 52 | a:hover, 53 | a:active { 54 | outline: 0; 55 | } 56 | 57 | // Prevents sub and sup affecting line-height in all browsers 58 | // ------------------------- 59 | 60 | sub, 61 | sup { 62 | position: relative; 63 | font-size: 75%; 64 | line-height: 0; 65 | vertical-align: baseline; 66 | } 67 | sup { 68 | top: -0.5em; 69 | } 70 | sub { 71 | bottom: -0.25em; 72 | } 73 | 74 | // Img border in a's and image quality 75 | // ------------------------- 76 | 77 | img { 78 | max-width: 100%; // Make images inherently responsive 79 | vertical-align: middle; 80 | border: 0; 81 | -ms-interpolation-mode: bicubic; 82 | } 83 | 84 | // Prevent max-width from affecting Google Maps 85 | #map_canvas img { 86 | max-width: none; 87 | } 88 | 89 | // Forms 90 | // ------------------------- 91 | 92 | // Font size in all browsers, margin changes, misc consistency 93 | button, 94 | input, 95 | select, 96 | textarea { 97 | margin: 0; 98 | font-size: 100%; 99 | vertical-align: middle; 100 | } 101 | button, 102 | input { 103 | *overflow: visible; // Inner spacing ie IE6/7 104 | line-height: normal; // FF3/4 have !important on line-height in UA stylesheet 105 | } 106 | button::-moz-focus-inner, 107 | input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 108 | padding: 0; 109 | border: 0; 110 | } 111 | button, 112 | input[type="button"], 113 | input[type="reset"], 114 | input[type="submit"] { 115 | cursor: pointer; // Cursors on all buttons applied consistently 116 | -webkit-appearance: button; // Style clickable inputs in iOS 117 | } 118 | input[type="search"] { // Appearance in Safari/Chrome 119 | -webkit-box-sizing: content-box; 120 | -moz-box-sizing: content-box; 121 | box-sizing: content-box; 122 | -webkit-appearance: textfield; 123 | } 124 | input[type="search"]::-webkit-search-decoration, 125 | input[type="search"]::-webkit-search-cancel-button { 126 | -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 127 | } 128 | textarea { 129 | overflow: auto; // Remove vertical scrollbar in IE6-9 130 | vertical-align: top; // Readability and alignment cross-browser 131 | } 132 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_responsive-1200px-min.scss: -------------------------------------------------------------------------------- 1 | // LARGE DESKTOP & UP 2 | // ------------------ 3 | 4 | @media (min-width: 1200px) { 5 | 6 | // Fixed grid 7 | @include grid-core(70px, 30px); 8 | 9 | // Fluid grid 10 | @include grid-fluid(5.982905983%, 2.564102564%); 11 | 12 | // Input grid 13 | @include grid-input(70px, 30px); 14 | 15 | // Thumbnails 16 | .thumbnails { 17 | margin-left: -30px; 18 | } 19 | .thumbnails > li { 20 | margin-left: 30px; 21 | } 22 | .row-fluid .thumbnails { 23 | margin-left: 0; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_responsive-767px-max.scss: -------------------------------------------------------------------------------- 1 | // UP TO LANDSCAPE PHONE 2 | // --------------------- 3 | 4 | html.breakpoint-phone, html.breakpoint-tablet { 5 | // Remove width from containers 6 | .container { 7 | width: auto; 8 | } 9 | } 10 | 11 | html.breakpoint-phone { 12 | 13 | // Smooth out the collapsing/expanding nav 14 | .nav-collapse { 15 | -webkit-transform: translate3d(0, 0, 0); // activate the GPU 16 | } 17 | 18 | // Block level the page header small tag for readability 19 | .page-header h1 small { 20 | display: block; 21 | line-height: $baseLineHeight; 22 | } 23 | 24 | // Update checkboxes for iOS 25 | input[type="checkbox"], 26 | input[type="radio"] { 27 | border: 1px solid #ccc; 28 | } 29 | 30 | // Remove the horizontal form styles 31 | .form-horizontal .control-group > label { 32 | float: none; 33 | width: auto; 34 | padding-top: 0; 35 | text-align: left; 36 | } 37 | // Move over all input controls and content 38 | .form-horizontal .controls { 39 | margin-left: 0; 40 | } 41 | // Move the options list down to align with labels 42 | .form-horizontal .control-list { 43 | padding-top: 0; // has to be padding because margin collaspes 44 | } 45 | // Move over buttons in .form-actions to align with .controls 46 | .form-horizontal .form-actions { 47 | padding-left: 10px; 48 | padding-right: 10px; 49 | } 50 | 51 | // Modals 52 | .modal { 53 | position: fixed; 54 | top: 80px; 55 | left: 10px; 56 | right: 10px; 57 | width: auto; 58 | margin: 0; 59 | &.fade.in { top: auto; } 60 | } 61 | .modal-header .close { 62 | padding: 10px; 63 | margin: -10px; 64 | } 65 | 66 | // Carousel 67 | .carousel-caption { 68 | position: static; 69 | } 70 | 71 | } 72 | 73 | 74 | 75 | // LANDSCAPE PHONE TO SMALL DESKTOP & PORTRAIT TABLET 76 | // -------------------------------------------------- 77 | 78 | html.breakpoint-tablet { 79 | 80 | // Padding to set content in a bit 81 | body { 82 | padding-left: 20px; 83 | padding-right: 20px; 84 | } 85 | // Negative indent the now static "fixed" navbar 86 | .navbar-fixed-top, 87 | .navbar-fixed-bottom { 88 | margin-left: -20px; 89 | margin-right: -20px; 90 | } 91 | // Remove padding on container given explicit padding set on body 92 | .container-fluid { 93 | padding: 0; 94 | } 95 | 96 | // TYPOGRAPHY 97 | // ---------- 98 | // Reset horizontal dl 99 | .dl-horizontal { 100 | dt { 101 | float: none; 102 | clear: none; 103 | width: auto; 104 | text-align: left; 105 | } 106 | dd { 107 | margin-left: 0; 108 | } 109 | } 110 | 111 | // GRID & CONTAINERS 112 | // ----------------- 113 | // Fluid rows 114 | .row-fluid { 115 | width: 100%; 116 | } 117 | // Undo negative margin on rows and thumbnails 118 | .row, 119 | .thumbnails { 120 | margin-left: 0; 121 | } 122 | // Make all grid-sized elements block level again 123 | [class*="span"], 124 | .row-fluid [class*="span"] { 125 | float: none; 126 | display: block; 127 | width: auto; 128 | margin-left: 0; 129 | } 130 | 131 | // FORM FIELDS 132 | // ----------- 133 | // Make span* classes full width 134 | .input-large, 135 | .input-xlarge, 136 | .input-xxlarge, 137 | input[class*="span"], 138 | select[class*="span"], 139 | textarea[class*="span"], 140 | .uneditable-input { 141 | @include input-block-level(); 142 | } 143 | // But don't let it screw up prepend/append inputs 144 | .input-prepend input, 145 | .input-append input, 146 | .input-prepend input[class*="span"], 147 | .input-append input[class*="span"] { 148 | display: inline-block; // redeclare so they don't wrap to new lines 149 | width: auto; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_responsive-768px-979px.scss: -------------------------------------------------------------------------------- 1 | // PORTRAIT TABLET TO DEFAULT DESKTOP 2 | // ---------------------------------- 3 | 4 | @media (min-width: 768px) and (max-width: 979px) { 5 | 6 | // Fixed grid 7 | @include grid-core(42px, 20px); 8 | 9 | // Fluid grid 10 | @include grid-fluid(5.801104972%, 2.762430939%); 11 | 12 | // Input grid 13 | @include grid-input(42px, 20px); 14 | 15 | // No need to reset .thumbnails here since it's the same @gridGutterWidth 16 | 17 | } 18 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_responsive-navbar.scss: -------------------------------------------------------------------------------- 1 | // TABLETS AND BELOW 2 | // ----------------- 3 | @media (max-width: 979px) { 4 | 5 | // UNFIX THE TOPBAR 6 | // ---------------- 7 | // Remove any padding from the body 8 | body { 9 | padding-top: 0; 10 | } 11 | // Unfix the navbar 12 | .navbar-fixed-top, 13 | .navbar-fixed-bottom { 14 | position: static; 15 | } 16 | .navbar-fixed-top { 17 | margin-bottom: $baseLineHeight; 18 | } 19 | .navbar-fixed-bottom { 20 | margin-top: $baseLineHeight; 21 | } 22 | .navbar-fixed-top .navbar-inner, 23 | .navbar-fixed-bottom .navbar-inner { 24 | padding: 5px; 25 | } 26 | .navbar .container { 27 | width: auto; 28 | padding: 0; 29 | } 30 | // Account for brand name 31 | .navbar .brand { 32 | padding-left: 10px; 33 | padding-right: 10px; 34 | margin: 0 0 0 -5px; 35 | } 36 | 37 | // COLLAPSIBLE NAVBAR 38 | // ------------------ 39 | // Nav collapse clears brand 40 | .nav-collapse { 41 | clear: both; 42 | } 43 | // Block-level the nav 44 | .nav-collapse .nav { 45 | float: none; 46 | margin: 0 0 ($baseLineHeight / 2); 47 | } 48 | .nav-collapse .nav > li { 49 | float: none; 50 | } 51 | .nav-collapse .nav > li > a { 52 | margin-bottom: 2px; 53 | } 54 | .nav-collapse .nav > .divider-vertical { 55 | display: none; 56 | } 57 | .nav-collapse .nav .nav-header { 58 | color: $navbarText; 59 | text-shadow: none; 60 | } 61 | // Nav and dropdown links in navbar 62 | .nav-collapse .nav > li > a, 63 | .nav-collapse .dropdown-menu a { 64 | padding: 6px 15px; 65 | font-weight: bold; 66 | color: $navbarLinkColor; 67 | @include border-radius(3px); 68 | } 69 | // Buttons 70 | .nav-collapse .btn { 71 | padding: 4px 10px 4px; 72 | font-weight: normal; 73 | @include border-radius(4px); 74 | } 75 | .nav-collapse .dropdown-menu li + li a { 76 | margin-bottom: 2px; 77 | } 78 | .nav-collapse .nav > li > a:hover, 79 | .nav-collapse .dropdown-menu a:hover { 80 | background-color: $navbarBackground; 81 | } 82 | // Buttons in the navbar 83 | .nav-collapse.in .btn-group { 84 | margin-top: 5px; 85 | padding: 0; 86 | } 87 | // Dropdowns in the navbar 88 | .nav-collapse .dropdown-menu { 89 | position: static; 90 | top: auto; 91 | left: auto; 92 | float: none; 93 | display: block; 94 | max-width: none; 95 | margin: 0 15px; 96 | padding: 0; 97 | background-color: transparent; 98 | border: none; 99 | @include border-radius(0); 100 | @include box-shadow(none); 101 | } 102 | .nav-collapse .dropdown-menu:before, 103 | .nav-collapse .dropdown-menu:after { 104 | display: none; 105 | } 106 | .nav-collapse .dropdown-menu .divider { 107 | display: none; 108 | } 109 | // Forms in navbar 110 | .nav-collapse .navbar-form, 111 | .nav-collapse .navbar-search { 112 | float: none; 113 | padding: ($baseLineHeight / 2) 15px; 114 | margin: ($baseLineHeight / 2) 0; 115 | border-top: 1px solid $navbarBackground; 116 | border-bottom: 1px solid $navbarBackground; 117 | @include box-shadow(#{inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)}); 118 | } 119 | // Pull right (secondary) nav content 120 | .navbar .nav-collapse .nav.pull-right { 121 | float: none; 122 | margin-left: 0; 123 | } 124 | // Hide everything in the navbar save .brand and toggle button */ 125 | .nav-collapse, 126 | .nav-collapse.collapse { 127 | overflow: hidden; 128 | height: 0; 129 | } 130 | // Navbar button 131 | .navbar .btn-navbar { 132 | display: block; 133 | } 134 | 135 | // STATIC NAVBAR 136 | // ------------- 137 | .navbar-static .navbar-inner { 138 | padding-left: 10px; 139 | padding-right: 10px; 140 | } 141 | } 142 | 143 | 144 | // DEFAULT DESKTOP 145 | // --------------- 146 | 147 | // Required to make the collapsing navbar work on regular desktops 148 | @media (min-width: 980px) { 149 | .nav-collapse.collapse { 150 | height: auto !important; 151 | overflow: visible !important; 152 | } 153 | } -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_responsive-utilities.scss: -------------------------------------------------------------------------------- 1 | // RESPONSIVE CLASSES 2 | // ------------------ 3 | 4 | // Hide from screenreaders and browsers 5 | // Credit: HTML5 Boilerplate 6 | .hidden { 7 | display: none; 8 | visibility: hidden; 9 | } 10 | 11 | // Visibility utilities 12 | 13 | // For desktops 14 | .visible-phone { display: none !important; } 15 | .visible-tablet { display: none !important; } 16 | .visible-desktop { } // Don't set initially 17 | .hidden-phone { } 18 | .hidden-tablet { } 19 | .hidden-desktop { display: none !important; } 20 | 21 | // Phones only 22 | @media (max-width: 767px) { 23 | // Show 24 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior 25 | // Hide 26 | .hidden-phone { display: none !important; } 27 | // Hide everything else 28 | .hidden-desktop { display: inherit !important; } 29 | .visible-desktop { display: none !important; } 30 | } 31 | 32 | // Tablets & small desktops only 33 | @media (min-width: 768px) and (max-width: 979px) { 34 | // Show 35 | .visible-tablet { display: inherit !important; } 36 | // Hide 37 | .hidden-tablet { display: none !important; } 38 | // Hide everything else 39 | .hidden-desktop { display: inherit !important; } 40 | .visible-desktop { display: none !important ; } 41 | } 42 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_scaffolding.scss: -------------------------------------------------------------------------------- 1 | // Scaffolding 2 | // Basic and global styles for generating a grid system, structural layout, and page templates 3 | // ------------------------------------------------------------------------------------------- 4 | 5 | 6 | // Body reset 7 | // ---------- 8 | 9 | body { 10 | margin: 0; 11 | font-family: $baseFontFamily; 12 | font-size: $baseFontSize; 13 | line-height: $baseLineHeight; 14 | color: $textColor; 15 | background-color: $bodyBackground; 16 | } 17 | 18 | 19 | // Links 20 | // ----- 21 | 22 | a { 23 | color: $linkColor; 24 | text-decoration: none; 25 | } 26 | a:hover { 27 | color: $linkColorHover; 28 | text-decoration: none; 29 | } 30 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_tables.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Tables 3 | // Tables for, you guessed it, tabular data 4 | // ---------------------------------------- 5 | 6 | 7 | // BASE TABLES 8 | // ----------------- 9 | 10 | table { 11 | max-width: 100%; 12 | background-color: $tableBackground; 13 | border-collapse: collapse; 14 | border-spacing: 0; 15 | } 16 | 17 | // BASELINE STYLES 18 | // --------------- 19 | 20 | .table { 21 | width: 100%; 22 | margin-bottom: $baseLineHeight; 23 | // Cells 24 | th, 25 | td { 26 | padding: 8px; 27 | line-height: $baseLineHeight; 28 | text-align: left; 29 | vertical-align: top; 30 | border-top: 1px solid $tableBorder; 31 | } 32 | th { 33 | font-weight: bold; 34 | } 35 | // Bottom align for column headings 36 | thead th { 37 | vertical-align: bottom; 38 | } 39 | // Remove top border from thead by default 40 | caption + thead tr:first-child th, 41 | caption + thead tr:first-child td, 42 | colgroup + thead tr:first-child th, 43 | colgroup + thead tr:first-child td, 44 | thead:first-child tr:first-child th, 45 | thead:first-child tr:first-child td { 46 | border-top: 0; 47 | } 48 | // Account for multiple tbody instances 49 | tbody + tbody { 50 | border-top: 2px solid $tableBorder; 51 | } 52 | } 53 | 54 | 55 | 56 | // CONDENSED TABLE W/ HALF PADDING 57 | // ------------------------------- 58 | 59 | .table-condensed { 60 | th, 61 | td { 62 | padding: 4px 5px; 63 | } 64 | } 65 | 66 | 67 | // BORDERED VERSION 68 | // ---------------- 69 | 70 | .table-bordered { 71 | border: 1px solid $tableBorder; 72 | border-collapse: separate; // Done so we can round those corners! 73 | *border-collapse: collapsed; // IE7 can't round corners anyway 74 | border-left: 0; 75 | @include border-radius(4px); 76 | th, 77 | td { 78 | border-left: 1px solid $tableBorder; 79 | } 80 | // Prevent a double border 81 | caption + thead tr:first-child th, 82 | caption + tbody tr:first-child th, 83 | caption + tbody tr:first-child td, 84 | colgroup + thead tr:first-child th, 85 | colgroup + tbody tr:first-child th, 86 | colgroup + tbody tr:first-child td, 87 | thead:first-child tr:first-child th, 88 | tbody:first-child tr:first-child th, 89 | tbody:first-child tr:first-child td { 90 | border-top: 0; 91 | } 92 | // For first th or td in the first row in the first thead or tbody 93 | thead:first-child tr:first-child th:first-child, 94 | tbody:first-child tr:first-child td:first-child { 95 | -webkit-border-top-left-radius: 4px; 96 | border-top-left-radius: 4px; 97 | -moz-border-radius-topleft: 4px; 98 | } 99 | thead:first-child tr:first-child th:last-child, 100 | tbody:first-child tr:first-child td:last-child { 101 | -webkit-border-top-right-radius: 4px; 102 | border-top-right-radius: 4px; 103 | -moz-border-radius-topright: 4px; 104 | } 105 | // For first th or td in the first row in the first thead or tbody 106 | thead:last-child tr:last-child th:first-child, 107 | tbody:last-child tr:last-child td:first-child { 108 | @include border-radius(0 0 0 4px); 109 | -webkit-border-bottom-left-radius: 4px; 110 | border-bottom-left-radius: 4px; 111 | -moz-border-radius-bottomleft: 4px; 112 | } 113 | thead:last-child tr:last-child th:last-child, 114 | tbody:last-child tr:last-child td:last-child { 115 | -webkit-border-bottom-right-radius: 4px; 116 | border-bottom-right-radius: 4px; 117 | -moz-border-radius-bottomright: 4px; 118 | } 119 | } 120 | 121 | 122 | // ZEBRA-STRIPING 123 | // -------------- 124 | 125 | // Default zebra-stripe styles (alternating gray and transparent backgrounds) 126 | .table-striped { 127 | tbody { 128 | > tr:nth-child(odd) > td, 129 | > tr:nth-child(odd) > th { 130 | background-color: $tableBackgroundAccent; 131 | } 132 | } 133 | } 134 | 135 | 136 | // HOVER EFFECT 137 | // ------------ 138 | // Placed here since it has to come after the potential zebra striping 139 | .table-hover { 140 | tbody { 141 | tr:hover > td, 142 | tr:hover > th { 143 | background-color: $tableBackgroundHover; 144 | } 145 | } 146 | } 147 | 148 | // TABLE CELL SIZING 149 | // ----------------- 150 | 151 | // Change the columns 152 | table { 153 | @for $i from 1 through 24 { 154 | .span#{$i} { @include tableColumns($i); } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_thumbnails.scss: -------------------------------------------------------------------------------- 1 | // THUMBNAILS 2 | // ---------- 3 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files 4 | 5 | // Make wrapper ul behave like the grid 6 | .thumbnails { 7 | margin-left: -$gridGutterWidth; 8 | list-style: none; 9 | @include clearfix(); 10 | } 11 | // Fluid rows have no left margin 12 | .row-fluid .thumbnails { 13 | margin-left: 0; 14 | } 15 | 16 | // Float li to make thumbnails appear in a row 17 | .thumbnails > li { 18 | float: left; // Explicity set the float since we don't require .span* classes 19 | margin-bottom: $baseLineHeight; 20 | margin-left: $gridGutterWidth; 21 | } 22 | 23 | // The actual thumbnail (can be `a` or `div`) 24 | .thumbnail { 25 | display: block; 26 | padding: 4px; 27 | line-height: 1; 28 | border: 1px solid #ddd; 29 | @include border-radius(4px); 30 | @include box-shadow(0 1px 1px rgba(0,0,0,.075)); 31 | } 32 | // Add a hover state for linked versions only 33 | a.thumbnail:hover { 34 | border-color: $linkColor; 35 | @include box-shadow(0 1px 4px rgba(0,105,214,.25)); 36 | } 37 | 38 | // Images and captions 39 | .thumbnail > img { 40 | display: block; 41 | max-width: 100%; 42 | margin-left: auto; 43 | margin-right: auto; 44 | } 45 | .thumbnail .caption { 46 | padding: 9px; 47 | } 48 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_tooltip.scss: -------------------------------------------------------------------------------- 1 | // TOOLTIP 2 | // ------= 3 | 4 | .tooltip { 5 | position: absolute; 6 | z-index: $zindexTooltip; 7 | display: block; 8 | visibility: visible; 9 | padding: 5px; 10 | font-size: 11px; 11 | @include opacity(0); 12 | &.in { @include opacity(80); } 13 | &.top { margin-top: -2px; } 14 | &.right { margin-left: 2px; } 15 | &.bottom { margin-top: 2px; } 16 | &.left { margin-left: -2px; } 17 | &.top .tooltip-arrow { @include popoverArrow-top(); } 18 | &.left .tooltip-arrow { @include popoverArrow-left(); } 19 | &.bottom .tooltip-arrow { @include popoverArrow-bottom(); } 20 | &.right .tooltip-arrow { @include popoverArrow-right(); } 21 | } 22 | .tooltip-inner { 23 | max-width: 200px; 24 | padding: 3px 8px; 25 | color: $white; 26 | text-align: center; 27 | text-decoration: none; 28 | background-color: $black; 29 | @include border-radius(4px); 30 | } 31 | .tooltip-arrow { 32 | position: absolute; 33 | width: 0; 34 | height: 0; 35 | } 36 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_utilities.scss: -------------------------------------------------------------------------------- 1 | // UTILITY CLASSES 2 | // --------------- 3 | 4 | .clearfix { 5 | @include clearfix(); 6 | } 7 | 8 | .hide-text { 9 | @include hide-text(); 10 | } 11 | 12 | .input-block-level { 13 | @include input-block-level(); 14 | } 15 | 16 | // Quick floats 17 | .pull-right { 18 | float: right; 19 | } 20 | .pull-left { 21 | float: left; 22 | } 23 | 24 | // Toggling content 25 | .hide, html.no-js .no-js-hide, html.js .js-hide { 26 | display: none; 27 | } 28 | .show, html.no-js .no-js-show, html.js .js-show { 29 | display: block; 30 | } 31 | 32 | // Visibility 33 | .invisible { 34 | visibility: hidden; 35 | } 36 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/_wells.scss: -------------------------------------------------------------------------------- 1 | // WELLS 2 | // ----- 3 | 4 | .well-plain { 5 | min-height: 20px; 6 | padding: 15px; 7 | margin-bottom: 20px; 8 | } 9 | 10 | .well-style { 11 | background-color: $white; 12 | border: 1px solid $grayLighter; 13 | 14 | blockquote { 15 | border-color: $grayLighter; 16 | } 17 | } 18 | 19 | .well { 20 | @extend .well-plain; 21 | @extend .well-style; 22 | } 23 | 24 | 25 | .well-elevated { 26 | @extend .well-style; 27 | @include box-shadow(0 2px 0 rgba(0, 0, 0, 0.05)); 28 | } 29 | 30 | // Sizes 31 | .well-large { 32 | padding: 24px; 33 | @include border-radius(6px); 34 | } 35 | .well-small { 36 | padding: 9px; 37 | @include border-radius(3px); 38 | } 39 | 40 | .well-sides-only { 41 | @extend .well; 42 | padding: 0 19px; 43 | } 44 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/bootstrap.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.0.4 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | // Core variables and mixins 12 | @import "variables"; // Modify this for custom colors, font-sizes, etc 13 | @import "mixins"; 14 | 15 | // CSS Reset 16 | @import "reset"; 17 | 18 | // Grid system and page structure 19 | @import "scaffolding"; 20 | @import "grid"; 21 | @import "layouts"; 22 | 23 | // Base CSS 24 | @import "type"; 25 | @import "code"; 26 | @import "forms"; 27 | @import "tables"; 28 | 29 | // Components: common 30 | @import "sprites"; 31 | @import "dropdowns"; 32 | @import "wells"; 33 | @import "component-animations"; 34 | @import "close"; 35 | 36 | // Components: Buttons & Alerts 37 | @import "buttons"; 38 | @import "button-groups"; 39 | @import "alerts"; // Note: alerts share common CSS with buttons and thus have styles in buttons 40 | 41 | // Components: Nav 42 | @import "navs"; 43 | @import "navbar"; 44 | @import "breadcrumbs"; 45 | @import "pagination"; 46 | @import "pager"; 47 | 48 | // Components: Popovers 49 | @import "modals"; 50 | @import "tooltip"; 51 | @import "popovers"; 52 | 53 | // Components: Misc 54 | @import "thumbnails"; 55 | @import "labels-badges"; 56 | @import "progress-bars"; 57 | @import "accordion"; 58 | @import "carousel"; 59 | @import "hero-unit"; 60 | 61 | // Utility classes 62 | @import "utilities"; // Has to be last to override when necessary 63 | -------------------------------------------------------------------------------- /pau/static/css/bootstrap/responsive.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.0.4 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | 12 | // Responsive 13 | // For phone and tablet devices 14 | // ------------------------------------------------------------- 15 | 16 | 17 | // REPEAT VARIABLES & MIXINS 18 | // ------------------------- 19 | // Required since we compile the responsive stuff separately 20 | 21 | @import "variables"; // Modify this for custom colors, font-sizes, etc 22 | @import "mixins"; 23 | 24 | 25 | // RESPONSIVE CLASSES 26 | // ------------------ 27 | 28 | @import "responsive-utilities"; 29 | 30 | 31 | // MEDIA QUERIES 32 | // ------------------ 33 | 34 | // Phones to portrait tablets and narrow desktops 35 | @import "responsive-767px-max"; 36 | 37 | // Tablets to regular desktops 38 | @import "responsive-768px-979px"; 39 | 40 | // Large desktops 41 | @import "responsive-1200px-min"; 42 | 43 | 44 | // RESPONSIVE NAVBAR 45 | // ------------------ 46 | 47 | // From 979px and below, show a button to toggle navbar contents 48 | @import "responsive-navbar"; 49 | -------------------------------------------------------------------------------- /pau/static/css/file_uploads.scss: -------------------------------------------------------------------------------- 1 | .file-preview { 2 | margin-top: 5px; 3 | margin-right: 10px; 4 | 5 | div.progress { 6 | margin-top: 8px; 7 | margin-bottom: 0; 8 | width: 250px; 9 | position: relative; 10 | line-height: 15px; 11 | text-align: right; 12 | background-color: $themeSolid; 13 | background-image: none; 14 | border: 1px solid $grayLighter; 15 | @include border-radius(18px); 16 | @include box-shadow(inset 0 1px 0 rgba(0, 0, 0, .05)); 17 | 18 | html.breakpoint-phone & { 19 | width: 160px; 20 | } 21 | 22 | .bar { 23 | position: absolute; 24 | width: 100%; 25 | height: 18px; 26 | top: 0; 27 | left: 0; 28 | z-index: 0; 29 | background-image: none; 30 | background-color: $themeAttention; 31 | @include border-radius(18px); 32 | @include box-shadow(inset 0 1px 0 rgba(0, 0, 0, .05)); 33 | } 34 | 35 | .image-name { 36 | margin-right: 8px; 37 | position: relative; 38 | z-index: 1; 39 | font-size: 11px; 40 | color: $white; 41 | font-family: $sansFontFamily; 42 | text-shadow: 0 1px 0 rgba(0, 0, 0, .05); 43 | 44 | .image-name-text { 45 | display: inline-block; 46 | max-width: 200px; 47 | overflow: hidden; 48 | white-space: nowrap; 49 | text-overflow: ellipsis; 50 | vertical-align: bottom; 51 | 52 | html.breakpoint-phone & { 53 | max-width: 110px; 54 | } 55 | } 56 | } 57 | } 58 | 59 | .plain .progress { 60 | border-color: transparent; 61 | background-color: transparent; 62 | background-image: none; 63 | @include border-radius(18px); 64 | @include box-shadow(none); 65 | 66 | .image-name { 67 | 68 | .icon-remove-sign { 69 | cursor: pointer; 70 | position: relative; 71 | top: 1px; 72 | font-size: 14px; 73 | margin-right: 4px; 74 | color: lighten($themeSubText, 15%); 75 | 76 | &:hover { 77 | color: darken($themeSubText, 10%); 78 | } 79 | } 80 | 81 | .text-success { 82 | color: $themeAccent; 83 | font-weight: normal; 84 | text-shadow: none; 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pau/static/css/font-awesome.scss: -------------------------------------------------------------------------------- 1 | .icon-repost:before { 2 | content:"\f079"; 3 | } -------------------------------------------------------------------------------- /pau/static/css/mixin/css3.scss: -------------------------------------------------------------------------------- 1 | $lightestGray: #eff0f6; // sidebar and pagination background-color 2 | $lightGray: #e3e4ea; // wrapper background-color 3 | $darkerGray: #d1d2d9; // sidebar and pagination border-color 4 | $darkestGray: #cacbd1; // block border-colors, wrapper border-color 5 | $purple: #6274a5; // link color 6 | $purpleBlue: #777a99; // download button 7 | $purpleGray: #8f93a3; // light feed, sidebar titles & unstyled download button bg 8 | $lightestBlue: #3e4968; // Used as a highlight on dark backgrounds 9 | $lightBlue: #343b50; 10 | $darkBlue: #282f43; // a lighter blue than $darkestBlue 11 | $darkestBlue: #101420; // used in header and other places where a deep blue is necessary 12 | 13 | @mixin box_shadow($color, $hoff, $voff, $blur, $spread) { 14 | -moz-box-shadow: $color $hoff $voff $blur $spread; 15 | -webkit-box-shadow: $color $hoff $voff $blur $spread; 16 | -o-box-shadow: $color $hoff $voff $blur $spread; 17 | box-shadow: $color $hoff $voff $blur $spread; 18 | } 19 | 20 | @mixin box_shadow_inset($inset_color, $inset_hoff, $inset_voff, $inset_blur, $color, $hoff, $voff, $blur) { 21 | -moz-box-shadow: $inset_hoff $inset_voff $inset_blur $inset_color inset, $hoff $voff $blur $color; 22 | -webkit-box-shadow: $inset_hoff $inset_voff $inset_blur $inset_color inset, $hoff $voff $blur $color; 23 | -o-box-shadow: $inset_hoff $inset_voff $inset_blur $inset_color inset, $hoff $voff $blur $color; 24 | box-shadow: $inset_hoff $inset_voff $inset_blur $inset_color inset, $hoff $voff $blur $color; 25 | } 26 | 27 | @mixin background_size($x, $y) { 28 | -webkit-background-size: $x, $y; 29 | -mox-background-size: $x, $y; 30 | -o-background-size: $x, $y; 31 | background-size: $x, $y; 32 | } 33 | 34 | @mixin border_radius($vertical, $horizontal:'') { 35 | -webkit-border-radius: $vertical; 36 | -moz-border-radius: $vertical; 37 | -o-border-radius: $vertical; 38 | -ms-border-radius: $vertical; 39 | -khtml-border-radius: $vertical; 40 | border-radius: $vertical; 41 | } 42 | 43 | @mixin linear_gradient($color1, $color2, $stop) { 44 | background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, $color1), color-stop($stop, $color2)); 45 | background-image: -moz-linear-gradient(top, $color1 0%, $color2 $stop); 46 | background-image: linear-gradient(top, $color1 0%, $color2 $stop); 47 | } 48 | 49 | @mixin clearfix() { 50 | *zoom: 1; 51 | 52 | &:before, 53 | &:after { 54 | display: table; 55 | content: ''; 56 | } 57 | 58 | &:after { 59 | clear: both; 60 | } 61 | } 62 | 63 | @mixin readable_textareas() { 64 | border: 1px solid #d2d2d2; 65 | padding: 4px 5px; 66 | font-size: 13px; 67 | line-height: 19px; 68 | @include box_shadow_inset(rgba(0, 0, 0, .1), 0, 1px, 1px, transparent, 0, 0, 0); 69 | @include linear_gradient(#fdfdfd, #fff, 1px); 70 | @include border_radius(3px, 3px); 71 | -moz-box-sizing: border-box; 72 | -webkit-box-sizing: border-box; 73 | box-sizing: border-box; 74 | } 75 | 76 | 77 | @mixin transition($property: opacity, $durration: .3s, $animation: ease-in-out) { 78 | 79 | -webkit-transition: $property $durration $animation; 80 | -moz-transition: $property $durration $animation; 81 | -ms-transition: $property $durration $animation; 82 | -o-transition: $property $durration $animation; 83 | transition: $property $durration $animation; 84 | } 85 | -------------------------------------------------------------------------------- /pau/static/css/mixin/media_block.scss: -------------------------------------------------------------------------------- 1 | // 2 | // # Media Blocks 3 | // 4 | // A Media Block is used when you need a fixed width div, mixed with a flexible div. 5 | // 6 | // For example, a 57px width box for a profile image and content box that will 7 | // take up 100% of the remaining content. 8 | // 9 | // visual layout 10 | // +------------------------- media block -------------------------+ 11 | // |+-------- media -----++------------- content -----------------+| 12 | // || || || 13 | // || || || 14 | // || || || 15 | // |<- $left_col_width -> | 16 | // 17 | // HTML layout 18 | //
    // could be other containers that use @media-block, eg. .user-container 19 | //
    20 | // 21 | // * put the .media element first, as a convention 22 | //
    23 | // 24 | //
    25 | // 26 | //

    Some awesome content, eg. a status update

    27 | //
    28 | // 29 | //
    30 | 31 | @mixin media-block($left_col_width, $left: 0, $top: 0) { 32 | 33 | position: relative; 34 | 35 | & > .content { 36 | margin-left: $left_col_width; 37 | 38 | & > .media { 39 | position: absolute; 40 | left: $left; 41 | top: $top; 42 | } 43 | } 44 | 45 | } 46 | 47 | @mixin reversed-media-block($right_col_width, $right: 0, $top: 0) { 48 | 49 | position: relative; 50 | 51 | .content { 52 | margin-right: $right_col_width; 53 | 54 | .media { 55 | position: absolute; 56 | right: $right; 57 | top: $top; 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /pau/static/css/mixin/textarea.scss: -------------------------------------------------------------------------------- 1 | @mixin textarea() { 2 | height: 80px; 3 | resize: none; 4 | overflow: hidden; 5 | margin: 0; 6 | padding: 10px 10px; 7 | 8 | // for autogrow to transition nicely 9 | @include transition(#{border-color .2s linear, height 50ms ease-in}); 10 | } 11 | -------------------------------------------------------------------------------- /pau/static/css/nav.scss: -------------------------------------------------------------------------------- 1 | .alpha-nav { 2 | html.ie.v9_0 & { 3 | background-image: none; 4 | filter: none; 5 | } 6 | 7 | .nav { 8 | display: block; 9 | width: 100%; 10 | 11 | li a { 12 | line-height: 20px; 13 | 14 | i { 15 | line-height: normal; 16 | } 17 | } 18 | } 19 | 20 | .dropdown .icon-only { 21 | display: none; 22 | } 23 | 24 | html.breakpoint-phone & { 25 | 26 | .branding { 27 | display: block; 28 | width: 30px; 29 | } 30 | 31 | .dropdown .icon-only { 32 | display: inline-block; 33 | } 34 | 35 | .dropdown .username { 36 | display: none; 37 | } 38 | } 39 | } 40 | 41 | .navbar-inner .nav .dropdown-menu { 42 | padding: 10px 0; 43 | background-color: rgba(255, 255, 255, .95); 44 | border: 0; 45 | 46 | &::after { 47 | border-bottom: 6px solid rgba(255, 255, 255, .95); 48 | } 49 | 50 | li { 51 | 52 | &.first { 53 | margin-top: 5px; 54 | padding-top: 3px; 55 | border-top: 1px solid darken($themeAccent, 10%); 56 | } 57 | 58 | a { 59 | font-size: 13px; 60 | padding-top: 2px; 61 | padding-bottom: 2px; 62 | @include box-shadow(none); 63 | 64 | html.breakpoint-phone & { 65 | font-size: 11px; 66 | padding-top: 1px; 67 | padding-bottom: 1px; 68 | } 69 | } 70 | } 71 | 72 | li.secondary-links { 73 | display: none; 74 | 75 | html.breakpoint-phone & { 76 | display: block; 77 | } 78 | } 79 | } 80 | 81 | .navbar { 82 | margin: 0 auto 0 auto; 83 | 84 | .navbar-inner { 85 | position: relative; 86 | padding: 0; 87 | @include box-shadow(none); 88 | background: none; 89 | filter: none; 90 | 91 | .nav { 92 | 93 | margin: 0; 94 | } 95 | 96 | .brand, 97 | .nav > li > a { 98 | padding-top: 11px; 99 | font-weight: bold; 100 | } 101 | 102 | .hide-textindent { 103 | width: 0; 104 | height: 0; 105 | } 106 | } 107 | } 108 | 109 | .navbar { 110 | 111 | .nav li { 112 | 113 | a.brand, 114 | &.about-dropdown-menu a { 115 | @include box-shadow(none); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /pau/static/css/pau.scss: -------------------------------------------------------------------------------- 1 | @import 'adn_base_variables.scss'; 2 | @import 'bootstrap/_mixins.scss'; 3 | @import 'mixin/textarea.scss'; 4 | @import 'mixin/media_block.scss'; 5 | 6 | // CSS Reset 7 | @import 'bootstrap/_reset.scss'; 8 | 9 | // Grid system and page structure 10 | @import 'bootstrap/_scaffolding.scss'; 11 | @import 'screen/grids.scss'; 12 | 13 | @import 'bootstrap/_layouts.scss'; 14 | 15 | // Base CSS 16 | @import 'bootstrap/_type.scss'; 17 | @import 'bootstrap/_code.scss'; 18 | @import 'bootstrap/_forms.scss'; 19 | @import 'bootstrap/_tables.scss'; 20 | 21 | // Components: common 22 | @import 'bootstrap/_dropdowns.scss'; 23 | @import 'bootstrap/_wells.scss'; 24 | @import 'bootstrap/_component-animations.scss'; 25 | @import 'bootstrap/_close.scss'; 26 | @import 'bootstrap/_labels-badges.scss'; 27 | 28 | // Components: Buttons & Alerts 29 | @import 'bold_buttons.scss'; 30 | @import 'bootstrap/_button-groups.scss'; 31 | @import 'bootstrap/_alerts.scss'; 32 | 33 | // Components: Nav 34 | @import 'bootstrap/_navs.scss'; 35 | @import 'bootstrap/_navbar.scss'; 36 | @import 'bootstrap/_breadcrumbs.scss'; 37 | 38 | // Components: Popovers 39 | @import 'bootstrap/_modals.scss'; 40 | @import 'bootstrap/_tooltip.scss'; 41 | @import 'bootstrap/_popovers.scss'; 42 | 43 | // Bootstrap responsive stack 44 | @import 'bootstrap/_responsive-767px-max.scss'; 45 | 46 | // font-awesome icons 47 | @import 'font-awesome.scss'; 48 | 49 | @import 'adn_base.scss'; 50 | @import 'feed.scss'; 51 | 52 | @import 'message_create.scss'; 53 | @import 'file_uploads.scss'; 54 | 55 | // Must be after our code 56 | @import 'bootstrap/_utilities.scss'; 57 | 58 | -------------------------------------------------------------------------------- /pau/static/images/alpha-index-image-chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-image-chat.png -------------------------------------------------------------------------------- /pau/static/images/alpha-index-image-chat@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-image-chat@2x.png -------------------------------------------------------------------------------- /pau/static/images/alpha-index-image-photos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-image-photos.png -------------------------------------------------------------------------------- /pau/static/images/alpha-index-image-photos@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-image-photos@2x.png -------------------------------------------------------------------------------- /pau/static/images/alpha-index-image-topics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-image-topics.png -------------------------------------------------------------------------------- /pau/static/images/alpha-index-image-topics@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-image-topics@2x.png -------------------------------------------------------------------------------- /pau/static/images/alpha-index-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-logo.png -------------------------------------------------------------------------------- /pau/static/images/alpha-index-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-index-logo@2x.png -------------------------------------------------------------------------------- /pau/static/images/alpha-logo-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-logo-light.png -------------------------------------------------------------------------------- /pau/static/images/alpha-logo-light@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-logo-light@2x.png -------------------------------------------------------------------------------- /pau/static/images/alpha-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-logo.png -------------------------------------------------------------------------------- /pau/static/images/alpha-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/alpha-logo@2x.png -------------------------------------------------------------------------------- /pau/static/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/apple-touch-icon.png -------------------------------------------------------------------------------- /pau/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/favicon.ico -------------------------------------------------------------------------------- /pau/static/images/no-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/no-icon.png -------------------------------------------------------------------------------- /pau/static/images/no-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/no-icon@2x.png -------------------------------------------------------------------------------- /pau/static/images/post-container-divider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/post-container-divider.png -------------------------------------------------------------------------------- /pau/static/images/sample_cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/sample_cover.jpg -------------------------------------------------------------------------------- /pau/static/images/sample_cover@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/sample_cover@2x.jpg -------------------------------------------------------------------------------- /pau/static/images/social-buttons-chat-bubbles-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/social-buttons-chat-bubbles-bg.jpg -------------------------------------------------------------------------------- /pau/static/images/social-buttons-chat-bubbles-bg@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/social-buttons-chat-bubbles-bg@2x.jpg -------------------------------------------------------------------------------- /pau/static/images/social-buttons-chat-bubbles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/social-buttons-chat-bubbles.png -------------------------------------------------------------------------------- /pau/static/images/social-buttons-chat-bubbles@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/static/images/social-buttons-chat-bubbles@2x.png -------------------------------------------------------------------------------- /pau/static/js/core/csrf.js: -------------------------------------------------------------------------------- 1 | $(document).ajaxSend(function (event, xhr, settings) { 2 | function getCookie(name) { 3 | var cookieValue = null; 4 | if (document.cookie && document.cookie !== '') { 5 | var cookies = document.cookie.split(';'); 6 | for (var i = 0; i < cookies.length; i++) { 7 | var cookie = jQuery.trim(cookies[i]); 8 | // Does this cookie string begin with the name we want? 9 | if (cookie.substring(0, name.length + 1) === (name + '=')) { 10 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 11 | break; 12 | } 13 | } 14 | } 15 | return cookieValue; 16 | } 17 | 18 | function sameOrigin(url) { 19 | // url could be relative or scheme relative or absolute 20 | var host = document.location.host; // host + port 21 | var protocol = document.location.protocol; 22 | var sr_origin = '//' + host; 23 | var origin = protocol + sr_origin; 24 | // Allow absolute or scheme relative URLs to same origin 25 | return (url === origin || url.slice(0, origin.length + 1) === origin + '/') || (url === sr_origin || url.slice(0, sr_origin.length + 1) === sr_origin + '/') || 26 | // or any other URL that isn't scheme relative or absolute i.e relative. 27 | !(/^(\/\/|http:|https:)/.test(url)); 28 | } 29 | 30 | function safeMethod(method) { 31 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 32 | } 33 | 34 | if (!safeMethod(settings.type) && sameOrigin(settings.url)) { 35 | xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /pau/static/js/core/feature_detection.js: -------------------------------------------------------------------------------- 1 | /*global Modernizr:true */ 2 | (function () { 3 | var doc_element = document.documentElement; 4 | 5 | var queries = window.TAPP_MEDIA_QUERIES = { 6 | phone: "(max-width: 700px)", 7 | tablet: "(min-width: 701px) and (max-width: 1151px)", 8 | widescreen: "(min-width: 1152px)", 9 | retina: "only screen and (-webkit-min-device-pixel-ratio : 2)", 10 | height_large: "(min-height: 1200px)", 11 | height_medium: "(min-height: 950px) and (max-height: 1199px)", 12 | height_small: "(min-height: 600px) and (max-height: 949px)", 13 | height_mini: "(max-height: 599px)", 14 | height_extra_mini: "(max-height: 320px)" 15 | }; 16 | 17 | var makeTest = function (query) { 18 | return function () { 19 | return Modernizr.mq(query); 20 | }; 21 | }; 22 | 23 | // remove the default state 24 | doc_element.className = doc_element.className.replace('breakpoint-phone', ''); 25 | Modernizr.test_media_queries = function () { 26 | for (var name in queries) { 27 | Modernizr.addTest('breakpoint-' + name, makeTest(queries[name])); 28 | } 29 | }; 30 | 31 | Modernizr.test_media_queries(); 32 | 33 | // from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/checked.js 34 | Modernizr.test_checked_selector = function () { 35 | Modernizr.addTest('checked', function () { 36 | return Modernizr.testStyles('#modernizr input {width:100px} #modernizr :checked {width:200px;display:block}', function (elem, rule) { 37 | var cb = document.createElement('input'); 38 | cb.setAttribute("type", "checkbox"); 39 | cb.setAttribute("checked", "checked"); 40 | elem.appendChild(cb); 41 | return cb.offsetWidth === 200; 42 | }); 43 | }); 44 | }; 45 | 46 | Modernizr.test_checked_selector(); 47 | 48 | }()); -------------------------------------------------------------------------------- /pau/static/js/core/module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TAPP.module is the base class 3 | * var TestModule = Module.extend({ 4 | * sweet: function(){ 5 | * return 'awesome'; 6 | * } 7 | * }); 8 | * 9 | * var tester_instance = new TestModule(); 10 | * 11 | * tester_instance.register_page_load_hook('testy', tester_instance.sweet); 12 | * $.publish('testy'); # 'awesome' 13 | */ 14 | 15 | (function () { 16 | var TRUE = true, 17 | FALSE = !TRUE, 18 | ArrayProto = Array.prototype, 19 | ObjProto = Object.prototype, 20 | FuncProto = Function.prototype, 21 | slice = ArrayProto.slice, 22 | // Shared empty constructor function to aid in prototype-chain creation. 23 | Ctor = function () {}, 24 | extender = function (obj) { 25 | $.each(slice.call(arguments, 1), function (i, source) { 26 | var prop; 27 | for (prop in source) { 28 | if (source[prop] !== void 0) { 29 | obj[prop] = source[prop]; 30 | } 31 | } 32 | }); 33 | return obj; 34 | }, 35 | inherits = function (parent, protoProps, staticProps) { 36 | var child; 37 | 38 | // The constructor function for the new subclass is either defined by you 39 | // (the "constructor" property in your `extend` definition), or defaulted 40 | // by us to simply call `super()`. 41 | if (protoProps && protoProps.hasOwnProperty('constructor')) { 42 | child = protoProps.constructor; 43 | } else { 44 | child = function () { 45 | return parent.apply(this, arguments); 46 | }; 47 | } 48 | 49 | // Inherit class (static) properties from parent. 50 | extender(child, parent); 51 | 52 | // Set the prototype chain to inherit from `parent`, without calling 53 | // `parent`'s constructor function. 54 | Ctor.prototype = parent.prototype; 55 | child.prototype = new Ctor(); 56 | 57 | // Add prototype properties (instance properties) to the subclass, 58 | // if supplied. 59 | if (protoProps) { 60 | extender(child.prototype, protoProps); 61 | } 62 | 63 | // Add static properties to the constructor function, if supplied. 64 | if (staticProps) { 65 | extender(child, staticProps); 66 | } 67 | 68 | // Correctly set child's `prototype.constructor`. 69 | child.prototype.constructor = child; 70 | 71 | // Set a convenience property in case the parent's prototype is needed later. 72 | child.__super__ = parent.prototype; 73 | 74 | return child; 75 | }, 76 | // The self-propagating extend function that Backbone classes use. 77 | extend = function (protoProps, classProps) { 78 | var child = inherits(this, protoProps, classProps); 79 | child.extend = this.extend; 80 | return child; 81 | }, 82 | // Just a shell 83 | Klass = function () {}; 84 | 85 | // Give Module the ability to be extended 86 | Klass.extend = extend; 87 | 88 | // So we can make classes that aren't modules 89 | TAPP.Klass = Klass; 90 | 91 | }()); 92 | -------------------------------------------------------------------------------- /pau/static/js/deps/bootstrap/README: -------------------------------------------------------------------------------- 1 | https://github.com/twitter/bootstrap 2 | gitosis@git.int.sfo01.mml:thirdparty/bootstrap.git 3 | b261f9781bbf31f499cb55c49451dc0c0ad43062 4 | 5 | NB: this one file - bootstrap-typeahead.js - was dragged in by mstorus from: 6 | c52368d3c5984b28e6a71e5e1240afdd788fc2e6 7 | we should upgrade all of bootstrap eventually 8 | -------------------------------------------------------------------------------- /pau/static/js/deps/bootstrap/bootstrap-collapse.js: -------------------------------------------------------------------------------- 1 | /* ============================================================= 2 | * bootstrap-collapse.js v2.0.3 3 | * http://twitter.github.com/bootstrap/javascript.html#collapse 4 | * ============================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* COLLAPSE PUBLIC CLASS DEFINITION 27 | * ================================ */ 28 | 29 | var Collapse = function (element, options) { 30 | this.$element = $(element) 31 | this.options = $.extend({}, $.fn.collapse.defaults, options) 32 | 33 | if (this.options.parent) { 34 | this.$parent = $(this.options.parent) 35 | } 36 | 37 | this.options.toggle && this.toggle() 38 | } 39 | 40 | Collapse.prototype = { 41 | 42 | constructor: Collapse 43 | 44 | , dimension: function () { 45 | var hasWidth = this.$element.hasClass('width') 46 | return hasWidth ? 'width' : 'height' 47 | } 48 | 49 | , show: function () { 50 | var dimension 51 | , scroll 52 | , actives 53 | , hasData 54 | 55 | if (this.transitioning) return 56 | 57 | dimension = this.dimension() 58 | scroll = $.camelCase(['scroll', dimension].join('-')) 59 | actives = this.$parent && this.$parent.find('> .accordion-group > .in') 60 | 61 | if (actives && actives.length) { 62 | hasData = actives.data('collapse') 63 | if (hasData && hasData.transitioning) return 64 | actives.collapse('hide') 65 | hasData || actives.data('collapse', null) 66 | } 67 | 68 | this.$element[dimension](0) 69 | this.transition('addClass', $.Event('show'), 'shown') 70 | this.$element[dimension](this.$element[0][scroll]) 71 | } 72 | 73 | , hide: function () { 74 | var dimension 75 | if (this.transitioning) return 76 | dimension = this.dimension() 77 | this.reset(this.$element[dimension]()) 78 | this.transition('removeClass', $.Event('hide'), 'hidden') 79 | this.$element[dimension](0) 80 | } 81 | 82 | , reset: function (size) { 83 | var dimension = this.dimension() 84 | 85 | this.$element 86 | .removeClass('collapse') 87 | [dimension](size || 'auto') 88 | [0].offsetWidth 89 | 90 | this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') 91 | 92 | return this 93 | } 94 | 95 | , transition: function (method, startEvent, completeEvent) { 96 | var that = this 97 | , complete = function () { 98 | if (startEvent.type == 'show') that.reset() 99 | that.transitioning = 0 100 | that.$element.trigger(completeEvent) 101 | } 102 | 103 | this.$element.trigger(startEvent) 104 | 105 | if (startEvent.isDefaultPrevented()) return 106 | 107 | this.transitioning = 1 108 | 109 | this.$element[method]('in') 110 | 111 | $.support.transition && this.$element.hasClass('collapse') ? 112 | this.$element.one($.support.transition.end, complete) : 113 | complete() 114 | } 115 | 116 | , toggle: function () { 117 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 118 | } 119 | 120 | } 121 | 122 | 123 | /* COLLAPSIBLE PLUGIN DEFINITION 124 | * ============================== */ 125 | 126 | $.fn.collapse = function (option) { 127 | return this.each(function () { 128 | var $this = $(this) 129 | , data = $this.data('collapse') 130 | , options = typeof option == 'object' && option 131 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 132 | if (typeof option == 'string') data[option]() 133 | }) 134 | } 135 | 136 | $.fn.collapse.defaults = { 137 | toggle: true 138 | } 139 | 140 | $.fn.collapse.Constructor = Collapse 141 | 142 | 143 | /* COLLAPSIBLE DATA-API 144 | * ==================== */ 145 | 146 | $(function () { 147 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { 148 | var $this = $(this), href 149 | , target = $this.attr('data-target') 150 | || e.preventDefault() 151 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 152 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 153 | $(target).collapse(option) 154 | }) 155 | }) 156 | 157 | }(window.jQuery); -------------------------------------------------------------------------------- /pau/static/js/deps/bootstrap/bootstrap-dropdown.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-dropdown.js v2.0.3 3 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* DROPDOWN CLASS DEFINITION 27 | * ========================= */ 28 | 29 | var toggle = '[data-toggle="dropdown"]' 30 | , Dropdown = function (element) { 31 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 32 | $('html').on('click.dropdown.data-api', function () { 33 | $el.parent().removeClass('open') 34 | }) 35 | } 36 | 37 | Dropdown.prototype = { 38 | 39 | constructor: Dropdown 40 | 41 | , toggle: function (e) { 42 | var $this = $(this) 43 | , $parent 44 | , selector 45 | , isActive 46 | 47 | if ($this.is('.disabled, :disabled')) return 48 | 49 | selector = $this.attr('data-target') 50 | 51 | if (!selector) { 52 | selector = $this.attr('href') 53 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 54 | } 55 | 56 | $parent = $(selector) 57 | $parent.length || ($parent = $this.parent()) 58 | 59 | isActive = $parent.hasClass('open') 60 | 61 | clearMenus() 62 | 63 | if (!isActive) $parent.toggleClass('open') 64 | 65 | return false 66 | } 67 | 68 | } 69 | 70 | function clearMenus() { 71 | $(toggle).parent().removeClass('open') 72 | } 73 | 74 | 75 | /* DROPDOWN PLUGIN DEFINITION 76 | * ========================== */ 77 | 78 | $.fn.dropdown = function (option) { 79 | return this.each(function () { 80 | var $this = $(this) 81 | , data = $this.data('dropdown') 82 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 83 | if (typeof option == 'string') data[option].call($this) 84 | }) 85 | } 86 | 87 | $.fn.dropdown.Constructor = Dropdown 88 | 89 | 90 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 91 | * =================================== */ 92 | 93 | $(function () { 94 | $('html').on('click.dropdown.data-api', clearMenus) 95 | $('body') 96 | .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() }) 97 | .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) 98 | }) 99 | 100 | }(window.jQuery); -------------------------------------------------------------------------------- /pau/static/js/deps/bootstrap/bootstrap-popover.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * bootstrap-popover.js v2.2.2 3 | * http://twitter.github.com/bootstrap/javascript.html#popovers 4 | * =========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * =========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* POPOVER PUBLIC CLASS DEFINITION 27 | * =============================== */ 28 | 29 | var Popover = function (element, options) { 30 | this.init('popover', element, options) 31 | } 32 | 33 | 34 | /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js 35 | ========================================== */ 36 | 37 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { 38 | 39 | constructor: Popover 40 | 41 | , setContent: function () { 42 | var $tip = this.tip() 43 | , title = this.getTitle() 44 | , content = this.getContent() 45 | 46 | $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) 47 | $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content) 48 | 49 | $tip.removeClass('fade top bottom left right in') 50 | } 51 | 52 | , hasContent: function () { 53 | return this.getTitle() || this.getContent() 54 | } 55 | 56 | , getContent: function () { 57 | var content 58 | , $e = this.$element 59 | , o = this.options 60 | 61 | content = $e.attr('data-content') 62 | || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) 63 | 64 | return content 65 | } 66 | 67 | , tip: function () { 68 | if (!this.$tip) { 69 | this.$tip = $(this.options.template) 70 | } 71 | return this.$tip 72 | } 73 | 74 | , destroy: function () { 75 | this.hide(); 76 | this.$element.off('.' + this.type).removeData(this.type); 77 | } 78 | 79 | }) 80 | 81 | 82 | /* POPOVER PLUGIN DEFINITION 83 | * ======================= */ 84 | 85 | var old = $.fn.popover 86 | 87 | $.fn.popover = function (option) { 88 | return this.each(function () { 89 | var $this = $(this) 90 | , data = $this.data('popover') 91 | , options = typeof option == 'object' && option 92 | if (!data) $this.data('popover', (data = new Popover(this, options))) 93 | if (typeof option == 'string') data[option]() 94 | }) 95 | } 96 | 97 | $.fn.popover.Constructor = Popover 98 | 99 | $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { 100 | placement: 'right' 101 | , trigger: 'click' 102 | , content: '' 103 | , template: '

    ' 104 | }) 105 | 106 | 107 | /* POPOVER NO CONFLICT 108 | * =================== */ 109 | 110 | $.fn.popover.noConflict = function () { 111 | $.fn.popover = old 112 | return this 113 | } 114 | 115 | }(window.jQuery); -------------------------------------------------------------------------------- /pau/static/js/deps/bootstrap/bootstrap-tab.js: -------------------------------------------------------------------------------- 1 | /* ======================================================== 2 | * bootstrap-tab.js v2.0.3 3 | * http://twitter.github.com/bootstrap/javascript.html#tabs 4 | * ======================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ======================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* TAB CLASS DEFINITION 27 | * ==================== */ 28 | 29 | var Tab = function ( element ) { 30 | this.element = $(element) 31 | } 32 | 33 | Tab.prototype = { 34 | 35 | constructor: Tab 36 | 37 | , show: function () { 38 | var $this = this.element 39 | , $ul = $this.closest('ul:not(.dropdown-menu)') 40 | , selector = $this.attr('data-target') 41 | , previous 42 | , $target 43 | , e 44 | 45 | if (!selector) { 46 | selector = $this.attr('href') 47 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 48 | } 49 | 50 | if ( $this.parent('li').hasClass('active') ) return 51 | 52 | previous = $ul.find('.active a').last()[0] 53 | 54 | e = $.Event('show', { 55 | relatedTarget: previous 56 | }) 57 | 58 | $this.trigger(e) 59 | 60 | if (e.isDefaultPrevented()) return 61 | 62 | $target = $(selector) 63 | 64 | this.activate($this.parent('li'), $ul) 65 | this.activate($target, $target.parent(), function () { 66 | $this.trigger({ 67 | type: 'shown' 68 | , relatedTarget: previous 69 | }) 70 | }) 71 | } 72 | 73 | , activate: function ( element, container, callback) { 74 | var $active = container.find('> .active') 75 | , transition = callback 76 | && $.support.transition 77 | && $active.hasClass('fade') 78 | 79 | function next() { 80 | $active 81 | .removeClass('active') 82 | .find('> .dropdown-menu > .active') 83 | .removeClass('active') 84 | 85 | element.addClass('active') 86 | 87 | if (transition) { 88 | element[0].offsetWidth // reflow for transition 89 | element.addClass('in') 90 | } else { 91 | element.removeClass('fade') 92 | } 93 | 94 | if ( element.parent('.dropdown-menu') ) { 95 | element.closest('li.dropdown').addClass('active') 96 | } 97 | 98 | callback && callback() 99 | } 100 | 101 | transition ? 102 | $active.one($.support.transition.end, next) : 103 | next() 104 | 105 | $active.removeClass('in') 106 | } 107 | } 108 | 109 | 110 | /* TAB PLUGIN DEFINITION 111 | * ===================== */ 112 | 113 | $.fn.tab = function ( option ) { 114 | return this.each(function () { 115 | var $this = $(this) 116 | , data = $this.data('tab') 117 | if (!data) $this.data('tab', (data = new Tab(this))) 118 | if (typeof option == 'string') data[option]() 119 | }) 120 | } 121 | 122 | $.fn.tab.Constructor = Tab 123 | 124 | 125 | /* TAB DATA-API 126 | * ============ */ 127 | 128 | $(function () { 129 | $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { 130 | e.preventDefault() 131 | $(this).tab('show') 132 | }) 133 | }) 134 | 135 | }(window.jQuery); -------------------------------------------------------------------------------- /pau/static/js/deps/bootstrap/bootstrap-transition.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.0.3 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | $(function () { 24 | 25 | "use strict"; // jshint ;_; 26 | 27 | 28 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 29 | * ======================================================= */ 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd' 40 | , 'msTransition' : 'MSTransitionEnd' 41 | , 'transition' : 'transitionend' 42 | } 43 | , name 44 | 45 | for (name in transEndEventNames){ 46 | if (el.style[name] !== undefined) { 47 | return transEndEventNames[name] 48 | } 49 | } 50 | 51 | }()) 52 | 53 | return transitionEnd && { 54 | end: transitionEnd 55 | } 56 | 57 | })() 58 | 59 | }) 60 | 61 | }(window.jQuery); -------------------------------------------------------------------------------- /pau/static/js/deps/cookie/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cookie plugin 3 | * 4 | * Copyright (c) 2006 Klaus Hartl (stilbuero.de) 5 | * Dual licensed under the MIT and GPL licenses: 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * http://www.gnu.org/licenses/gpl.html 8 | * 9 | */ 10 | 11 | /** 12 | * Create a cookie with the given name and value and other optional parameters. 13 | * 14 | * @example $.cookie('the_cookie', 'the_value'); 15 | * @desc Set the value of a cookie. 16 | * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); 17 | * @desc Create a cookie with all available options. 18 | * @example $.cookie('the_cookie', 'the_value'); 19 | * @desc Create a session cookie. 20 | * @example $.cookie('the_cookie', null); 21 | * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain 22 | * used when the cookie was set. 23 | * 24 | * @param String name The name of the cookie. 25 | * @param String value The value of the cookie. 26 | * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. 27 | * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. 28 | * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. 29 | * If set to null or omitted, the cookie will be a session cookie and will not be retained 30 | * when the the browser exits. 31 | * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). 32 | * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). 33 | * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will 34 | * require a secure protocol (like HTTPS). 35 | * @type undefined 36 | * 37 | * @name $.cookie 38 | * @cat Plugins/Cookie 39 | * @author Klaus Hartl/klaus.hartl@stilbuero.de 40 | */ 41 | 42 | /** 43 | * Get the value of a cookie with the given name. 44 | * 45 | * @example $.cookie('the_cookie'); 46 | * @desc Get the value of a cookie. 47 | * 48 | * @param String name The name of the cookie. 49 | * @return The value of the cookie. 50 | * @type String 51 | * 52 | * @name $.cookie 53 | * @cat Plugins/Cookie 54 | * @author Klaus Hartl/klaus.hartl@stilbuero.de 55 | */ 56 | jQuery.cookie = function(name, value, options) { 57 | if (typeof value != 'undefined') { // name and value given, set cookie 58 | options = options || {}; 59 | if (value === null) { 60 | value = ''; 61 | options.expires = -1; 62 | } 63 | var expires = ''; 64 | if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { 65 | var date; 66 | if (typeof options.expires == 'number') { 67 | date = new Date(); 68 | date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); 69 | } else { 70 | date = options.expires; 71 | } 72 | expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE 73 | } 74 | // CAUTION: Needed to parenthesize options.path and options.domain 75 | // in the following expressions, otherwise they evaluate to undefined 76 | // in the packed version for some reason... 77 | var path = options.path ? '; path=' + (options.path) : ''; 78 | var domain = options.domain ? '; domain=' + (options.domain) : ''; 79 | var secure = options.secure ? '; secure' : ''; 80 | document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); 81 | } else { // only name given, get cookie 82 | var cookieValue = null; 83 | if (document.cookie && document.cookie != '') { 84 | var cookies = document.cookie.split(';'); 85 | for (var i = 0; i < cookies.length; i++) { 86 | var cookie = jQuery.trim(cookies[i]); 87 | // Does this cookie string begin with the name we want? 88 | if (cookie.substring(0, name.length + 1) == (name + '=')) { 89 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 90 | break; 91 | } 92 | } 93 | } 94 | return cookieValue; 95 | } 96 | }; -------------------------------------------------------------------------------- /pau/static/js/deps/imagesloaded/README: -------------------------------------------------------------------------------- 1 | https://github.com/desandro/imagesloaded 2 | 3 | eb363e0db53da02fc180323ba8fe8a320937221e 4 | 5 | v 2.1.1 -------------------------------------------------------------------------------- /pau/static/js/deps/imagesloaded/jquery.imagesloaded.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery imagesLoaded plugin v2.1.1 3 | * http://github.com/desandro/imagesloaded 4 | * 5 | * MIT License. by Paul Irish et al. 6 | */ 7 | 8 | /*jshint curly: true, eqeqeq: true, noempty: true, strict: true, undef: true, browser: true */ 9 | /*global jQuery: false */ 10 | 11 | ;(function($, undefined) { 12 | 'use strict'; 13 | 14 | // blank image data-uri bypasses webkit log warning (thx doug jones) 15 | var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='; 16 | 17 | $.fn.imagesLoaded = function( callback ) { 18 | var $this = this, 19 | deferred = $.isFunction($.Deferred) ? $.Deferred() : 0, 20 | hasNotify = $.isFunction(deferred.notify), 21 | $images = $this.find('img').add( $this.filter('img') ), 22 | loaded = [], 23 | proper = [], 24 | broken = []; 25 | 26 | // Register deferred callbacks 27 | if ($.isPlainObject(callback)) { 28 | $.each(callback, function (key, value) { 29 | if (key === 'callback') { 30 | callback = value; 31 | } else if (deferred) { 32 | deferred[key](value); 33 | } 34 | }); 35 | } 36 | 37 | function doneLoading() { 38 | var $proper = $(proper), 39 | $broken = $(broken); 40 | 41 | if ( deferred ) { 42 | if ( broken.length ) { 43 | deferred.reject( $images, $proper, $broken ); 44 | } else { 45 | deferred.resolve( $images ); 46 | } 47 | } 48 | 49 | if ( $.isFunction( callback ) ) { 50 | callback.call( $this, $images, $proper, $broken ); 51 | } 52 | } 53 | 54 | function imgLoadedHandler( event ) { 55 | imgLoaded( event.target, event.type === 'error' ); 56 | } 57 | 58 | function imgLoaded( img, isBroken ) { 59 | // don't proceed if BLANK image, or image is already loaded 60 | if ( img.src === BLANK || $.inArray( img, loaded ) !== -1 ) { 61 | return; 62 | } 63 | 64 | // store element in loaded images array 65 | loaded.push( img ); 66 | 67 | // keep track of broken and properly loaded images 68 | if ( isBroken ) { 69 | broken.push( img ); 70 | } else { 71 | proper.push( img ); 72 | } 73 | 74 | // cache image and its state for future calls 75 | $.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } ); 76 | 77 | // trigger deferred progress method if present 78 | if ( hasNotify ) { 79 | deferred.notifyWith( $(img), [ isBroken, $images, $(proper), $(broken) ] ); 80 | } 81 | 82 | // call doneLoading and clean listeners if all images are loaded 83 | if ( $images.length === loaded.length ) { 84 | setTimeout( doneLoading ); 85 | $images.unbind( '.imagesLoaded', imgLoadedHandler ); 86 | } 87 | } 88 | 89 | // if no images, trigger immediately 90 | if ( !$images.length ) { 91 | doneLoading(); 92 | } else { 93 | $images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoadedHandler ) 94 | .each( function( i, el ) { 95 | var src = el.src; 96 | 97 | // find out if this image has been already checked for status 98 | // if it was, and src has not changed, call imgLoaded on it 99 | var cached = $.data( el, 'imagesLoaded' ); 100 | if ( cached && cached.src === src ) { 101 | imgLoaded( el, cached.isBroken ); 102 | return; 103 | } 104 | 105 | // if complete is true and browser supports natural sizes, try 106 | // to check for image status manually 107 | if ( el.complete && el.naturalWidth !== undefined ) { 108 | imgLoaded( el, el.naturalWidth === 0 || el.naturalHeight === 0 ); 109 | return; 110 | } 111 | 112 | // cached images don't fire load sometimes, so we reset src, but only when 113 | // dealing with IE, or image is complete (loaded) and failed manual check 114 | // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f 115 | if ( el.readyState || el.complete ) { 116 | el.src = BLANK; 117 | el.src = src; 118 | } 119 | }); 120 | } 121 | 122 | return deferred ? deferred.promise( $this ) : $this; 123 | }; 124 | 125 | })(jQuery); -------------------------------------------------------------------------------- /pau/static/js/deps/modernizr/README: -------------------------------------------------------------------------------- 1 | modernizr.js - v2.5.3 2 | https://github.com/Modernizr/Modernizr/blob/master/modernizr.js - commit 4705f9766c078a7baebc2434f10343594085b699 -------------------------------------------------------------------------------- /pau/static/js/deps/pjax/README: -------------------------------------------------------------------------------- 1 | from http://git.int.sfo01.mml/?p=thirdparty/jquery-pjax.git;a=summary 2 | commit f2cd4485f2125fe06b380aa13f8206faabc82cf0 3 | -------------------------------------------------------------------------------- /pau/static/js/deps/placeholder/jquery.placeholder.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * HTML5 Placeholder jQuery Plugin v1.8.2 3 | * @link http://github.com/mathiasbynens/Placeholder-jQuery-Plugin 4 | * @author Mathias Bynens 5 | */ 6 | (function($) { 7 | 8 | var isInputSupported = 'placeholder' in document.createElement('input'), 9 | isTextareaSupported = 'placeholder' in document.createElement('textarea'); 10 | if (isInputSupported && isTextareaSupported) { 11 | $.fn.placeholder = function() { 12 | return this; 13 | }; 14 | $.fn.placeholder.input = $.fn.placeholder.textarea = true; 15 | } else { 16 | $.fn.placeholder = function() { 17 | return this.filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') 18 | .bind('focus.placeholder', clearPlaceholder) 19 | .bind('blur.placeholder', setPlaceholder) 20 | .trigger('blur.placeholder').end(); 21 | }; 22 | $.fn.placeholder.input = isInputSupported; 23 | $.fn.placeholder.textarea = isTextareaSupported; 24 | } 25 | 26 | function args(elem) { 27 | // Return an object of element attributes 28 | var newAttrs = {}, 29 | rinlinejQuery = /^jQuery\d+$/; 30 | $.each(elem.attributes, function(i, attr) { 31 | if (attr.specified && !rinlinejQuery.test(attr.name)) { 32 | newAttrs[attr.name] = attr.value; 33 | } 34 | }); 35 | return newAttrs; 36 | } 37 | 38 | function clearPlaceholder() { 39 | var $input = $(this); 40 | if ($input.val() === $input.attr('placeholder') && $input.hasClass('placeholder')) { 41 | if ($input.data('placeholder-password')) { 42 | $input.hide().next().attr('id', $input.removeAttr('id').data('placeholder-id')).show().focus(); 43 | } else { 44 | $input.val('').removeClass('placeholder'); 45 | } 46 | } 47 | } 48 | 49 | function setPlaceholder(elem) { 50 | var $replacement, 51 | $input = $(this), 52 | $origInput = $input, 53 | id = this.id; 54 | if ($input.val() === '') { 55 | if ($input.is(':password')) { 56 | if (!$input.data('placeholder-textinput')) { 57 | try { 58 | $replacement = $input.clone().attr({ type: 'text' }); 59 | } catch(e) { 60 | $replacement = $('').attr($.extend(args(this), { type: 'text' })); 61 | } 62 | $replacement 63 | .removeAttr('name') 64 | // We could just use the `.data(obj)` syntax here, but that wouldn’t work in pre-1.4.3 jQueries 65 | .data('placeholder-password', true) 66 | .data('placeholder-id', id) 67 | .bind('focus.placeholder', clearPlaceholder); 68 | $input 69 | .data('placeholder-textinput', $replacement) 70 | .data('placeholder-id', id) 71 | .before($replacement); 72 | } 73 | $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); 74 | } 75 | $input.addClass('placeholder').val($input.attr('placeholder')); 76 | } else { 77 | $input.removeClass('placeholder'); 78 | } 79 | } 80 | 81 | $(function() { 82 | // Look for forms 83 | $('form').bind('submit.placeholder', function() { 84 | // Clear the placeholder values so they don’t get submitted 85 | var $inputs = $('.placeholder', this).each(clearPlaceholder); 86 | setTimeout(function() { 87 | $inputs.each(setPlaceholder); 88 | }, 10); 89 | }); 90 | }); 91 | 92 | // Clear placeholder values upon page reload 93 | $(window).bind('unload.placeholder', function() { 94 | $('.placeholder').val(''); 95 | }); 96 | 97 | }(jQuery)); -------------------------------------------------------------------------------- /pau/static/js/deps/pubsub/pubsub.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | jQuery pub/sub plugin by Peter Higgins (dante@dojotoolkit.org) 4 | 5 | Loosely based on Dojo publish/subscribe API, limited in scope. Rewritten blindly. 6 | 7 | Original is (c) Dojo Foundation 2004-2010. Released under either AFL or new BSD, see: 8 | http://dojofoundation.org/license for more information. 9 | 10 | */ 11 | 12 | (function(d){ 13 | 14 | // the topic/subscription hash 15 | var cache = {}, 16 | empty; 17 | 18 | d.publish = function(/* String */topic, /* Array? */args){ 19 | // summary: 20 | // Publish some data on a named topic. 21 | // topic: String 22 | // The channel to publish on 23 | // args: Array? 24 | // The data to publish. Each array item is converted into an ordered 25 | // arguments on the subscribed functions. 26 | // 27 | // example: 28 | // Publish stuff on '/some/topic'. Anything subscribed will be called 29 | // with a function signature like: function(a,b,c){ ... } 30 | // 31 | // | $.publish("/some/topic", ["a","b","c"]); 32 | empty = cache[topic] && d.each(cache[topic], function(){ 33 | this.apply(d, args || []); 34 | }); 35 | 36 | return false; 37 | }; 38 | 39 | d.subscribe = function(/* String */topic, /* Function */callback){ 40 | // summary: 41 | // Register a callback on a named topic. 42 | // topic: String 43 | // The channel to subscribe to 44 | // callback: Function 45 | // The handler event. Anytime something is $.publish'ed on a 46 | // subscribed channel, the callback will be called with the 47 | // published array as ordered arguments. 48 | // 49 | // returns: Array 50 | // A handle which can be used to unsubscribe this particular subscription. 51 | // 52 | // example: 53 | // | $.subscribe("/some/topic", function(a, b, c){ /* handle data */ }); 54 | // 55 | if(!cache[topic]){ 56 | cache[topic] = []; 57 | } 58 | cache[topic].push(callback); 59 | return [topic, callback]; // Array 60 | }; 61 | 62 | d.unsubscribe = function(/* Array */handle){ 63 | // summary: 64 | // Disconnect a subscribed function for a topic. 65 | // handle: Array 66 | // The return value from a $.subscribe call. 67 | // example: 68 | // | var handle = $.subscribe("/something", function(){}); 69 | // | $.unsubscribe(handle); 70 | 71 | var t = handle[0]; 72 | empty = cache[t] && d.each(cache[t], function(idx){ 73 | if(this == handle[1]){ 74 | cache[t].splice(idx, 1); 75 | } 76 | }); 77 | }; 78 | 79 | })(jQuery); -------------------------------------------------------------------------------- /pau/static/js/deps/twitter-typeahead/README: -------------------------------------------------------------------------------- 1 | from https://github.com/twitter/typeahead.js 2 | 3 | commit: 2bd1119ecdd5ed4bb6b78c83b904d70adc49e023 -------------------------------------------------------------------------------- /pau/static/js/deps/underscore/README: -------------------------------------------------------------------------------- 1 | underscore is at 1.2.3 2 | downloaded from http://documentcloud.github.com/underscore/ -------------------------------------------------------------------------------- /pau/static/js/modules/autocomplete.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var Autocomplete = { 4 | init: function () { 5 | var search_form = $('[data-search-bar-form]'); 6 | var search_bar = search_form.find("input.search-bar-input"); 7 | 8 | if (search_bar.length === 0) { 9 | return; 10 | } 11 | 12 | search_bar.typeahead([{ 13 | name: 'universal', 14 | template: function (datum) { 15 | return datum.html; 16 | }, 17 | remote: { 18 | url: page_context.autocomplete_url + '?q=%QUERY', 19 | filter: function (resp) { 20 | return resp.response; 21 | }, 22 | dataType: 'jsonp' 23 | } 24 | }]); 25 | 26 | search_bar.tapp_on('typeahead:selected', function (e, datum) { 27 | if (datum.search !== undefined) { 28 | $.ajax({ 29 | url: page_context.search_log_url, 30 | dataType: 'jsonp', 31 | data: { 32 | 'search-data': datum.search 33 | } 34 | }); 35 | } 36 | window.setTimeout(function () { 37 | window.location = datum.url; 38 | }, 50); 39 | }); 40 | 41 | search_bar.tapp_on('typeahead:autocomplete', function (e, datum) { 42 | search_form.submit(); 43 | }); 44 | 45 | search_bar.tapp_on('keypress', function (e) { 46 | if (e.which === 13) { 47 | search_form.get(0).submit(); 48 | } 49 | }); 50 | 51 | search_form.find('[type="submit"]').tapp_on('click', function () { 52 | search_form.get(0).submit(); 53 | }); 54 | 55 | } 56 | }; 57 | 58 | TAPP.autocomplete = Autocomplete; 59 | 60 | }()); 61 | -------------------------------------------------------------------------------- /pau/static/js/modules/photo.js: -------------------------------------------------------------------------------- 1 | (function (P) { 2 | TAPP.photo = { 3 | init: function () { 4 | $('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1, maximum-scale=10'); 5 | var center_and_resize = function () { 6 | var image_container = $('.image-container'); 7 | var img = image_container.find('img'); 8 | var img_width = img.attr('data-full-width'); 9 | var img_height = img.attr('data-full-height'); 10 | var max_height = image_container.height(); 11 | var max_width = image_container.width(); 12 | 13 | var dims = TAPP.utils.fit_to_box(img_width, img_height, max_width, max_height); 14 | var width = dims[0]; 15 | var height = dims[1]; 16 | 17 | img.hide(); 18 | img.attr({ 19 | 'width': width, 20 | 'height': height 21 | }); 22 | img.css({ 23 | 'margin-top': -1 * height / 2, 24 | 'margin-left': -1 * width / 2 25 | // top 50% and left 50% set in css 26 | }); 27 | img.show(); 28 | }; 29 | 30 | var img = $('.image-container img'); 31 | var img_loaded = false; 32 | var loader = $('.loader'); 33 | setTimeout(function () { 34 | // add a little delay before showing the spinner so it doesn't pop up all the time 35 | if (!img_loaded) { 36 | loader.show(); 37 | } 38 | }, 250); 39 | 40 | center_and_resize(); 41 | img.imagesLoaded().done(function () { 42 | img_loaded = true; 43 | img.addClass('first-load'); 44 | loader.hide(); 45 | }); 46 | $(window).tapp_on('resize', _.debounce(function () { 47 | center_and_resize(); 48 | }, 50)); 49 | 50 | $('.image-container').tapp_on('click', function () { 51 | $('[data-post-container]').toggle(); 52 | }); 53 | } 54 | }; 55 | }(TAPP)); 56 | -------------------------------------------------------------------------------- /pau/templates-jinja2/429.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 113 | 114 | 119 |
    120 | 121 | 429 122 |
    123 |

    You've made too many requests. Please wait a moment before trying again.

    124 |

    If this error persists, please let us know.

    125 | 126 |
    127 | 128 | 129 | -------------------------------------------------------------------------------- /pau/templates-jinja2/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% import "pau/common/macros.html" as pau_macros with context %} 4 | 5 | 6 | {{ page_title }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% if page_description %} 16 | 17 | {% endif %} 18 | {% if page_keywords %} 19 | 20 | {% endif %} 21 | 22 | 23 | 24 | {% block extra_header %} 25 | {% endblock %} 26 | 27 | {% block extra_css %} 28 | 29 | 30 | 36 | 37 | {% endblock %} 38 | 39 | 40 | 41 | {% block body %} 42 |
    43 | {{ pau_macros.page_context_meta_tag() }} 44 | {% block nav %} 45 | {% include "pau/common/nav.html" %} 46 | {% endblock %} 47 | {% block hero_block %}{% endblock %} 48 | {% block main %}{% endblock %} 49 | {% block footer %} 50 | {% include "pau/common/footer.html" %} 51 | {% endblock %} 52 |
    53 | {% endblock %} 54 | 55 | {% block pre_javascript %}{% endblock %} 56 | 57 | {% block javascript %} 58 | {%- if DEBUG -%} 59 | 60 | {%- else -%} 61 | 62 | {%- endif -%} 63 | 64 | {% endblock %} 65 | 66 | {% block post_javascript %} 67 | {# This is for emoticons #} 68 | 69 | {% endblock %} 70 | 71 | {% block analytics %} 72 | {% endblock %} 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /pau/templates-jinja2/blank.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/templates-jinja2/blank.html -------------------------------------------------------------------------------- /pau/templates-jinja2/common/footer.html: -------------------------------------------------------------------------------- 1 | {% if not infinite_scroll and not hide_signup and not hide_footer %} 2 | 5 | {% endif %} 6 | 7 | -------------------------------------------------------------------------------- /pau/templates-jinja2/common/macros.html: -------------------------------------------------------------------------------- 1 | {% import "pau/macros/forms.html" as forms with context %} 2 | 3 | {% macro reportpost() %} 4 |
    5 |
    6 | {{ forms.input_field(report_post_form['report_post_data']) }} 7 |
    8 |
    9 | {% endmacro %} 10 | 11 | {% macro page_context_meta_tag() %} 12 | 13 | {% endmacro %} 14 | -------------------------------------------------------------------------------- /pau/templates-jinja2/common/nav.html: -------------------------------------------------------------------------------- 1 |
    2 | 33 |
    34 | -------------------------------------------------------------------------------- /pau/templates-jinja2/explore.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    {{ explore_title }}
    6 |
    7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /pau/templates-jinja2/global.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    Global
    6 |
    7 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/hashtags.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 |
    7 |

    #

    8 |
    {{hashtag}}
    9 |
    10 |
    11 | RSS 12 |
    13 |
    14 |
    15 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/logout.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/base.html' %} 2 | {% import "pau/macros/forms.html" as forms_macros with context %} 3 | {% block main %} 4 | {{ forms_macros.auto_submit_page( 5 | action='logout', 6 | in_progress_text='Logging out ...', 7 | before_button_text='Still here? Click ', 8 | button_text='here', 9 | after_button_text=' to complete the logout process.' 10 | ) 11 | }} 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /pau/templates-jinja2/macros/forms.html: -------------------------------------------------------------------------------- 1 | {% macro bootstrap_field(field, label=True, hidden=False, label_subtext=None) %} 2 |
    3 | {% if label %} 4 | 5 | {% if label_subtext %} 6 |
    {{ label_subtext }}
    7 | {% endif %} 8 | {% endif %} 9 |
    10 | {{ caller() }} 11 |
    12 |
    13 | {% endmacro %} 14 | 15 | {% macro field_errors(field) %} 16 | {% if field.errors %} 17 | {% for error in field.errors %} 18 | {% if not loop.first %}, {% endif %}{{ error|escape }} 19 | {% endfor %} 20 | {% endif %} 21 | {% endmacro %} 22 | 23 | {% macro input_field(field, label=True, hidden=False, label_subtext=None) %} 24 | {% call bootstrap_field(field, label, hidden, label_subtext) %} 25 | {% if not app_creating and field.field.only_for_create %} 26 | {% if field.field.pre_text %}{{field.field.pre_text}}{% endif %}{{field.value()}} 27 | {% else %} 28 | {{ field|safe }} 29 | {% if field.errors %} 30 | 31 | {{ field_errors(field) }} 32 | 33 | {% endif %} 34 | {% endif %} 35 | {% if field.help_text %} 36 |

    {{ field.help_text|safe }}

    37 | {% endif %} 38 | {% endcall %} 39 | {% endmacro %} 40 | 41 | {% macro locked_input_field(field, label=True, hidden=False, label_subtext=None) %} 42 | {% call bootstrap_field(field, label, hidden, label_subtext) %} 43 | {% if field.field.pre_text %}{{field.field.pre_text}}{% endif %}{{field.value()}} 44 | {% if field.help_text %} 45 |

    {{ field.help_text|safe }}

    46 | {% endif %} 47 | {% endcall %} 48 | {% endmacro %} 49 | 50 | {% macro checkbox_container(field, wrap_with_label, label_classes='control-label') %} 51 |
    52 | {% if not wrap_with_label %} 53 | {{ field|safe }} {{field.label}} 54 | {% else %} 55 | 56 | {% endif %} 57 |
    58 | {{ field_errors(field) }} 59 | {% endmacro %} 60 | 61 | {% macro checkbox_field(field, wrap_with_label=False) %} 62 |
    63 |
    64 | {{ checkbox_container(field, wrap_with_label) }} 65 |
    66 |
    67 | {% endmacro %} 68 | 69 | {% macro fieldset(form_key, form) %} 70 |
    71 | {% for field in form.hidden_fields() %} 72 | {{ field|safe }} 73 | {% endfor %} 74 | {{ caller() }} 75 |
    76 | {% endmacro %} 77 | 78 | {% macro auto_submit_page(action='', in_progress_text='', before_button_text='', button_text='here', after_button_text='') %} 79 |
    80 |
    81 |

    {{ in_progress_text }}

    82 |
    83 |
    84 | 85 | 86 |
    87 |
    88 | {{ before_button_text }}{{ after_button_text }} 89 |
    90 |
    91 |
    92 |
    93 | 96 |
    97 |
    98 | {% endmacro %} 99 | -------------------------------------------------------------------------------- /pau/templates-jinja2/oauth_error.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/base.html' %} 2 | 3 | {% block nav %}{% endblock %} 4 | {% block hero_block %} 5 |
    6 |
    7 |
    8 | 9 |

    There was an error

    10 |

    {{ error_description }}

    11 | 14 |
    15 |
    16 |
    17 | {% endblock %} 18 | 19 | {% block main %} 20 | {% endblock %} 21 | 22 | {% block footer %} 23 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/pjax_base.html: -------------------------------------------------------------------------------- 1 | {% import "pau/common/macros.html" as pau_macros with context %} 2 | {% block nav %} 3 | {% include "pau/common/nav.html" %} 4 | {% endblock %} 5 | {% block main %}{% endblock %} 6 | {% block footer %} 7 | {% include "pau/common/footer.html" %} 8 | {% if is_pjax %} 9 | {{ pau_macros.page_context_meta_tag() }} 10 | {{ page_title }} 11 | {% endif %} 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /pau/templates-jinja2/pjax_switch.html: -------------------------------------------------------------------------------- 1 | {% if is_pjax %} 2 | {% extends 'pau/pjax_base.html' %} 3 | {% else %} 4 | {% extends 'pau/base.html' %} 5 | {% endif %} -------------------------------------------------------------------------------- /pau/templates-jinja2/post/create.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | {% block nav_list %}{% endblock %} 3 | {% block main %} 4 |
    5 |
    6 |
    7 |

    Post to App.net {% if post_create_url_message %}{{ post_create_url_message }}{% endif %}

    8 |
    9 |
    10 | {{ post_box_presenter|render_presenters }} 11 |
    12 |
    13 |
    14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /pau/templates-jinja2/post/create_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | {% block nav_list %}{% endblock %} 3 | {% block main %} 4 |
    5 |
    6 |

    Thanks

    7 |

    Close Window

    8 | 11 |
    12 |
    13 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/post/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/two_column_base.html' %} 2 | {% import "pau/common/macros.html" as pau_macros with context %} 3 | 4 | {# This stuff won't be around in pjax but thats okay because crawlers don't use pjax #} 5 | {% block extra_header %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% endblock %} 13 | 14 | {% macro posts_in_thread(post_presenters) %} 15 | {% if post_presenters %} 16 |
    17 |
    18 |
    19 | {{post_presenters|render_presenters}} 20 |
    21 |
    22 |
    23 | {% endif %} 24 | {% endmacro %} 25 | 26 | {% block main_column %} 27 |
    28 |
    29 | {{ posts_in_thread(before_post_presenters) }} 30 |
    31 | {{ main_post_inner|render_presenters }} 32 | {% if owner %} 33 |
    34 |
    35 |
    36 |
    37 | {{num_replies}} 38 |

    {% trans num=num_replies %}reply{% pluralize %}replies{% endtrans %}

    39 |
    40 | 46 | 52 |
    53 | {{ star_facepile_presenter|render_presenters}} 54 |
    55 |
    56 |
    57 |
    58 | {% endif %} 59 |
    60 | {{ pau_macros.reportpost() }} 61 | {{ posts_in_thread(after_post_presenters) }} 62 | {% if not is_repost %} 63 |
    64 | {{ post_box_presenter|render_presenters }} 65 |
    66 | {% endif %} 67 |
    68 |
    69 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/post/photo.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | {% import "pau/common/macros.html" as pau_macros with context %} 3 | 4 | {% block nav %} 5 | {% endblock %} 6 | 7 | {# This stuff won't be around in pjax but thats okay because crawlers don't use pjax #} 8 | {% block extra_header %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% endblock %} 17 | 18 | {% block main %} 19 |
    20 |
    21 |
    22 | 23 | See conversation → 24 | Conversation 25 | {% if not request.user.is_authenticated() %} 26 | 29 | {% endif %} 30 |
    31 |
    32 |
    33 |
    34 |
    35 | 36 |
    37 | {% if prev_photo_url %} 38 | 39 | {% endif %} 40 | {% if next_photo_url %} 41 | 42 | {% endif %} 43 |
    44 |
    45 | {{post_presenter|render_presenters}} 46 |
    47 |
    48 | {% endblock %} 49 | 50 | {% block footer %} 51 | {% if is_pjax %} 52 | {{ pau_macros.page_context_meta_tag() }} 53 | {{ page_title }} 54 | {% endif %} 55 | {% endblock %} 56 | -------------------------------------------------------------------------------- /pau/templates-jinja2/post/reposters.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 |
    7 |
    Reposted By
    8 |
    9 |
    10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /pau/templates-jinja2/post/starred_by.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 |
    7 |
    Starred By
    8 |
    9 |
    10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /pau/templates-jinja2/splash.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/base.html' %} 2 | 3 | {% block nav %}{% endblock %} 4 | {% block hero_block %} 5 |
    6 |
    7 |
    8 | 9 |

    Alpha

    10 |

    Microblogging for App.net

    11 | 14 |
    15 |
    16 |
    17 |
    18 |
    19 |
    20 |
    What is Alpha?
    21 |

    Alpha is an ad-free social network. It’s your real-time feed, a home for meaningful conversation, where you control your data.

    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |

    Join the conversation

    31 |

    Alpha has been designed with communication at its core. Easily join a conversation or start your own and see why the App.net community is so special.

    32 |
    33 |
    34 |
    35 |
    36 |
    37 |
    38 |
    39 |

    Stay Informed

    40 |

    With a vibrant community of individuals and organizations, Alpha is your place to stay informed. Whether you’re looking for humor or news, to find friends or make new ones, Alpha has something for you.

    41 |
    42 |
    43 |
    44 |
    45 |
    46 |
    47 |
    48 |
    49 |
    50 |
    51 |
    52 |
    53 |

    Beautiful Photos

    54 |

    Sometimes you need a picture to tell the entire story. Your photos will be displayed in your stream and on a photo display page.

    55 |
    56 |
    57 |
    58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    64 |
    65 |

    Want access to Alpha?

    66 | Log In with App.net 67 |
    68 |
    69 |
    70 | {% endblock %} 71 | 72 | {% block main %} 73 | {% endblock %} 74 | -------------------------------------------------------------------------------- /pau/templates-jinja2/stream.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/two_column_base.html' %} 2 | {% import "pau/common/macros.html" as pau_macros with context %} 3 | 4 | {% block main_column %} 5 | {% if show_new_post_box %} 6 | {{ post_box_presenter|render_presenters }} 7 | {% endif %} 8 | {% block extra_items %} 9 | {% endblock %} 10 | {% if item_presenters %} 11 |
    12 |
    13 |
    18 | {{item_presenters|render_presenters}} 19 |
    20 |
    21 | 22 |
    23 |
    24 |
    25 | {% elif empty_message %} 26 |
    27 | {{ empty_message }} 28 |
    29 | {% endif %} 30 | {{ pau_macros.reportpost() }} 31 | {% if rss_link %} 32 | {# 33 | because of pjax it's just easier if we put this in the body 34 | class='hide' is complete hocus pocus 35 | #} 36 | 37 | {% endif %} 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /pau/templates-jinja2/two_column_base.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | 3 | {% macro nav_menu_item(key, class, display_name, href, extra_nav_classes='', badge=0) %} 4 |
  • 5 | 6 | {% if class %} 7 | 8 | {% endif %} 9 | {{display_name}} 10 | {% if badge %} 11 | {{ badge }} 12 | {% endif %} 13 | 14 | 15 |
  • 16 | {% endmacro %} 17 | 18 | {% macro search_bar(query) %} 19 |
    20 | 27 |
    28 | {% endmacro %} 29 | 30 | {% block main %} 31 | {% block hero_block %} 32 | {% endblock %} 33 |
    34 |
    35 |
    36 | {% block sidebar %} 37 | 72 | {% endblock %} 73 | {# XXX: Fucking horrible, I know, but this is the simplest least affecting change I could think of. #} 74 |
    75 |
    76 | {% block hero %} 77 | {% endblock %} 78 | {% block before_main_content %}{% endblock %} 79 |
    80 | {% block main_column %}{% endblock %} 81 |
    82 | {% block after_main_content %}{% endblock %} 83 |
    84 |
    85 |
    86 |
    87 | {% endblock %} 88 | -------------------------------------------------------------------------------- /pau/templates-jinja2/user/follow.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | {% block nav_list %}{% endblock %} 3 | {% block main %} 4 |
    5 |
    6 |
    7 | {% if not following %} 8 |

    Follow {{user.username}} on App.net

    9 | {{ user_follow_presenter|render_presenters }} 10 | {% else %} 11 |

    You are already following {{user.username}}

    12 |

    Close Window

    13 | {% endif %} 14 |
    15 |
    16 |
    17 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/user/follow_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | {% block nav_list %}{% endblock %} 3 | {% block main %} 4 |
    5 |
    6 |

    Thanks

    7 |

    Close Window

    8 | 11 |
    12 |
    13 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/user/follows.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 | {% set user_detail_url = url_for('user_detail_view', args=[owner.username]) %} 7 | {% if follow_type == 'follows_to' %} 8 |
    9 |
    10 |
    Followers
    11 |
    12 |
    13 |

    @{{owner.username|possessive}} followers

    14 |
    15 | {% elif follow_type == 'follows_from' %} 16 |
    17 |
    18 |
    Following
    19 |
    20 |
    21 |

    Users @{{owner.username}} is following

    22 |
    23 | {% endif %} 24 |
    25 |
    26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /pau/templates-jinja2/user/interactions.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 |
    7 |
    Interactions
    8 |
    9 |
    10 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/user/mentions.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 |
    7 |
    Mentions
    8 |
    9 |
    10 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/user/starred.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 |
    7 |
    Stars
    8 |
    9 |
    10 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/user/stream.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/stream.html' %} 2 | 3 | {% block hero %} 4 |
    5 |
    6 |
    7 |
    Your Stream
    8 |
    9 |
    10 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/user/subscribe.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | {% block nav_list %}{% endblock %} 3 | {% block main %} 4 |
    5 |
    6 |
    7 | {% if not subscribed %} 8 |

    Subscribe to {{ channel_subscribe_presenter.channel_title }} on App.net

    9 | {{ channel_subscribe_presenter|render_presenters }} 10 | {% else %} 11 |

    You are already subscribed to:
    {{ channel_subscribe_presenter.channel_title }}

    12 | {% endif %} 13 |
    14 |
    15 |
    16 | {% endblock %} -------------------------------------------------------------------------------- /pau/templates-jinja2/user/subscribe_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'pau/pjax_switch.html' %} 2 | {% block nav_list %}{% endblock %} 3 | {% block main %} 4 |
    5 |
    6 |

    Thanks

    7 | 10 |
    11 |
    12 | {% endblock %} -------------------------------------------------------------------------------- /pau/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/utils/__init__.py -------------------------------------------------------------------------------- /pau/utils/annotations.py: -------------------------------------------------------------------------------- 1 | 2 | from pau.constants import CORE_OEMBED 3 | 4 | 5 | def _check_core_oembed(oembed_type): 6 | def inner(annotation_dict): 7 | annotation_type = annotation_dict.get('type') 8 | if annotation_type == CORE_OEMBED: 9 | value = annotation_dict.get('value') 10 | if value: 11 | value_type = value.get('type') 12 | version = value.get('version') 13 | width = value.get('width') 14 | height = value.get('height') 15 | thumbnail_width = value.get('thumbnail_width') 16 | thumbnail_height = value.get('thumbnail_height') 17 | thumbnail_url = value.get('thumbnail_url') 18 | return all([version == '1.0', value_type == oembed_type, width, height, thumbnail_width, thumbnail_height, 19 | thumbnail_url]) 20 | return inner 21 | 22 | 23 | is_photo_annotation = _check_core_oembed('photo') 24 | is_video_annotation = lambda annotation: _check_core_oembed('video')(annotation) or _check_core_oembed('html5video')(annotation) 25 | 26 | 27 | def get_photo_annotations(annotations): 28 | return filter(is_photo_annotation, annotations) 29 | 30 | 31 | def get_video_annotations(annotations): 32 | return filter(is_video_annotation, annotations) 33 | 34 | 35 | def get_attachment_annotations(annotations): 36 | return filter(lambda a_dict: a_dict.get('type') == 'net.app.core.attachments', annotations) 37 | 38 | 39 | def get_place_annotation(annotations): 40 | place = None 41 | for a in annotations: 42 | annotation_type = a.get('type') 43 | if annotation_type in ('net.app.core.checkin', 'net.app.ohai.location'): 44 | value = a.get('value') 45 | if value: 46 | if value.get('name'): 47 | place = a 48 | return place 49 | -------------------------------------------------------------------------------- /pau/utils/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | def oembed_url(url): 4 | return "%s/oembed?url=%s" % (settings.PUBLIC_API_ROOT, url) 5 | -------------------------------------------------------------------------------- /pau/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/pau/views/__init__.py -------------------------------------------------------------------------------- /pau/views/auth.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.contrib.auth import logout as auth_logout, login as auth_login, REDIRECT_FIELD_NAME 4 | from django.http import HttpResponseRedirect 5 | 6 | from social.actions import do_complete 7 | from social.apps.django_app.utils import strategy 8 | 9 | from paucore.utils.web import smart_reverse 10 | 11 | from pau.views.base import PauMMLActionView 12 | from pau.bridge import handle_actions_blob, AlphaInsufficientStorageException 13 | 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | def _url_redirect_when_logged_in(request, blob_dict=None): 19 | if blob_dict and 'action' in blob_dict: 20 | try: 21 | handle_actions_blob(request, blob_dict) 22 | except AlphaInsufficientStorageException: 23 | logger.warning('AlphaInsufficientStorageException on handle_action_blob for login redirect blob_dict=%s', blob_dict) 24 | 25 | 26 | class PauLogoutView(PauMMLActionView): 27 | template_name = 'pau/logout.html' 28 | page_title = 'Log Out - Alpha' 29 | requires_auth = True 30 | page_description = 'Log Out' 31 | 32 | def action_logout(self, request, *args, **kwargs): 33 | auth_logout(request) 34 | return HttpResponseRedirect(smart_reverse(request, 'home')) 35 | 36 | logout = PauLogoutView.as_view() 37 | 38 | 39 | class PauOAuthErrorView(PauMMLActionView): 40 | template_name = 'pau/oauth_error.html' 41 | page_title = 'oAuth error - Alpha' 42 | requires_auth = False 43 | page_description = 'oAuth error' 44 | 45 | def populate_context(self, request, *args, **kwargs): 46 | super(PauOAuthErrorView, self).populate_context(request, *args, **kwargs) 47 | error = request.GET.get('error') 48 | error_description = '' 49 | if error == 'access_denied': 50 | error_description = 'You must click authorization when you are asked to authorize Alpha.' 51 | 52 | else: 53 | error_description = ('There was an during the authorization flow.' 54 | ' Please contact the site administrators and let them know.') 55 | 56 | self.view_ctx['error_description'] = error_description 57 | 58 | oauth_error = PauOAuthErrorView.as_view() 59 | 60 | 61 | @strategy('social:complete') 62 | def complete(request, backend, *args, **kwargs): 63 | """Authentication complete view, override this view if transaction 64 | management doesn't suit your needs.""" 65 | next_blob = request.session.pop('next_blob', None) 66 | 67 | error = request.GET.get('error') 68 | if error: 69 | return oauth_error(request) 70 | 71 | resp = do_complete(request.social_strategy, _do_login, request.user, 72 | redirect_name=REDIRECT_FIELD_NAME, *args, **kwargs) 73 | 74 | if next_blob: 75 | alt_resp = _url_redirect_when_logged_in(request, next_blob) 76 | if alt_resp: 77 | return alt_resp 78 | 79 | return resp 80 | 81 | 82 | def _do_login(strategy, user): 83 | # user.social_user is the used UserSocialAuth instance defined in 84 | # authenticate process 85 | social_user = user.social_user 86 | auth_login(strategy.request, user) 87 | strategy.request.session['OMG_NEW_TOKEN_SPOT_omo_oauth2_token'] = social_user.tokens 88 | if strategy.setting('SESSION_EXPIRATION', True): 89 | # Set session expiration date if present and not disabled 90 | # by setting. Use last social-auth instance for current 91 | # provider, users can associate several accounts with 92 | # a same provider. 93 | expiration = social_user.expiration_datetime() 94 | if expiration: 95 | try: 96 | strategy.request.session.set_expiry( 97 | expiration.seconds + expiration.days * 86400 98 | ) 99 | except OverflowError: 100 | # Handle django time zone overflow 101 | strategy.request.session.set_expiry(None) 102 | -------------------------------------------------------------------------------- /pau/views/base.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.conf import settings 4 | from django.contrib.auth import logout as auth_logout 5 | from django.http import HttpResponseRedirect 6 | 7 | from paucore.utils.web import smart_reverse 8 | from paucore.web.template import render_template_response 9 | from paucore.web.views import MMLActionView 10 | 11 | from pau import bridge 12 | 13 | from pau.forms import ReportPostForm 14 | from pau.views.mixins import OAuthLoginRequiredViewMixin 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | def rate_limit_handler(request, *args, **kwargs): 20 | response = render_template_response(request, {}, '429.html') 21 | response.status_code = 429 22 | return response 23 | 24 | 25 | class PauMMLActionView(OAuthLoginRequiredViewMixin, MMLActionView): 26 | selected_nav_page = None 27 | minify_html = False 28 | 29 | def populate_context(self, request, *args, **kwargs): 30 | super(PauMMLActionView, self).populate_context(request, *args, **kwargs) 31 | self.view_ctx.update_ctx({ 32 | '__js_page_load_hooks': ['utils.handle_resize', 'init_pau', 'init_post_delete', 'init_mute_user', 'init_post_report', 33 | 'init_star_post', 'init_repost', 'pau.init_fixed_nav'], 34 | '__js_api_options': { 35 | 'api_base_url': smart_reverse(request, 'omo_api_proxy'), 36 | }, 37 | '__js_canvas_mode': 'pau', 38 | '__js_subscribe_url': 'https://account.app.net/upgrade/', 39 | '__js_upgrade_storage_url': 'https://account.app.net/settings/upgrade/storage/', 40 | 'selected_nav_page': self.selected_nav_page, 41 | 'explore_streams': bridge.list_explore_streams(request), 42 | 'report_post_form': ReportPostForm(), 43 | }) 44 | 45 | self.view_ctx.forms = [] 46 | 47 | def dispatch(self, request, *args, **kwargs): 48 | try: 49 | response = super(PauMMLActionView, self).dispatch(request, *args, **kwargs) 50 | except bridge.AlphaRateLimitAPIException, e: 51 | logger.warn('Hit an api rate limit: %s', e) 52 | return rate_limit_handler(request, *args, **kwargs) 53 | except bridge.AlphaAuthAPIException, e: 54 | logger.info('Alpha auth API execption: %s', e) 55 | auth_logout(request) 56 | return HttpResponseRedirect('/') 57 | 58 | response['X-Build-Info'] = settings.BUILD_INFO 59 | 60 | return response 61 | -------------------------------------------------------------------------------- /pau/views/mixins.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.http import HttpResponseRedirect 4 | 5 | from paucore.utils.web import smart_reverse 6 | from paucore.web.views import ViewMiddleware, EarlyReturn 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class OAuthLoginRequiredViewMixin(ViewMiddleware): 12 | requires_auth = True 13 | 14 | def process_pre_view(self, view_inst): 15 | # duplicated with enforce_auth so that requires_auth can vary based on the view_inst 16 | if view_inst.requires_auth and not self.is_authorized(view_inst.state.request): 17 | self.raise_not_authorized(view_inst.state.request, *view_inst.state.view_args, **view_inst.state.view_kwargs) 18 | 19 | def is_authorized(self, request, *args, **kwargs): 20 | return request.user.is_authenticated() 21 | 22 | def next_url(self, request): 23 | return request.build_absolute_uri() 24 | 25 | def handle_not_authorized(self, request, *args, **kwargs): 26 | return HttpResponseRedirect(smart_reverse(request, 'login', url_params={'next': self.next_url(request)})) 27 | 28 | def raise_not_authorized(self, request, *args, **kwargs): 29 | resp = self.handle_not_authorized(request, *args, **kwargs) 30 | raise EarlyReturn(resp) 31 | 32 | def enforce_auth(self, request, *args, **kwargs): 33 | if self.requires_auth and not self.is_authorized(request): 34 | # this is how we "decorate" for login_required 35 | self.raise_not_authorized(request, *args, **kwargs) 36 | 37 | def dispatch(self, request, *args, **kwargs): 38 | try: 39 | self.enforce_auth(request, *args, **kwargs) 40 | except EarlyReturn, er: 41 | return er.response 42 | return super(OAuthLoginRequiredViewMixin, self).dispatch(request, *args, **kwargs) 43 | -------------------------------------------------------------------------------- /pau/views/proxy.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.http import HttpResponse 4 | 5 | from pau import bridge 6 | 7 | PASS_THROUGH_HEADERS = ( 8 | ('HTTP_X_ADN_MIGRATION_OVERRIDES', 'X-Adn-Migration-Overrides'), 9 | ) 10 | 11 | 12 | def ajax_api_proxy(request, path, *args, **kwargs): 13 | data = request.POST 14 | post_type = 'form_data' # default to form data unless the client sent us json 15 | 16 | if request.META.get('CONTENT_TYPE', None) == 'application/json': 17 | 18 | try: 19 | data = json.loads(request.body) 20 | post_type = 'json' 21 | except: 22 | pass 23 | 24 | headers = {} 25 | files = {} 26 | 27 | for file_slug, f in request.FILES.iteritems(): 28 | files[file_slug] = (f.name, f, f.content_type) 29 | 30 | for dj_header, header in PASS_THROUGH_HEADERS: 31 | if dj_header in request.META: 32 | headers[header] = request.META[dj_header] 33 | 34 | path = '/' + path 35 | 36 | resp = bridge.api.call_api(request, path, params=request.GET, data=data, method=request.method, headers=headers, 37 | post_type=post_type, files=files) 38 | 39 | return HttpResponse(json.dumps(resp), content_type="application/json", status=resp['meta']['code']) 40 | -------------------------------------------------------------------------------- /paucore/__init__.py: -------------------------------------------------------------------------------- 1 | """paucore - Base library for Mixed Media Labs Python apps.""" 2 | 3 | __version__ = '0.1.0' 4 | -------------------------------------------------------------------------------- /paucore/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/paucore/data/__init__.py -------------------------------------------------------------------------------- /paucore/stats/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/paucore/stats/__init__.py -------------------------------------------------------------------------------- /paucore/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/paucore/utils/__init__.py -------------------------------------------------------------------------------- /paucore/utils/date.py: -------------------------------------------------------------------------------- 1 | import calendar 2 | import itertools 3 | import pytz 4 | 5 | from datetime import datetime 6 | 7 | from django.utils.translation import ugettext_lazy as _ 8 | 9 | MXML_ISO_STRF_FORMAT = '%Y-%m-%dT%H:%M:%SZ' 10 | ALL_TIMEZONE_CHOICES = tuple(itertools.izip(*itertools.tee(pytz.all_timezones, 2))) 11 | FANCY_COMMON_TIMEZONES = tuple(itertools.izip(*itertools.tee(pytz.common_timezones, 2))) 12 | 13 | # just the values, not a "choices" 14 | COMMON_TIMEZONES = pytz.common_timezones 15 | 16 | JUST_NOW = _("now") 17 | SECONDS_AGO = _("%(seconds)ds") 18 | MINUTES_AGO = _("%(minutes)dm") 19 | HOURS_AGO = _("%(hours)dh") 20 | DAYS_AGO = _("%(count)dd") 21 | WEEKS_AGO = _("%(count)dw") 22 | 23 | OLDER_CHUNKS = ( 24 | (7.0, 1.0, DAYS_AGO), 25 | (30.0, 7.0, WEEKS_AGO), 26 | ) 27 | 28 | 29 | def append_ago(base, ago_suffix=False): 30 | if ago_suffix: 31 | return base + u' ago' 32 | else: 33 | return base 34 | 35 | 36 | def naturaldate(date, just_now=JUST_NOW, ago_suffix=False, start_date=None, older_chunks=OLDER_CHUNKS): 37 | """Convert datetime into a human natural date string.""" 38 | 39 | if not date: 40 | return unicode('') 41 | 42 | if not start_date: # MATTHEW. 43 | start_date = datetime.now() 44 | 45 | today = datetime(start_date.year, start_date.month, start_date.day) 46 | delta = start_date - date 47 | delta_midnight = today - date 48 | 49 | days = delta.days 50 | hours = round(delta.seconds / 3600, 0) 51 | minutes = delta.seconds / 60 52 | 53 | if days < 0: 54 | return unicode(just_now) 55 | 56 | if days == 0: 57 | if hours == 0: 58 | if minutes > 0: 59 | return append_ago(unicode(MINUTES_AGO % {"minutes": minutes}), ago_suffix=ago_suffix) 60 | else: 61 | return unicode(just_now) 62 | else: 63 | return append_ago(unicode(HOURS_AGO % {"hours": hours}), ago_suffix=ago_suffix) 64 | 65 | for limiter, chunk, text in older_chunks: 66 | if days <= limiter: 67 | count = round((delta_midnight.days + 1) / chunk, 0) 68 | return append_ago(unicode(text % ({"count": count})), ago_suffix=ago_suffix) 69 | 70 | if days <= 365: 71 | return date.strftime('%d %b') 72 | 73 | return date.strftime('%d %b %y') 74 | 75 | 76 | def datetime_to_secs(dt): 77 | # http://stackoverflow.com/questions/2956886/python-calendar-timegm-vs-time-mktime 78 | return int(calendar.timegm(dt.utctimetuple())) 79 | -------------------------------------------------------------------------------- /paucore/utils/image.py: -------------------------------------------------------------------------------- 1 | def fit_to_box(w, h, max_w, max_h, expand=False): 2 | # proportionately scale a box defined by (w,h) so that it fits within a box defined by (max_w, max_h) 3 | # by default, only scaling down is allowed, unless expand=True, in which case scaling up is allowed 4 | if w < max_w and h < max_h and not expand: 5 | return (w, h) 6 | largest_ratio = max(float(w) / max_w, float(h) / max_h) 7 | new_height = int(float(h) / largest_ratio) 8 | new_width = int(float(w) / largest_ratio) 9 | return (new_width, new_height) 10 | -------------------------------------------------------------------------------- /paucore/utils/presenters.py: -------------------------------------------------------------------------------- 1 | from paucore.utils.htmlgen import HtmlGen 2 | 3 | 4 | html = HtmlGen() 5 | 6 | 7 | class AbstractPresenter(object): 8 | def generate_html(self): 9 | raise Exception("Not implemented") 10 | 11 | def generate_json(self, short=False): 12 | raise Exception("Not implemented") 13 | 14 | def render_html(self): 15 | if not getattr(self, 'visible', True): 16 | return '' 17 | if not hasattr(self, '_cached_html'): 18 | setattr(self, '_cached_html', self.generate_html()) 19 | return self._cached_html 20 | 21 | def render_json(self, short=False): 22 | key = '_cached_short_json' if short else '_cached_json' 23 | if not hasattr(self, key): 24 | setattr(self, key, self.generate_json(short=short)) 25 | return getattr(self, key) 26 | 27 | @classmethod 28 | def from_model(cls, model): 29 | """Factory method that creates the presenter from the model and pulls out/formats the appropriate data""" 30 | raise Exception("This presenter can't render class:%s" % model.__class__.__name__) 31 | 32 | 33 | def html_list_to_english(L, with_period=False): 34 | 'Convert a list into a string separated by commas and "and"' 35 | if len(L) == 0: 36 | return [] 37 | elif len(L) == 1: 38 | res = L 39 | else: 40 | res = [] 41 | 42 | for el in L[:-1]: 43 | res.append(el) 44 | res.append(", ") 45 | 46 | if res: 47 | # Why yes, I do give a fuck about an Oxford comma. 48 | res.pop() 49 | 50 | res.append(" and ") 51 | res.append(L[-1]) 52 | 53 | if with_period: 54 | res.append('.') 55 | 56 | return res 57 | -------------------------------------------------------------------------------- /paucore/utils/string.py: -------------------------------------------------------------------------------- 1 | import re 2 | from functools import partial 3 | 4 | from django.contrib.auth.models import BaseUserManager 5 | 6 | 7 | generate_random = partial(BaseUserManager.make_random_password.im_func, None) 8 | 9 | 10 | def string_to_css_class(string): 11 | 'Convert a string to a format useful for use as a css class.' 12 | if not string: 13 | return '' 14 | 15 | return string.lower().replace(' ', '_').replace('.', '_').replace('[', '_').replace(']', '_') 16 | 17 | 18 | def possessive(str): 19 | if not str: 20 | return '' 21 | if str[-1] == 's': 22 | return "%s'" % str 23 | else: 24 | return "%s's" % str 25 | 26 | 27 | def truncate(s, length=300, continuation="..."): 28 | if s: 29 | return (s[:length - len(continuation)] + continuation) if len(s) > length else s 30 | else: 31 | return "" 32 | 33 | 34 | # Stolen from https://github.com/mitsuhiko/jinja2/blob/7d268bef0e8f3f12c0acb90f30d67726a3e3f261/jinja2/filters.py 35 | # since there hasn't been a real release. When jinja2 goes to 2.7 we can delete this 36 | # https://github.com/mitsuhiko/jinja2/pull/59 37 | def do_filesizeformat(value, binary=False): 38 | """Format the value like a 'human-readable' file size (i.e. 13 kB, 39 | 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, 40 | Giga, etc.), if the second parameter is set to `True` the binary 41 | prefixes are used (Mebi, Gibi). 42 | """ 43 | bytes = float(value) 44 | base = binary and 1024 or 1000 45 | prefixes = [ 46 | (binary and 'KiB' or 'kB'), 47 | (binary and 'MiB' or 'MB'), 48 | (binary and 'GiB' or 'GB'), 49 | (binary and 'TiB' or 'TB'), 50 | (binary and 'PiB' or 'PB'), 51 | (binary and 'EiB' or 'EB'), 52 | (binary and 'ZiB' or 'ZB'), 53 | (binary and 'YiB' or 'YB') 54 | ] 55 | if bytes == 1: 56 | return '1 Byte' 57 | elif bytes < base: 58 | return '%d Bytes' % bytes 59 | else: 60 | for i, prefix in enumerate(prefixes): 61 | unit = base ** (i + 2) 62 | if bytes < unit: 63 | return '%.1f %s' % ((base * bytes / unit), prefix) 64 | return '%.1f %s' % ((base * bytes / unit), prefix) 65 | 66 | 67 | def camelcase_to_underscore(s): 68 | # stolen from http://stackoverflow.com/a/1176023 69 | s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', s) 70 | return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() 71 | -------------------------------------------------------------------------------- /paucore/utils/web.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import urllib 3 | 4 | from django.conf import settings 5 | from django.core.urlresolvers import reverse 6 | from django.utils.encoding import smart_str 7 | 8 | from paucore.utils.python import lru_cache 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | def smart_urlencode(params, force_percent=False): 14 | if force_percent: 15 | qf = urllib.quote 16 | else: 17 | qf = urllib.quote_plus 18 | 19 | parts = ('='.join((qf(smart_str(k)), qf(smart_str(v)))) for k, v in params.iteritems()) 20 | 21 | return '&'.join(parts) 22 | 23 | 24 | def append_query_string(url, params=None, force_percent=False): 25 | if not params: 26 | return url 27 | 28 | parts = [url] 29 | if '?' in url: 30 | parts.append('&') 31 | else: 32 | parts.append('?') 33 | 34 | parts.append(smart_urlencode(params, force_percent=force_percent)) 35 | 36 | return ''.join(parts) 37 | 38 | 39 | def memoized_reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current_app=None): 40 | args = args or () 41 | kwargs = kwargs or {} 42 | 43 | # make these hashable 44 | memo_args = (viewname, urlconf, tuple(args), tuple(kwargs.iteritems()), prefix, current_app) 45 | 46 | try: 47 | hash(memo_args) 48 | except: 49 | # Shouldn't get called, but log and don't fail if it does 50 | logger.warning('memoized_reverse() called with unhashable args: %s', memo_args) 51 | 52 | return reverse(viewname, urlconf=urlconf, args=args, kwargs=kwargs, prefix=prefix, current_app=current_app) 53 | 54 | return _wrapped_reverse(memo_args) 55 | 56 | 57 | @lru_cache(maxsize=10240) 58 | def _wrapped_reverse(memo_args): 59 | viewname, urlconf, args, kwargs, prefix, current_app = memo_args 60 | 61 | return reverse(viewname, urlconf=urlconf, args=args, kwargs=dict(kwargs), prefix=prefix, current_app=current_app) 62 | 63 | 64 | def smart_reverse(request, view, args=None, kwargs=None, force_qualified=False, url_params=None, url_fragment=None): 65 | view_path = memoized_reverse(view, args=args, kwargs=kwargs) 66 | 67 | # want SSL if: host requires SSL or is_ssl is set 68 | # or if we're on an SSL link and is_ssl was not supplied 69 | is_ssl = (request and request.is_secure()) or settings.SSL_ONLY 70 | 71 | # want scheme if: 72 | # is_ssl and !request.is_secure() 73 | # !is_ssl and request.is_secure() 74 | 75 | parts = [] 76 | 77 | if force_qualified or not request or is_ssl != request.is_secure(): 78 | if is_ssl: 79 | parts.append('https:') 80 | else: 81 | parts.append('http:') 82 | 83 | if parts: 84 | parts.append('//') 85 | parts.append(settings.PARENT_HOST) 86 | 87 | parts.append(view_path) 88 | 89 | if url_params: 90 | parts.append('?') 91 | parts.append(smart_urlencode(url_params)) 92 | 93 | if url_fragment: 94 | parts.append('#') 95 | parts.append(url_fragment) 96 | 97 | return ''.join(parts) 98 | -------------------------------------------------------------------------------- /paucore/web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdotnet/alpha/e3841a9bb0b9baa0dadc94e49db50461a8c0c32e/paucore/web/__init__.py -------------------------------------------------------------------------------- /paucore/web/auth_backend.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | from social.backends.appdotnet import AppDotNetOAuth2 4 | 5 | 6 | class MXMLAppDotNetOAuth2(AppDotNetOAuth2): 7 | # TODO figure out what actual constants to distribute, we can't use PARENT_HOST here 8 | # AUTHORIZATION_URL = "%s/oauth/authenticate" % settings.SOCIAL_AUTH_APPDOTNET_OAUTH_BASE 9 | # use this one for testing login 10 | AUTHORIZATION_URL = "%s/oauth/authorize" % settings.SOCIAL_AUTH_APPDOTNET_OAUTH_BASE 11 | ACCESS_TOKEN_URL = "%s/oauth/access_token" % settings.ALPHA_API_ROOT 12 | 13 | def auth_headers(self): 14 | headers = super(MXMLAppDotNetOAuth2, self).auth_headers() 15 | headers['Host'] = settings.SOCIAL_AUTH_APPDOTNET_OAUTH_BASE.split('://')[1] 16 | return headers 17 | 18 | def user_data(self, access_token, *args, **kwargs): 19 | """Loads user data from service""" 20 | url = '%s/token' % settings.ALPHA_API_ROOT 21 | params = { 22 | 'access_token': access_token 23 | } 24 | headers = { 25 | 'Host': 'api.%s' % settings.PARENT_HOST 26 | } 27 | 28 | resp = self.get_json(url, params=params, headers=headers) 29 | resp['data']['access_token'] = access_token 30 | 31 | return resp 32 | 33 | def extra_data(self, user, uid, response, details): 34 | return response['data'] 35 | -------------------------------------------------------------------------------- /paucore/web/context_processors.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from functools import partial 3 | from datetime import datetime 4 | 5 | from django.conf import settings 6 | 7 | from paucore.utils.date import datetime_to_secs 8 | from paucore.utils.web import smart_reverse 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | def url_for(request): 14 | return { 15 | 'url_for': partial(smart_reverse, request) 16 | } 17 | 18 | 19 | def default_jscontext(request): 20 | context = {} 21 | 22 | user = getattr(request, 'user', None) 23 | if user and user.is_authenticated(): 24 | context['__js_is_authenticated'] = True 25 | context['__js_authenticated_user_id'] = user.pk 26 | else: 27 | context['__js_is_authenticated'] = False 28 | 29 | if hasattr(request, 'unique_id'): 30 | context['__js_cookie_value'] = request.unique_id.cookie_value 31 | 32 | context['__js_timestamp'] = datetime_to_secs(datetime.now()) * 1000 33 | context['__js_build_info'] = settings.BUILD_INFO 34 | 35 | context['is_pjax'] = request.META.get('HTTP_X_PJAX') 36 | context['__js_is_pjax'] = request.META.get('HTTP_X_PJAX') 37 | context['get_asset_url'] = lambda _, x: x 38 | return context 39 | -------------------------------------------------------------------------------- /paucore/web/decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | from django.utils.decorators import available_attrs 4 | 5 | 6 | def ssl_only(view_func): 7 | """ 8 | Modifies a view function so that it is always viewed 9 | over SSL. 10 | """ 11 | def wrapped_view(*args, **kwargs): 12 | return view_func(*args, **kwargs) 13 | wrapped_view.ssl_only = True 14 | return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) 15 | 16 | 17 | def ignore_ssl(view_func): 18 | """ 19 | Modifies a view function so that it is exempt from SSL handling. 20 | """ 21 | def wrapped_view(*args, **kwargs): 22 | return view_func(*args, **kwargs) 23 | wrapped_view.ignore_ssl = True 24 | return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) 25 | -------------------------------------------------------------------------------- /paucore/web/middleware.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | class NullSessionBackend(dict): 5 | def flush(self): 6 | self.clear() 7 | 8 | def cycle_key(self): 9 | self.clear() 10 | 11 | 12 | class NoCacheMiddleware(object): 13 | 14 | def process_response(self, request, response): 15 | if request.user.is_authenticated(): 16 | response['Cache-control'] = 'private, no-cache, no-store, must-revalidate' 17 | response['Expires'] = 'Thu, 9 Sep 1999 09:09:09 GMT' 18 | response['Pragma'] = 'no-cache' 19 | elif settings.DEBUG: 20 | # Kinder, friendlier no-cache for resources in dev 21 | response['Cache-control'] = 'no-cache' 22 | 23 | return response 24 | -------------------------------------------------------------------------------- /paucore/web/template.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | from django.http import HttpResponse 4 | from django.template.loader import render_to_string, get_template 5 | from django.template import RequestContext 6 | from slimmer import slimmer 7 | 8 | from paucore.utils.data import is_seq_not_string 9 | from paucore.stats.statsd_client import graphite_timer 10 | 11 | 12 | def minify(html): 13 | "Convenience method." 14 | if isinstance(html, unicode): 15 | html = html.encode('utf-8') 16 | 17 | return slimmer.html_slimmer(html) 18 | 19 | 20 | def render_template_string(request, ctx, template, minify_html=None, extra_ctx=None): 21 | with graphite_timer('jinja2.render_template_string'): 22 | if minify_html is None: 23 | minify_html = template.endswith('.html') 24 | 25 | if extra_ctx: 26 | if not is_seq_not_string(extra_ctx): 27 | extra_ctx = [extra_ctx] 28 | 29 | new_ctx = {} 30 | page_load_hooks = ctx.get('__js_page_load_hooks', []) 31 | 32 | for ctx_callable in extra_ctx: 33 | ctx_item = ctx_callable(request) 34 | if ctx_item: 35 | if '__js_page_load_hooks' in ctx_item: 36 | page_load_hooks.extend(ctx_item['__js_page_load_hooks']) 37 | del ctx_item['__js_page_load_hooks'] 38 | new_ctx.update(ctx_item) 39 | 40 | new_ctx.update(ctx) 41 | new_ctx['__js_page_load_hooks'] = page_load_hooks 42 | 43 | ctx = new_ctx 44 | 45 | with graphite_timer('jinja2.render_to_string'): 46 | output = render_to_string(template, ctx, context_instance=RequestContext(request)) 47 | 48 | if isinstance(output, unicode): 49 | output = output.encode('utf-8') 50 | 51 | if ctx and ctx.get('__no_minify'): 52 | minify_html = False 53 | 54 | if minify_html: 55 | with graphite_timer('jinja2.minify_html'): 56 | output = slimmer.html_slimmer(output) 57 | 58 | return output 59 | 60 | 61 | def get_macro_module(request, ctx, template): 62 | """Fetch a module that represents a template for the purpose of rendering individual macros as snippets.""" 63 | ctx = ctx or {} 64 | 65 | t = get_template(template) 66 | if hasattr(t, 'make_module'): 67 | context_instance = RequestContext(request) 68 | context_instance.update(ctx) 69 | 70 | flat_dict = {} 71 | 72 | for d in context_instance.dicts: 73 | flat_dict.update(d) 74 | 75 | return t.make_module(t.new_context(flat_dict)) 76 | else: 77 | raise Exception('get_macro_module only works for Jinja2 templates') 78 | 79 | 80 | def render_template_response(request, ctx, template, no_cache=False, minify_html=None, extra_ctx=None, status_code=200): 81 | response = HttpResponse(render_template_string(request, ctx, template, minify_html, extra_ctx), status=status_code) 82 | 83 | if no_cache: 84 | response['Pragma'] = 'no-cache' 85 | response['Cache-Control'] = 'no-cache, must-revalidate' 86 | 87 | return response 88 | 89 | 90 | def template_response(template, no_cache=False, minify_html=None, extra_ctx=None): 91 | 92 | def render_template_decorator(view): 93 | 94 | @wraps(view) 95 | def inner(request, *a, **kw): 96 | ctx = view(request, *a, **kw) 97 | 98 | if isinstance(ctx, HttpResponse): 99 | return ctx 100 | else: 101 | return render_template_response(request, ctx, template, no_cache=no_cache, minify_html=minify_html, 102 | extra_ctx=extra_ctx) 103 | 104 | return inner 105 | return render_template_decorator 106 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.6.3 2 | 3 | # Heroku reqs 4 | dj-database-url==0.2.2 5 | dj-static==0.0.5 6 | gunicorn==18.0 7 | psycopg2==2.5.1 8 | static==0.4 9 | wsgiref==0.1.2 10 | 11 | # base deps 12 | iso8601==0.1.4 13 | pytz==2013b 14 | simplejson==2.5.2 15 | django-slimmer==0.0.2 16 | Jinja2==2.6 17 | lxml==3.2.1 18 | 19 | # adn 20 | adnpy==0.3.8 21 | git+https://github.com/berg/python-social-auth.git 22 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-2.7.3 --------------------------------------------------------------------------------