├── main_app ├── __init__.py ├── views │ ├── __init__.py │ ├── paste.py │ └── ajax.py ├── static │ ├── foundation │ │ ├── img │ │ │ └── .gitkeep │ │ ├── js │ │ │ ├── foundation │ │ │ │ ├── foundation.alerts.js │ │ │ │ ├── foundation.cookie.js │ │ │ │ ├── foundation.magellan.js │ │ │ │ ├── foundation.placeholder.js │ │ │ │ ├── foundation.dropdown.js │ │ │ │ ├── foundation.tooltips.js │ │ │ │ ├── foundation.interchange.js │ │ │ │ ├── foundation.reveal.js │ │ │ │ ├── foundation.topbar.js │ │ │ │ ├── foundation.js │ │ │ │ ├── foundation.section.js │ │ │ │ ├── foundation.orbit.js │ │ │ │ ├── foundation.clearing.js │ │ │ │ └── foundation.forms.js │ │ │ └── vendor │ │ │ │ └── custom.modernizr.js │ │ └── css │ │ │ └── normalize.css │ ├── img │ │ └── ajax-loader.gif │ ├── third_party_gd │ │ ├── REVIEW.md │ │ └── springy │ │ │ └── springyui.js │ ├── css │ │ └── main.css │ └── js │ │ ├── graph_utils.js │ │ └── main_ui.js ├── models.py ├── urls.py ├── tests.py ├── templates │ ├── base.html │ └── main.html └── utils.py ├── pastegraph ├── __init__.py ├── urls.py ├── wsgi.py └── settings.py ├── runtime.txt ├── .gitignore ├── Procfile ├── freeze.sh ├── set_local_params.sh ├── requirements.txt ├── set_heroku_params.sh ├── file_keeper ├── urls.py ├── views.py ├── admin.py ├── models.py └── __init__.py ├── manage.py ├── README.md ├── DEV-NOTES.md └── graphviz_restful_service ├── README.md └── graphviz_service.py /main_app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pastegraph/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-2.7.4 -------------------------------------------------------------------------------- /main_app/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main_app/static/foundation/img/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | *.pyc 3 | pastegraph/staticfiles/* 4 | env_params.txt 5 | *.tmp -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: python manage.py collectstatic --noinput; gunicorn pastegraph.wsgi 2 | -------------------------------------------------------------------------------- /freeze.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | source venv/bin/activate 4 | pip freeze | grep -iv "distribute" | tee requirements.txt 5 | -------------------------------------------------------------------------------- /main_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Paste(models.Model): 5 | source = models.TextField() 6 | -------------------------------------------------------------------------------- /main_app/static/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpavlenko/pastegraph/HEAD/main_app/static/img/ajax-loader.gif -------------------------------------------------------------------------------- /set_local_params.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for line in `cat env_params.txt`; do 4 | export "$line" 5 | done 6 | 7 | export DEBUG='True' -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.5.1 2 | argparse==1.2.1 3 | dj-database-url==0.2.1 4 | gunicorn==0.17.4 5 | psycopg2==2.5.1 6 | requests==1.2.3 7 | wsgiref==0.1.2 8 | -------------------------------------------------------------------------------- /set_heroku_params.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | for line in `cat env_params.txt`; do 6 | heroku config:set "$line" 7 | done 8 | 9 | heroku config:set DEBUG='False' 10 | 11 | -------------------------------------------------------------------------------- /file_keeper/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | 4 | urlpatterns = patterns('', 5 | url(r'^get/(?P[-_.A-Za-z0-9]*)$', 'file_keeper.views.get_file', name='get_file'), 6 | ) 7 | -------------------------------------------------------------------------------- /file_keeper/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | 3 | from file_keeper.models import File 4 | 5 | 6 | def get_file(request, hash_): 7 | file_ = File.objects.get(hash=hash_) 8 | return HttpResponse(file_.base64.decode('base64'), mimetype=file_.mime) 9 | -------------------------------------------------------------------------------- /file_keeper/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from file_keeper.models import File 4 | 5 | 6 | class FileAdmin(admin.ModelAdmin): 7 | fields = ('hash', 'mime', 'base64') 8 | list_display = fields 9 | 10 | 11 | admin.site.register(File, FileAdmin) 12 | -------------------------------------------------------------------------------- /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", "pastegraph.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /file_keeper/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class File(models.Model): 5 | hash = models.CharField(max_length=100, db_index=True, unique=True) 6 | # although sha224 uses only 56 characters 7 | 8 | mime = models.CharField(max_length=100) 9 | base64 = models.TextField() 10 | -------------------------------------------------------------------------------- /main_app/views/paste.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from main_app.models import Paste 3 | 4 | 5 | def home(request): 6 | return render(request, 'main.html', {}) 7 | 8 | 9 | def show_paste(request, link): 10 | paste = Paste.objects.get(link=link) 11 | return render(request, 'main.html', {'paste': paste}) 12 | -------------------------------------------------------------------------------- /main_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | urlpatterns = patterns( 4 | '', 5 | 6 | url(r'^$', 'main_app.views.paste.home', name='home'), 7 | 8 | # url(r'^paste/(?P[-_A-Za-z0-9]*)$', 'main_app.views.paste.show_paste', name='show_paste'), 9 | 10 | url(r'^plot/', 'main_app.views.ajax.plot', name='plot'), 11 | ) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pastegraph 2 | ========== 3 | 4 | pastegraph is a Django-based web application which provides a pastebin-like service to share graph plots done by Graphviz. It's mainly designed for ACM competitors and other people which sometimes want to visualize graph given by adjacency matrix or edges list. 5 | 6 | As for now, the application is being deployed to Heroku: http://pastegraph.herokuapp.com/ 7 | 8 | -------------------------------------------------------------------------------- /DEV-NOTES.md: -------------------------------------------------------------------------------- 1 | Migrations 2 | ---------- 3 | http://www.mikeball.us/blog/using-south-on-heroku-hosted-django-project 4 | 5 | PostgreSQL 6 | ---------- 7 | http://www.deploydjango.com/postgresql/ 8 | 9 | Social Auth 10 | ----------- 11 | http://adw0rd.com/2013/2/27/django-social-auth/#.UclzRptlVAY 12 | 13 | Static Files 14 | ------------ 15 | http://matthewphiong.com/managing-django-static-files-on-heroku 16 | 17 | Actual working [Procfile](../master/Procfile) differs: see this repo 18 | 19 | Zen 20 | --- 21 | http://www.12factor.net/ -------------------------------------------------------------------------------- /graphviz_restful_service/README.md: -------------------------------------------------------------------------------- 1 | Graphviz RESTful service 2 | ======================== 3 | 4 | Run the script `graphviz_service.py` on your server. 5 | Send the POST request to this script with the following parameters: 6 | - `description`: description of a graph in a graphviz format 7 | - `graphviz_utility`: one of {`dot`, `neato`} 8 | - `image_type`: one of {`png`, `svg`} 9 | 10 | The script sends two parameters in response: 11 | - `mime`: mime-type of an image 12 | - `base64`: content of output image represented by base64-encoded string -------------------------------------------------------------------------------- /main_app/static/third_party_gd/REVIEW.md: -------------------------------------------------------------------------------- 1 | Browser graph drawing tools 2 | =========================== 3 | 4 | - http://philogb.github.io/jit/demos.html 5 | 6 | Several types of graph drawing methods 7 | 8 | Interest: +++ 9 | 10 | - http://d3js.org/ 11 | 12 | Force-based label placement 13 | 14 | Interest: ++ 15 | 16 | - https://github.com/anvaka/VivaGraphJS 17 | 18 | JavaScript library 19 | 20 | Interest: ++ 21 | 22 | - http://www.graphdracula.net/ 23 | 24 | JavaScript Graph Library 25 | 26 | Interest: ++ 27 | 28 | - http://getspringy.com/ 29 | 30 | JavaScript Graph Library 31 | 32 | Interest: ++ 33 | 34 | - https://code.google.com/p/canviz/ 35 | 36 | JavaScript xdot renderer 37 | 38 | Interest: + 39 | -------------------------------------------------------------------------------- /main_app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | from utils import Graph 4 | 5 | 6 | class UtilsTestCase(TestCase): 7 | def test_directed_graph_description(self): 8 | graph_source = '''0 1 0 9 | 1 0 1 10 | 0 0 0''' 11 | graph_options = {'source_type': 'adjacency_matrix', 'directed': 'true'} 12 | 13 | expected_description = '''graph 14 | G { 15 | fontsize = 4.0; 16 | ratio = auto; 17 | 1 [shape = circle, height=.1, width=.1]; 18 | 2 [shape = circle, height=.1, width=.1]; 19 | 3 [shape = circle, height=.1, width=.1]; 20 | 2 -> 1 [ label = "" ]; 21 | 2 -> 1 [ label = "" ]; 22 | 2 -> 1 [ label = "" ]; 23 | }''' 24 | self.assertEqual(Graph(graph_source, graph_options).to_graphviz_description(), expected_description) 25 | -------------------------------------------------------------------------------- /file_keeper/__init__.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import pickle 3 | import mimetypes 4 | 5 | from models import File 6 | 7 | 8 | def file_keeper_hash(mime, base64): 9 | print(mime, mimetypes.guess_extension(mime)) 10 | return hashlib.sha224(pickle.dumps((mime, base64))).hexdigest() + mimetypes.guess_extension(mime) 11 | 12 | 13 | def save_file(mime, base64): 14 | ''' 15 | Calculate hash of the file, look up in the database, 16 | save if it's a new one and return its hash. The hash 17 | can the be used as URL. 18 | ''' 19 | hash_ = file_keeper_hash(mime, base64) 20 | objects = File.objects.filter(hash=hash_) 21 | if not objects: 22 | file_ = File(hash=hash_, mime=mime, base64=base64) 23 | file_.save() 24 | return hash_ 25 | -------------------------------------------------------------------------------- /pastegraph/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | from django.contrib import admin 4 | admin.autodiscover() 5 | 6 | import settings 7 | 8 | 9 | urlpatterns = patterns('', 10 | # Examples: 11 | # url(r'^$', 'pastegraph.views.home', name='home'), 12 | # url(r'^pastegraph/', include('pastegraph.foo.urls')), 13 | 14 | # Uncomment the admin/doc line below to enable admin documentation: 15 | url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 16 | 17 | # Uncomment the next line to enable the admin: 18 | url(r'^admin/', include(admin.site.urls)), 19 | 20 | url(r'^file/', include('file_keeper.urls')), 21 | 22 | url(r'^', include('main_app.urls')), 23 | ) 24 | 25 | urlpatterns += patterns('', 26 | (r'^static/(?P.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}), 27 | ) 28 | -------------------------------------------------------------------------------- /main_app/views/ajax.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.http import HttpResponse 4 | from django.views.decorators.http import require_POST 5 | 6 | from main_app.utils import Graphviz 7 | from file_keeper import save_file 8 | 9 | 10 | GRAPH_OPTIONS = ('directed', 'source_type',) 11 | PLOT_OPTIONS = ('graphviz_utility', 'image_type',) 12 | GRAPH_SOURCE_LIMIT = 1000 13 | 14 | 15 | @require_POST 16 | def plot(request): 17 | # ajax request 18 | # params: graph_source, graph_options, plot_options 19 | graph_source = request.POST['graph_source'] 20 | graph_options = {key: request.POST[key] for key in GRAPH_OPTIONS} 21 | plot_options = {key: request.POST[key] for key in PLOT_OPTIONS} 22 | 23 | if len(graph_source) > GRAPH_SOURCE_LIMIT: 24 | return HttpResponse(status=413) 25 | 26 | mime, base64 = Graphviz.render(graph_source, graph_options, plot_options) 27 | hash_ = save_file(mime, base64) 28 | data = {'image_hash': hash_} 29 | return HttpResponse(json.dumps(data), mimetype="application/json") 30 | -------------------------------------------------------------------------------- /main_app/static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-bottom: 10px; 3 | } 4 | 5 | #ajax-loader { 6 | padding: 1em 1em 1em 1em; 7 | } 8 | 9 | #error { 10 | display: none; 11 | } 12 | 13 | #source-textarea { 14 | height: 10em; 15 | font-family: Consolas, "Liberation Mono", Courier, monospace; 16 | } 17 | 18 | #source-hint { 19 | color: gray; 20 | } 21 | 22 | #source-hint-clickable { 23 | border-bottom: 1px dotted gray; 24 | } 25 | 26 | .ajax-loader-rendering { 27 | /*background-color: lightyellow;*/ 28 | } 29 | 30 | .ajax-loader-loading { 31 | /*background-color: lightgreen;*/ 32 | } 33 | 34 | .source-type-choice { 35 | padding: 3px; 36 | } 37 | 38 | .source-type-choice:hover { 39 | background-color: #fff6f0; 40 | } 41 | 42 | .source-type-choice:hover:not(.source-type-selected) { 43 | border: 1px solid #ff7518; /* #ff7518 */ 44 | } 45 | 46 | .source-type-choice.source-type-selected { 47 | border: 1px solid #ff7518; /* #ff7518 */ 48 | color: #ff7518; 49 | } 50 | 51 | .source-type-choice:not(.source-type-selected) { 52 | border-bottom: 1px dashed orange; 53 | color: black; 54 | } 55 | 56 | /*.source-type-choice:not(:last-of-type) { 57 | margin-right: 3em; 58 | }*/ 59 | 60 | .source-type-choice:first-of-type { 61 | float: left; 62 | } 63 | 64 | .source-type-choice:last-of-type { 65 | float: right; 66 | } 67 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.alerts.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.alerts = { 7 | name : 'alerts', 8 | 9 | version : '4.2.2', 10 | 11 | settings : { 12 | speed: 300, // fade out speed 13 | callback: function (){} 14 | }, 15 | 16 | init : function (scope, method, options) { 17 | this.scope = scope || this.scope; 18 | 19 | if (typeof method === 'object') { 20 | $.extend(true, this.settings, method); 21 | } 22 | 23 | if (typeof method !== 'string') { 24 | if (!this.settings.init) { this.events(); } 25 | 26 | return this.settings.init; 27 | } else { 28 | return this[method].call(this, options); 29 | } 30 | }, 31 | 32 | events : function () { 33 | var self = this; 34 | 35 | $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) { 36 | e.preventDefault(); 37 | $(this).closest("[data-alert]").fadeOut(self.speed, function () { 38 | $(this).remove(); 39 | self.settings.callback(); 40 | }); 41 | }); 42 | 43 | this.settings.init = true; 44 | }, 45 | 46 | off : function () { 47 | $(this.scope).off('.fndtn.alerts'); 48 | }, 49 | 50 | reflow : function () {} 51 | }; 52 | }(Foundation.zj, this, this.document)); 53 | -------------------------------------------------------------------------------- /pastegraph/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for pastegraph 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"] = "pastegraph.settings" 22 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pastegraph.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 | application = get_wsgi_application() 29 | 30 | # Apply WSGI middleware here. 31 | # from helloworld.wsgi import HelloWorldApplication 32 | # application = HelloWorldApplication(application) 33 | -------------------------------------------------------------------------------- /graphviz_restful_service/graphviz_service.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import base64 4 | import subprocess 5 | import json 6 | import http.server 7 | import socketserver 8 | import mimetypes 9 | 10 | PORT = 9999 11 | ALLOWED_GRAPHVIZ_UTILITIES = ['dot', 'neato'] 12 | 13 | 14 | def graphviz_process(params): 15 | if params['graphviz_utility'] not in ALLOWED_GRAPHVIZ_UTILITIES: 16 | raise ValueError('Unsupported Graphviz utility "{0}"'.format(params['graphviz_utility'])) 17 | process = subprocess.Popen(args=[params['graphviz_utility'], 18 | '-T{0}'.format(params['image_type'])], 19 | stdin=subprocess.PIPE, 20 | stdout=subprocess.PIPE, 21 | stderr=subprocess.PIPE) 22 | output, errors = process.communicate(params['description'].encode('ascii')) 23 | mime = mimetypes.guess_type('a.{0}'.format(params['image_type']))[0] 24 | return mime, base64.b64encode(output).decode('ascii'), errors 25 | 26 | 27 | class GraphvizHandler(http.server.BaseHTTPRequestHandler): 28 | def do_POST(self): 29 | self.log_message('received POST from {0}:{1}'.format(*self.client_address)) 30 | input_data = self.rfile.read(int(self.headers.get('Content-Length'))).decode('utf-8') 31 | self.log_message('json: {0}'.format(input_data)) 32 | params = json.loads(input_data) 33 | mime, base64_output, errors = graphviz_process(params) 34 | 35 | if errors: 36 | error_message = 'Errors during graphviz execution: {0}'.format(errors.decode('ascii')) 37 | self.send_response(400, error_message) 38 | self.log_message(error_message) 39 | else: 40 | self.send_response(200) 41 | self.send_header('Content-type', 'text/html') 42 | self.end_headers() 43 | 44 | data = {'mime': mime, 'base64': base64_output} 45 | output = json.dumps(data) 46 | self.wfile.write(output.encode('utf-8')) 47 | self.log_message(output) 48 | 49 | 50 | socketserver.TCPServer.allow_reuse_address = True 51 | httpd = socketserver.TCPServer(("", PORT), GraphvizHandler) 52 | 53 | print("serving at port", PORT) 54 | httpd.serve_forever() 55 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.3 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2011, Klaus Hartl 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.opensource.org/licenses/GPL-2.0 9 | * 10 | * Modified to work with Zepto.js by ZURB 11 | */ 12 | (function ($, document, undefined) { 13 | 14 | var pluses = /\+/g; 15 | 16 | function raw(s) { 17 | return s; 18 | } 19 | 20 | function decoded(s) { 21 | return decodeURIComponent(s.replace(pluses, ' ')); 22 | } 23 | 24 | var config = $.cookie = function (key, value, options) { 25 | 26 | // write 27 | if (value !== undefined) { 28 | options = $.extend({}, config.defaults, options); 29 | 30 | if (value === null) { 31 | options.expires = -1; 32 | } 33 | 34 | if (typeof options.expires === 'number') { 35 | var days = options.expires, t = options.expires = new Date(); 36 | t.setDate(t.getDate() + days); 37 | } 38 | 39 | value = config.json ? JSON.stringify(value) : String(value); 40 | 41 | return (document.cookie = [ 42 | encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), 43 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 44 | options.path ? '; path=' + options.path : '', 45 | options.domain ? '; domain=' + options.domain : '', 46 | options.secure ? '; secure' : '' 47 | ].join('')); 48 | } 49 | 50 | // read 51 | var decode = config.raw ? raw : decoded; 52 | var cookies = document.cookie.split('; '); 53 | for (var i = 0, l = cookies.length; i < l; i++) { 54 | var parts = cookies[i].split('='); 55 | if (decode(parts.shift()) === key) { 56 | var cookie = decode(parts.join('=')); 57 | return config.json ? JSON.parse(cookie) : cookie; 58 | } 59 | } 60 | 61 | return null; 62 | }; 63 | 64 | config.defaults = {}; 65 | 66 | $.removeCookie = function (key, options) { 67 | if ($.cookie(key) !== null) { 68 | $.cookie(key, null, options); 69 | return true; 70 | } 71 | return false; 72 | }; 73 | 74 | })(Foundation.zj, document); 75 | -------------------------------------------------------------------------------- /main_app/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | Pastegraph - {% block title %}plot graphs{% endblock %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Fork me on GitHub 28 | 29 |
30 |
31 |

Pastegraph – plot graphs

32 |
33 |
34 | 35 |
36 | {% block content %} 37 | {% endblock %} 38 |
39 | 40 | 45 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /main_app/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load staticfiles %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 | {% csrf_token %} 11 |
12 | Graph source 13 |
14 | 19 |
20 | adjacency matrix 21 | edge list 22 | 23 |
24 | 25 | directional 26 | 27 | 28 |
29 | 30 | 31 | 32 | 34 | 35 | 36 |
37 |
38 | 39 |
40 |
41 |
42 |

Springy

43 |
44 |
45 | 46 |
47 |
48 |
49 | 50 |
51 |

Graphviz

52 |
53 |
54 | 55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 |
68 |
69 | 70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /main_app/utils.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import json 3 | 4 | import requests 5 | 6 | from django.conf import settings 7 | 8 | 9 | def convert_into_numbers(s): 10 | lines = s.strip().split('\n') 11 | return [[int(word) for word in line.split()] for line in lines] 12 | 13 | 14 | def to_bool(json_data): 15 | if json_data in ['true', 'false']: 16 | return eval(json_data.capitalize()) 17 | else: 18 | return bool(eval(json_data)) 19 | 20 | 21 | class Graph: 22 | ''' 23 | graph_options: 24 | directed: bool 25 | source_type in ['adjacency_matrix', 'edges_list'] 26 | ''' 27 | 28 | def __init__(self, graph_source, graph_options={}): 29 | self.graph_source = graph_source 30 | self.graph_options = graph_options 31 | self.edges = collections.defaultdict(lambda: set()) 32 | self._recognize_graph() 33 | 34 | def _recognize_graph(self): 35 | self.raw_numbers = convert_into_numbers(self.graph_source) 36 | source_type = self.graph_options['source_type'] 37 | if source_type == 'adjacency_matrix': 38 | self._from_adjacency_matrix() 39 | elif source_type == 'edges_list': 40 | self._from_edges_list() 41 | else: 42 | raise ValueError('Unknown source_type "{0}"'.format(source_type)) 43 | self.directed = to_bool(self.graph_options['directed']) 44 | 45 | def _from_adjacency_matrix(self): 46 | lines = self.raw_numbers 47 | self.num_vertices = len(lines) 48 | self.vertices = list(range(1, self.num_vertices + 1)) 49 | assert all(self.num_vertices == len(line) for line in lines) 50 | for i in range(self.num_vertices): 51 | for j in range(self.num_vertices): 52 | if lines[i][j]: 53 | self.add_edge(i + 1, j + 1) 54 | 55 | def _from_edges_list(self): 56 | lines = self.raw_numbers 57 | min_vertex = min(min(line) for line in lines) 58 | max_vertex = max(max(line) for line in lines) 59 | self.num_vertices = max_vertex - min_vertex + 1 60 | self.vertices = list(range(min_vertex, max_vertex + 1)) 61 | for u, v in lines: 62 | self.add_edge(u, v) 63 | 64 | def add_edge(self, i, j): 65 | self.edges[i].add(j) 66 | 67 | def to_graphviz_description(self): 68 | description = [] 69 | description.append('digraph' if self.directed else 'graph') 70 | description.append('''G { 71 | fontsize = 4.0; 72 | ratio = auto;''') 73 | for v in self.vertices: 74 | description.append('{0} [shape = circle, height=.1, width=.1];'.format(v)) 75 | already_added = set() 76 | for v in self.edges: 77 | for w in self.edges[v]: 78 | pair = (min(v, w), max(v, w)) 79 | if self.directed or pair not in already_added: 80 | description.append('{0} {2} {1} [ label = "" ];'.format( 81 | v, w, '->' if self.directed else '--')) 82 | already_added.add(pair) 83 | description.append('}') 84 | return '\n'.join(description) 85 | 86 | 87 | class Graphviz: 88 | @classmethod 89 | def render(cls, graph_source, graph_options, plot_options): 90 | ''' 91 | Return a tuple (mime-type, base-64-encoded-file) 92 | ''' 93 | description = Graph(graph_source, graph_options).to_graphviz_description() 94 | graphviz_utility = plot_options['graphviz_utility'] 95 | image_type = plot_options['image_type'] 96 | assert graphviz_utility in ['dot', 'neato'] 97 | params = {'description': description, 98 | 'graphviz_utility': graphviz_utility, 99 | 'image_type': image_type} 100 | r = requests.post(settings.GRAPHVIZ_URL, data=json.dumps(params), timeout=20).json() 101 | return r['mime'], r['base64'] 102 | -------------------------------------------------------------------------------- /main_app/static/js/graph_utils.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | function to_2d_array(lines) { 3 | return jQuery.map(lines.split("\n"), function(line) { 4 | return [$.trim(line).split(' ')]; 5 | }); 6 | } 7 | 8 | build_graph = function() { 9 | // Build graph from #source using #graph-options. 10 | // Return instance of Springy.Graph class. 11 | 12 | var graph = new Springy.Graph(); 13 | var lines = to_2d_array($.trim($('#source-textarea').val())); 14 | var directed = $('input[name=directional]').prop('checked'); 15 | 16 | if ($('#source-type-to-adjacency-matrix').hasClass('source-type-selected')) { 17 | var min_number = 1; 18 | var num_vertices = lines.length; 19 | 20 | for (var i = min_number; i < min_number + num_vertices; ++i) { 21 | graph.addNodes(i); 22 | } 23 | 24 | for (var i = 0; i < num_vertices; ++i) { 25 | if (lines[i].length != num_vertices) { 26 | js_error('Invalid amount of numbers at line ' + (i + 1) + 27 | ': expected ' + num_vertices + ', got ' + 28 | lines[i].length); 29 | return undefined; 30 | } 31 | for (var j = 0; j < num_vertices; ++j) { 32 | if (lines[i][j] != 0) { 33 | graph.addEdges([min_number + i, min_number + j, {directional: directed}]); 34 | } 35 | } 36 | } 37 | } else { 38 | for (var i in lines) { 39 | if (lines[i].length != 2) { 40 | js_error('Invalid amount of numbers at line ' + (parseInt(i, 10) + 1) + 41 | ': expected 2, got ' + 42 | lines[i].length); 43 | return undefined; 44 | } 45 | if (!(lines[i][0] in graph.nodeSet)) { 46 | graph.addNodes(lines[i][0]); 47 | } 48 | if (!(lines[i][1] in graph.nodeSet)) { 49 | graph.addNodes(lines[i][1]); 50 | } 51 | graph.addEdges([lines[i][0], lines[i][1], {directional: directed}]); 52 | } 53 | } 54 | 55 | return graph; 56 | } 57 | 58 | function enumerate_vertices(graph) { 59 | var enumeration = {}; 60 | var num_vertices = 0; 61 | 62 | for (var node_index in graph.nodes) { 63 | var node = graph.nodes[node_index]; 64 | enumeration[node.id] = num_vertices++; 65 | } 66 | 67 | return enumeration; 68 | } 69 | 70 | graph_to_adjacency_matrix = function(graph) { 71 | // Convert Springy.Graph instance to string 72 | // describing it. 73 | 74 | var enumeration = enumerate_vertices(graph); 75 | var num_vertices = graph.nodes.length; 76 | 77 | var matrix = []; 78 | for (var i = 0; i < num_vertices; ++i) { 79 | matrix[i] = []; 80 | for (var j = 0; j < num_vertices; ++j) { 81 | matrix[i][j] = 0; 82 | } 83 | } 84 | 85 | for (var edge_index in graph.edges) { 86 | var edge = graph.edges[edge_index]; 87 | matrix[enumeration[edge.source.id]][enumeration[edge.target.id]] = 1; 88 | if (edge.data['directional'] === 'false') { 89 | matrix[enumeration[edge.target.id]][enumeration[edge.source.id]] = 1; 90 | } 91 | } 92 | 93 | var lines = []; 94 | for (var row_id in matrix) { 95 | lines.push(matrix[row_id].join(' ')); 96 | } 97 | 98 | return lines.join('\n'); 99 | } 100 | 101 | graph_to_edge_list = function(graph) { 102 | var enumeration = enumerate_vertices(graph); 103 | var edge_list = []; 104 | 105 | for (var edge_index in graph.edges) { 106 | var edge = graph.edges[edge_index]; 107 | edge_list.push((enumeration[edge.source.id] + 1) + " " + (enumeration[edge.target.id] + 1)); 108 | } 109 | 110 | return edge_list.join('\n'); 111 | } 112 | }) -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.magellan.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.magellan = { 7 | name : 'magellan', 8 | 9 | version : '4.2.2', 10 | 11 | settings : { 12 | activeClass: 'active' 13 | }, 14 | 15 | init : function (scope, method, options) { 16 | this.scope = scope || this.scope; 17 | Foundation.inherit(this, 'data_options'); 18 | 19 | if (typeof method === 'object') { 20 | $.extend(true, this.settings, method); 21 | } 22 | 23 | if (typeof method !== 'string') { 24 | if (!this.settings.init) { 25 | this.fixed_magellan = $("[data-magellan-expedition]"); 26 | this.set_threshold(); 27 | this.last_destination = $('[data-magellan-destination]').last(); 28 | this.events(); 29 | } 30 | 31 | return this.settings.init; 32 | } else { 33 | return this[method].call(this, options); 34 | } 35 | }, 36 | 37 | events : function () { 38 | var self = this; 39 | $(this.scope).on('arrival.fndtn.magellan', '[data-magellan-arrival]', function (e) { 40 | var $destination = $(this), 41 | $expedition = $destination.closest('[data-magellan-expedition]'), 42 | activeClass = $expedition.attr('data-magellan-active-class') 43 | || self.settings.activeClass; 44 | 45 | $destination 46 | .closest('[data-magellan-expedition]') 47 | .find('[data-magellan-arrival]') 48 | .not($destination) 49 | .removeClass(activeClass); 50 | $destination.addClass(activeClass); 51 | }); 52 | 53 | this.fixed_magellan 54 | .on('update-position.fndtn.magellan', function(){ 55 | var $el = $(this); 56 | // $el.data("magellan-fixed-position",""); 57 | // $el.data("magellan-top-offset", ""); 58 | }) 59 | .trigger('update-position'); 60 | 61 | $(window) 62 | .on('resize.fndtn.magellan', function() { 63 | this.fixed_magellan.trigger('update-position'); 64 | }.bind(this)) 65 | 66 | .on('scroll.fndtn.magellan', function() { 67 | var windowScrollTop = $(window).scrollTop(); 68 | self.fixed_magellan.each(function() { 69 | var $expedition = $(this); 70 | if (typeof $expedition.data('magellan-top-offset') === 'undefined') { 71 | $expedition.data('magellan-top-offset', $expedition.offset().top); 72 | } 73 | if (typeof $expedition.data('magellan-fixed-position') === 'undefined') { 74 | $expedition.data('magellan-fixed-position', false) 75 | } 76 | var fixed_position = (windowScrollTop + self.settings.threshold) > $expedition.data("magellan-top-offset"); 77 | var attr = $expedition.attr('data-magellan-top-offset'); 78 | 79 | if ($expedition.data("magellan-fixed-position") != fixed_position) { 80 | $expedition.data("magellan-fixed-position", fixed_position); 81 | if (fixed_position) { 82 | $expedition.addClass('fixed'); 83 | $expedition.css({position:"fixed", top:0}); 84 | } else { 85 | $expedition.removeClass('fixed'); 86 | $expedition.css({position:"", top:""}); 87 | } 88 | if (fixed_position && typeof attr != 'undefined' && attr != false) { 89 | $expedition.css({position:"fixed", top:attr + "px"}); 90 | } 91 | } 92 | }); 93 | }); 94 | 95 | 96 | if (this.last_destination.length > 0) { 97 | $(window).on('scroll.fndtn.magellan', function (e) { 98 | var windowScrollTop = $(window).scrollTop(), 99 | scrolltopPlusHeight = windowScrollTop + $(window).height(), 100 | lastDestinationTop = Math.ceil(self.last_destination.offset().top); 101 | 102 | $('[data-magellan-destination]').each(function () { 103 | var $destination = $(this), 104 | destination_name = $destination.attr('data-magellan-destination'), 105 | topOffset = $destination.offset().top - windowScrollTop; 106 | 107 | if (topOffset <= self.settings.threshold) { 108 | $("[data-magellan-arrival='" + destination_name + "']").trigger('arrival'); 109 | } 110 | // In large screens we may hit the bottom of the page and dont reach the top of the last magellan-destination, so lets force it 111 | if (scrolltopPlusHeight >= $(self.scope).height() && lastDestinationTop > windowScrollTop && lastDestinationTop < scrolltopPlusHeight) { 112 | $('[data-magellan-arrival]').last().trigger('arrival'); 113 | } 114 | }); 115 | }); 116 | } 117 | 118 | this.settings.init = true; 119 | }, 120 | 121 | set_threshold : function () { 122 | if (!this.settings.threshold) { 123 | this.settings.threshold = (this.fixed_magellan.length > 0) ? 124 | this.outerHeight(this.fixed_magellan, true) : 0; 125 | } 126 | }, 127 | 128 | off : function () { 129 | $(this.scope).off('.fndtn.magellan'); 130 | }, 131 | 132 | reflow : function () {} 133 | }; 134 | }(Foundation.zj, this, this.document)); 135 | -------------------------------------------------------------------------------- /main_app/static/js/main_ui.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | var current_plot_method = undefined; 4 | 5 | // famous Ajax setup for Django CSRF Protection 6 | $(function(){ 7 | $.ajaxSetup({ 8 | beforeSend: function(xhr, settings) { 9 | function getCookie(name) { 10 | var cookieValue = null; 11 | if (document.cookie && document.cookie != '') { 12 | var cookies = document.cookie.split(';'); 13 | for (var i = 0; i < cookies.length; i++) { 14 | var cookie = jQuery.trim(cookies[i]); 15 | // Does this cookie string begin with the name we want? 16 | if (cookie.substring(0, name.length + 1) == (name + '=')) { 17 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 18 | break; 19 | } 20 | } 21 | } 22 | return cookieValue; 23 | } 24 | if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { 25 | // Only send the token to relative URLs i.e. locally. 26 | xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); 27 | } 28 | } 29 | }); 30 | }); 31 | 32 | function handle_error(action, jqXHR, textStatus, errorThrown) { 33 | $('#error').html($('
').html('An error occured during ' + action + ': 
' + jqXHR.responseText)); 34 | $('#error').show(); 35 | } 36 | 37 | js_error = function(report) { 38 | $('#error').text(report); 39 | $('#error').show(); 40 | } 41 | 42 | function clean_errors() { 43 | $('#error').hide(); 44 | $('#error').html(''); 45 | } 46 | 47 | function switch_to_method(name) { 48 | clean_errors(); 49 | } 50 | 51 | function light_update() { 52 | clean_errors(); 53 | 54 | if (current_plot_method == 'springy-simple') { 55 | $('#springy-simple-link').click(); 56 | } 57 | } 58 | 59 | function hard_update() { 60 | clean_errors(); 61 | 62 | $('#' + current_plot_method + '-link').click(); 63 | } 64 | 65 | $('#graphviz-link').click(function() { 66 | clean_errors(); 67 | 68 | current_plot_method = 'graphviz'; 69 | 70 | $('#graphviz-plot-result').removeClass('ajax-loader-rendering'); 71 | $('#graphviz-plot-result').removeClass('ajax-loader-loading'); 72 | 73 | $('#graphviz-plot-img').hide(); 74 | $('#ajax-loader').show(); 75 | $('#graphviz-plot-result').addClass('ajax-loader-rendering'); 76 | // $('#plot').hide(); 77 | $.post('plot/', $('#graph-form').serialize()) 78 | .done(function(data, textStatus, jqXHR) { 79 | // $('#plot').html($('').attr('src', 'file/get/' + data['image_hash'])); 80 | $('#graphviz-plot-result').removeClass('ajax-loader-rendering'); 81 | $('#graphviz-plot-result').addClass('ajax-loader-loading'); 82 | $('#graphviz-plot-img').attr('src', 'file/get/' + data['image_hash']).load(function() { 83 | $('#graphviz-plot-result').removeClass('ajax-loader-loading'); 84 | $('#ajax-loader').hide(); 85 | $('#graphviz-plot-img').show(); 86 | }); 87 | }) 88 | .fail(function(jqXHR, textStatus, errorThrown) { 89 | $('#ajax-loader').hide(); 90 | handle_error('sending graph data to plot', jqXHR, textStatus, errorThrown); 91 | }) 92 | }) 93 | 94 | $('#springy-simple-link').click(function() { 95 | clean_errors(); 96 | 97 | current_plot_method = 'springy-simple'; 98 | 99 | var graph = build_graph(); 100 | if (graph !== undefined) { 101 | var canvas = $('') 102 | $('#springy-simple-plot-result').html(canvas); 103 | canvas.springy({graph: graph}); 104 | } 105 | }); 106 | 107 | $('#source-type-to-adjacency-matrix').click(function() { 108 | clean_errors(); 109 | 110 | if (!$('#source-type-to-adjacency-matrix').hasClass('source-type-selected')) { 111 | $('#source-textarea').val(graph_to_adjacency_matrix(build_graph())); 112 | } 113 | 114 | $('.source-type-choice').removeClass('source-type-selected'); 115 | $('#source-type-to-adjacency-matrix').addClass('source-type-selected'); 116 | 117 | $('input[name=source_type]').val('adjacency_matrix'); 118 | 119 | return false; 120 | }); 121 | 122 | $('#source-type-to-edge-list').click(function() { 123 | clean_errors(); 124 | 125 | if (!$('#source-type-to-edge-list').hasClass('source-type-selected')) { 126 | $('#source-textarea').val(graph_to_edge_list(build_graph())); 127 | } 128 | 129 | $('.source-type-choice').removeClass('source-type-selected'); 130 | $('#source-type-to-edge-list').addClass('source-type-selected'); 131 | 132 | $('input[name=source_type]').val('edges_list'); 133 | 134 | return false; 135 | }); 136 | 137 | $('input[name=directional]').click(function() { 138 | $('input[name=directed]').val($('input[name=directional]').prop('checked')); 139 | }); 140 | 141 | $('#source-textarea').keyup(function() { 142 | light_update(); 143 | }); 144 | 145 | $('#directional-checkbox').click(function() { 146 | hard_update(); 147 | }); 148 | 149 | // perform on start 150 | 151 | $('#springy-simple-link').click(); 152 | }) -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.placeholder.js: -------------------------------------------------------------------------------- 1 | /*! http://mths.be/placeholder v2.0.7 by @mathias 2 | Modified to work with Zepto.js by ZURB 3 | */ 4 | ;(function(window, document, $) { 5 | 6 | var isInputSupported = 'placeholder' in document.createElement('input'), 7 | isTextareaSupported = 'placeholder' in document.createElement('textarea'), 8 | prototype = $.fn, 9 | valHooks = $.valHooks, 10 | hooks, 11 | placeholder; 12 | 13 | if (isInputSupported && isTextareaSupported) { 14 | 15 | placeholder = prototype.placeholder = function() { 16 | return this; 17 | }; 18 | 19 | placeholder.input = placeholder.textarea = true; 20 | 21 | } else { 22 | 23 | placeholder = prototype.placeholder = function() { 24 | var $this = this; 25 | $this 26 | .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') 27 | .not('.placeholder') 28 | .bind({ 29 | 'focus.placeholder': clearPlaceholder, 30 | 'blur.placeholder': setPlaceholder 31 | }) 32 | .data('placeholder-enabled', true) 33 | .trigger('blur.placeholder'); 34 | return $this; 35 | }; 36 | 37 | placeholder.input = isInputSupported; 38 | placeholder.textarea = isTextareaSupported; 39 | 40 | hooks = { 41 | 'get': function(element) { 42 | var $element = $(element); 43 | return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; 44 | }, 45 | 'set': function(element, value) { 46 | var $element = $(element); 47 | if (!$element.data('placeholder-enabled')) { 48 | return element.value = value; 49 | } 50 | if (value == '') { 51 | element.value = value; 52 | // Issue #56: Setting the placeholder causes problems if the element continues to have focus. 53 | if (element != document.activeElement) { 54 | // We can't use `triggerHandler` here because of dummy text/password inputs :( 55 | setPlaceholder.call(element); 56 | } 57 | } else if ($element.hasClass('placeholder')) { 58 | clearPlaceholder.call(element, true, value) || (element.value = value); 59 | } else { 60 | element.value = value; 61 | } 62 | // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 63 | return $element; 64 | } 65 | }; 66 | 67 | isInputSupported || (valHooks.input = hooks); 68 | isTextareaSupported || (valHooks.textarea = hooks); 69 | 70 | $(function() { 71 | // Look for forms 72 | $(document).delegate('form', 'submit.placeholder', function() { 73 | // Clear the placeholder values so they don't get submitted 74 | var $inputs = $('.placeholder', this).each(clearPlaceholder); 75 | setTimeout(function() { 76 | $inputs.each(setPlaceholder); 77 | }, 10); 78 | }); 79 | }); 80 | 81 | // Clear placeholder values upon page reload 82 | $(window).bind('beforeunload.placeholder', function() { 83 | $('.placeholder').each(function() { 84 | this.value = ''; 85 | }); 86 | }); 87 | 88 | } 89 | 90 | function args(elem) { 91 | // Return an object of element attributes 92 | var newAttrs = {}, 93 | rinlinejQuery = /^jQuery\d+$/; 94 | $.each(elem.attributes, function(i, attr) { 95 | if (attr.specified && !rinlinejQuery.test(attr.name)) { 96 | newAttrs[attr.name] = attr.value; 97 | } 98 | }); 99 | return newAttrs; 100 | } 101 | 102 | function clearPlaceholder(event, value) { 103 | var input = this, 104 | $input = $(input); 105 | if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { 106 | if ($input.data('placeholder-password')) { 107 | $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); 108 | // If `clearPlaceholder` was called from `$.valHooks.input.set` 109 | if (event === true) { 110 | return $input[0].value = value; 111 | } 112 | $input.focus(); 113 | } else { 114 | input.value = ''; 115 | $input.removeClass('placeholder'); 116 | input == document.activeElement && input.select(); 117 | } 118 | } 119 | } 120 | 121 | function setPlaceholder() { 122 | var $replacement, 123 | input = this, 124 | $input = $(input), 125 | $origInput = $input, 126 | id = this.id; 127 | if (input.value == '') { 128 | if (input.type == 'password') { 129 | if (!$input.data('placeholder-textinput')) { 130 | try { 131 | $replacement = $input.clone().attr({ 'type': 'text' }); 132 | } catch(e) { 133 | $replacement = $('').attr($.extend(args(this), { 'type': 'text' })); 134 | } 135 | $replacement 136 | .removeAttr('name') 137 | .data({ 138 | 'placeholder-password': true, 139 | 'placeholder-id': id 140 | }) 141 | .bind('focus.placeholder', clearPlaceholder); 142 | $input 143 | .data({ 144 | 'placeholder-textinput': $replacement, 145 | 'placeholder-id': id 146 | }) 147 | .before($replacement); 148 | } 149 | $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); 150 | // Note: `$input[0] != input` now! 151 | } 152 | $input.addClass('placeholder'); 153 | $input[0].value = $input.attr('placeholder'); 154 | } else { 155 | $input.removeClass('placeholder'); 156 | } 157 | } 158 | 159 | }(this, document, Foundation.zj)); 160 | 161 | ;(function ($, window, document, undefined) { 162 | 'use strict'; 163 | 164 | Foundation.libs.placeholder = { 165 | name : 'placeholder', 166 | 167 | version : '4.2.2', 168 | 169 | init : function (scope, method, options) { 170 | this.scope = scope || this.scope; 171 | 172 | if (typeof method !== 'string') { 173 | window.onload = function () { 174 | $('input, textarea').placeholder(); 175 | } 176 | } 177 | } 178 | }; 179 | }(Foundation.zj, this, this.document)); 180 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.dropdown.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.dropdown = { 7 | name : 'dropdown', 8 | 9 | version : '4.2.0', 10 | 11 | settings : { 12 | activeClass: 'open', 13 | is_hover: false, 14 | opened: function(){}, 15 | closed: function(){} 16 | }, 17 | 18 | init : function (scope, method, options) { 19 | this.scope = scope || this.scope; 20 | Foundation.inherit(this, 'throttle scrollLeft data_options'); 21 | 22 | if (typeof method === 'object') { 23 | $.extend(true, this.settings, method); 24 | } 25 | 26 | if (typeof method !== 'string') { 27 | 28 | if (!this.settings.init) { 29 | this.events(); 30 | } 31 | 32 | return this.settings.init; 33 | } else { 34 | return this[method].call(this, options); 35 | } 36 | }, 37 | 38 | events : function () { 39 | var self = this; 40 | 41 | $(this.scope) 42 | .on('click.fndtn.dropdown', '[data-dropdown]', function (e) { 43 | var settings = $.extend({}, self.settings, self.data_options($(this))); 44 | e.preventDefault(); 45 | 46 | if (!settings.is_hover) self.toggle($(this)); 47 | }) 48 | .on('mouseenter', '[data-dropdown]', function (e) { 49 | var settings = $.extend({}, self.settings, self.data_options($(this))); 50 | if (settings.is_hover) self.toggle($(this)); 51 | }) 52 | .on('mouseleave', '[data-dropdown-content]', function (e) { 53 | var target = $('[data-dropdown="' + $(this).attr('id') + '"]'), 54 | settings = $.extend({}, self.settings, self.data_options(target)); 55 | if (settings.is_hover) self.close.call(self, $(this)); 56 | }) 57 | .on('opened.fndtn.dropdown', '[data-dropdown-content]', this.settings.opened) 58 | .on('closed.fndtn.dropdown', '[data-dropdown-content]', this.settings.closed); 59 | 60 | $('body').on('click.fndtn.dropdown', function (e) { 61 | var parent = $(e.target).closest('[data-dropdown-content]'); 62 | 63 | if ($(e.target).data('dropdown')) { 64 | return; 65 | } 66 | if (parent.length > 0 && ($(e.target).is('[data-dropdown-content]') || $.contains(parent.first()[0], e.target))) { 67 | e.stopPropagation(); 68 | return; 69 | } 70 | 71 | self.close.call(self, $('[data-dropdown-content]')); 72 | }); 73 | 74 | $(window).on('resize.fndtn.dropdown', self.throttle(function () { 75 | self.resize.call(self); 76 | }, 50)).trigger('resize'); 77 | 78 | this.settings.init = true; 79 | }, 80 | 81 | close: function (dropdown) { 82 | var self = this; 83 | dropdown.each(function () { 84 | if ($(this).hasClass(self.settings.activeClass)) { 85 | $(this) 86 | .css(Foundation.rtl ? 'right':'left', '-99999px') 87 | .removeClass(self.settings.activeClass); 88 | $(this).trigger('closed'); 89 | } 90 | }); 91 | }, 92 | 93 | open: function (dropdown, target) { 94 | this 95 | .css(dropdown 96 | .addClass(this.settings.activeClass), target); 97 | dropdown.trigger('opened'); 98 | }, 99 | 100 | toggle : function (target) { 101 | var dropdown = $('#' + target.data('dropdown')); 102 | 103 | this.close.call(this, $('[data-dropdown-content]').not(dropdown)); 104 | 105 | if (dropdown.hasClass(this.settings.activeClass)) { 106 | this.close.call(this, dropdown); 107 | } else { 108 | this.close.call(this, $('[data-dropdown-content]')) 109 | this.open.call(this, dropdown, target); 110 | } 111 | }, 112 | 113 | resize : function () { 114 | var dropdown = $('[data-dropdown-content].open'), 115 | target = $("[data-dropdown='" + dropdown.attr('id') + "']"); 116 | 117 | if (dropdown.length && target.length) { 118 | this.css(dropdown, target); 119 | } 120 | }, 121 | 122 | css : function (dropdown, target) { 123 | var offset_parent = dropdown.offsetParent(); 124 | // temporary workaround until 4.2 125 | if (offset_parent.length > 0 && /body/i.test(dropdown.offsetParent()[0].nodeName)) { 126 | var position = target.offset(); 127 | position.top -= dropdown.offsetParent().offset().top; 128 | position.left -= dropdown.offsetParent().offset().left; 129 | } else { 130 | var position = target.position(); 131 | } 132 | 133 | if (this.small()) { 134 | dropdown.css({ 135 | position : 'absolute', 136 | width: '95%', 137 | left: '2.5%', 138 | 'max-width': 'none', 139 | top: position.top + this.outerHeight(target) 140 | }); 141 | } else { 142 | if (!Foundation.rtl && $(window).width() > this.outerWidth(dropdown) + target.offset().left) { 143 | var left = position.left; 144 | if (dropdown.hasClass('right')) { 145 | dropdown.removeClass('right'); 146 | } 147 | } else { 148 | if (!dropdown.hasClass('right')) { 149 | dropdown.addClass('right'); 150 | } 151 | var left = position.left - (this.outerWidth(dropdown) - this.outerWidth(target)); 152 | } 153 | 154 | dropdown.attr('style', '').css({ 155 | position : 'absolute', 156 | top: position.top + this.outerHeight(target), 157 | left: left 158 | }); 159 | } 160 | 161 | return dropdown; 162 | }, 163 | 164 | small : function () { 165 | return $(window).width() < 768 || $('html').hasClass('lt-ie9'); 166 | }, 167 | 168 | off: function () { 169 | $(this.scope).off('.fndtn.dropdown'); 170 | $('html, body').off('.fndtn.dropdown'); 171 | $(window).off('.fndtn.dropdown'); 172 | $('[data-dropdown-content]').off('.fndtn.dropdown'); 173 | this.settings.init = false; 174 | }, 175 | 176 | reflow : function () {} 177 | }; 178 | }(Foundation.zj, this, this.document)); 179 | -------------------------------------------------------------------------------- /pastegraph/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for pastegraph project. 2 | 3 | 4 | import os 5 | import sys 6 | 7 | 8 | PROJECT_ROOT = os.path.abspath( 9 | os.path.join(os.path.dirname(__file__), ".."), 10 | ) 11 | 12 | PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) 13 | 14 | 15 | DEBUG = eval(os.environ['DEBUG']) 16 | 17 | TEMPLATE_DEBUG = DEBUG 18 | 19 | ADMINS = ( 20 | # ('Your Name', 'your_email@example.com'), 21 | ('Vitaly Pavlenko', 'cxielamiko@gmail.com'), 22 | ) 23 | 24 | MANAGERS = ADMINS 25 | 26 | # Parse database configuration from $DATABASE_URL 27 | import dj_database_url 28 | DATABASES = {'default': dj_database_url.config()} 29 | 30 | # Honor the 'X-Forwarded-Proto' header for request.is_secure() 31 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 32 | 33 | # Hosts/domain names that are valid for this site; required if DEBUG is False 34 | # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts 35 | ALLOWED_HOSTS = ['pastegraph.herokuapp.com'] 36 | 37 | # Local time zone for this installation. Choices can be found here: 38 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 39 | # although not all choices may be available on all operating systems. 40 | # In a Windows environment this must be set to your system time zone. 41 | TIME_ZONE = 'America/Chicago' 42 | 43 | # Language code for this installation. All choices can be found here: 44 | # http://www.i18nguy.com/unicode/language-identifiers.html 45 | LANGUAGE_CODE = 'en-us' 46 | 47 | SITE_ID = 1 48 | 49 | # If you set this to False, Django will make some optimizations so as not 50 | # to load the internationalization machinery. 51 | USE_I18N = True 52 | 53 | # If you set this to False, Django will not format dates, numbers and 54 | # calendars according to the current locale. 55 | USE_L10N = True 56 | 57 | # If you set this to False, Django will not use timezone-aware datetimes. 58 | USE_TZ = True 59 | 60 | # Absolute filesystem path to the directory that will hold user-uploaded files. 61 | # Example: "/var/www/example.com/media/" 62 | MEDIA_ROOT = '' 63 | 64 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 65 | # trailing slash. 66 | # Examples: "http://example.com/media/", "http://media.example.com/" 67 | MEDIA_URL = '' 68 | 69 | # Absolute path to the directory static files should be collected to. 70 | # Don't put anything in this directory yourself; store your static files 71 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 72 | # Example: "/var/www/example.com/static/" 73 | STATIC_ROOT = os.path.join(PROJECT_PATH, 'staticfiles') 74 | 75 | # URL prefix for static files. 76 | # Example: "http://example.com/static/", "http://static.example.com/" 77 | STATIC_URL = '/static/' 78 | 79 | # Additional locations of static files 80 | STATICFILES_DIRS = ( 81 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 82 | # Always use forward slashes, even on Windows. 83 | # Don't forget to use absolute paths, not relative paths. 84 | ) 85 | 86 | # List of finder classes that know how to find static files in 87 | # various locations. 88 | STATICFILES_FINDERS = ( 89 | 'django.contrib.staticfiles.finders.FileSystemFinder', 90 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 91 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 92 | ) 93 | 94 | # Make this unique, and don't share it with anybody. 95 | SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] 96 | 97 | # List of callables that know how to import templates from various sources. 98 | TEMPLATE_LOADERS = ( 99 | 'django.template.loaders.filesystem.Loader', 100 | 'django.template.loaders.app_directories.Loader', 101 | # 'django.template.loaders.eggs.Loader', 102 | ) 103 | 104 | MIDDLEWARE_CLASSES = ( 105 | 'django.middleware.common.CommonMiddleware', 106 | 'django.contrib.sessions.middleware.SessionMiddleware', 107 | 'django.middleware.csrf.CsrfViewMiddleware', 108 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 109 | 'django.contrib.messages.middleware.MessageMiddleware', 110 | # Uncomment the next line for simple clickjacking protection: 111 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 112 | ) 113 | 114 | ROOT_URLCONF = 'pastegraph.urls' 115 | 116 | # Python dotted path to the WSGI application used by Django's runserver. 117 | WSGI_APPLICATION = 'pastegraph.wsgi.application' 118 | 119 | TEMPLATE_DIRS = ( 120 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 121 | # Always use forward slashes, even on Windows. 122 | # Don't forget to use absolute paths, not relative paths. 123 | ) 124 | 125 | INSTALLED_APPS = ( 126 | 'django.contrib.auth', 127 | 'django.contrib.contenttypes', 128 | 'django.contrib.sessions', 129 | 'django.contrib.sites', 130 | 'django.contrib.messages', 131 | 'django.contrib.staticfiles', 132 | # Uncomment the next line to enable the admin: 133 | 'django.contrib.admin', 134 | # Uncomment the next line to enable admin documentation: 135 | 'django.contrib.admindocs', 136 | 137 | # proper apps 138 | 'main_app', 139 | 'file_keeper', 140 | ) 141 | 142 | # A sample logging configuration. The only tangible logging 143 | # performed by this configuration is to send an email to 144 | # the site admins on every HTTP 500 error when DEBUG=False. 145 | # See http://docs.djangoproject.com/en/dev/topics/logging for 146 | # more details on how to customize your logging configuration. 147 | LOGGING = { 148 | 'version': 1, 149 | 'disable_existing_loggers': False, 150 | 'filters': { 151 | 'require_debug_false': { 152 | '()': 'django.utils.log.RequireDebugFalse' 153 | } 154 | }, 155 | 'handlers': { 156 | 'mail_admins': { 157 | 'level': 'ERROR', 158 | 'filters': ['require_debug_false'], 159 | 'class': 'django.utils.log.AdminEmailHandler' 160 | }, 161 | 'console': { 162 | 'level': 'INFO', 163 | 'class': 'logging.StreamHandler', 164 | 'stream': sys.stdout 165 | }, 166 | }, 167 | 'loggers': { 168 | 'django.request': { 169 | 'handlers': ['console'], 170 | 'level': 'INFO', 171 | 'propagate': True, 172 | }, 173 | } 174 | } 175 | 176 | 177 | GRAPHVIZ_URL = os.environ['GRAPHVIZ_URL'] 178 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.tooltips.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.tooltips = { 7 | name: 'tooltips', 8 | 9 | version : '4.2.2', 10 | 11 | settings : { 12 | selector : '.has-tip', 13 | additionalInheritableClasses : [], 14 | tooltipClass : '.tooltip', 15 | appendTo: 'body', 16 | 'disable-for-touch': false, 17 | tipTemplate : function (selector, content) { 18 | return '' + content + ''; 21 | } 22 | }, 23 | 24 | cache : {}, 25 | 26 | init : function (scope, method, options) { 27 | Foundation.inherit(this, 'data_options'); 28 | var self = this; 29 | 30 | if (typeof method === 'object') { 31 | $.extend(true, this.settings, method); 32 | } else if (typeof options !== 'undefined') { 33 | $.extend(true, this.settings, options); 34 | } 35 | 36 | if (typeof method !== 'string') { 37 | if (Modernizr.touch) { 38 | $(this.scope) 39 | .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', 40 | '[data-tooltip]', function (e) { 41 | var settings = $.extend({}, self.settings, self.data_options($(this))); 42 | if (!settings['disable-for-touch']) { 43 | e.preventDefault(); 44 | $(settings.tooltipClass).hide(); 45 | self.showOrCreateTip($(this)); 46 | } 47 | }) 48 | .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', 49 | this.settings.tooltipClass, function (e) { 50 | e.preventDefault(); 51 | $(this).fadeOut(150); 52 | }); 53 | } else { 54 | $(this.scope) 55 | .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip', 56 | '[data-tooltip]', function (e) { 57 | var $this = $(this); 58 | 59 | if (/enter|over/i.test(e.type)) { 60 | self.showOrCreateTip($this); 61 | } else if (e.type === 'mouseout' || e.type === 'mouseleave') { 62 | self.hide($this); 63 | } 64 | }); 65 | } 66 | 67 | // $(this.scope).data('fndtn-tooltips', true); 68 | } else { 69 | return this[method].call(this, options); 70 | } 71 | 72 | }, 73 | 74 | showOrCreateTip : function ($target) { 75 | var $tip = this.getTip($target); 76 | 77 | if ($tip && $tip.length > 0) { 78 | return this.show($target); 79 | } 80 | 81 | return this.create($target); 82 | }, 83 | 84 | getTip : function ($target) { 85 | var selector = this.selector($target), 86 | tip = null; 87 | 88 | if (selector) { 89 | tip = $('span[data-selector="' + selector + '"]' + this.settings.tooltipClass); 90 | } 91 | 92 | return (typeof tip === 'object') ? tip : false; 93 | }, 94 | 95 | selector : function ($target) { 96 | var id = $target.attr('id'), 97 | dataSelector = $target.attr('data-tooltip') || $target.attr('data-selector'); 98 | 99 | if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') { 100 | dataSelector = 'tooltip' + Math.random().toString(36).substring(7); 101 | $target.attr('data-selector', dataSelector); 102 | } 103 | 104 | return (id && id.length > 0) ? id : dataSelector; 105 | }, 106 | 107 | create : function ($target) { 108 | var $tip = $(this.settings.tipTemplate(this.selector($target), $('
').html($target.attr('title')).html())), 109 | classes = this.inheritable_classes($target); 110 | 111 | $tip.addClass(classes).appendTo(this.settings.appendTo); 112 | if (Modernizr.touch) { 113 | $tip.append('tap to close '); 114 | } 115 | $target.removeAttr('title').attr('title',''); 116 | this.show($target); 117 | }, 118 | 119 | reposition : function (target, tip, classes) { 120 | var width, nub, nubHeight, nubWidth, column, objPos; 121 | 122 | tip.css('visibility', 'hidden').show(); 123 | 124 | width = target.data('width'); 125 | nub = tip.children('.nub'); 126 | nubHeight = this.outerHeight(nub); 127 | nubWidth = this.outerHeight(nub); 128 | 129 | objPos = function (obj, top, right, bottom, left, width) { 130 | return obj.css({ 131 | 'top' : (top) ? top : 'auto', 132 | 'bottom' : (bottom) ? bottom : 'auto', 133 | 'left' : (left) ? left : 'auto', 134 | 'right' : (right) ? right : 'auto', 135 | 'width' : (width) ? width : 'auto' 136 | }).end(); 137 | }; 138 | 139 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', target.offset().left, width); 140 | 141 | if ($(window).width() < 767) { 142 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', 12.5, $(this.scope).width()); 143 | tip.addClass('tip-override'); 144 | objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left); 145 | } else { 146 | var left = target.offset().left; 147 | if (Foundation.rtl) { 148 | left = target.offset().left + target.offset().width - this.outerWidth(tip); 149 | } 150 | objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', left, width); 151 | tip.removeClass('tip-override'); 152 | if (classes && classes.indexOf('tip-top') > -1) { 153 | objPos(tip, (target.offset().top - this.outerHeight(tip)), 'auto', 'auto', left, width) 154 | .removeClass('tip-override'); 155 | } else if (classes && classes.indexOf('tip-left') > -1) { 156 | objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight*2.5), 'auto', 'auto', (target.offset().left - this.outerWidth(tip) - nubHeight), width) 157 | .removeClass('tip-override'); 158 | } else if (classes && classes.indexOf('tip-right') > -1) { 159 | objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight*2.5), 'auto', 'auto', (target.offset().left + this.outerWidth(target) + nubHeight), width) 160 | .removeClass('tip-override'); 161 | } 162 | } 163 | 164 | tip.css('visibility', 'visible').hide(); 165 | }, 166 | 167 | inheritable_classes : function (target) { 168 | var inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'noradius'].concat(this.settings.additionalInheritableClasses), 169 | classes = target.attr('class'), 170 | filtered = classes ? $.map(classes.split(' '), function (el, i) { 171 | if ($.inArray(el, inheritables) !== -1) { 172 | return el; 173 | } 174 | }).join(' ') : ''; 175 | 176 | return $.trim(filtered); 177 | }, 178 | 179 | show : function ($target) { 180 | var $tip = this.getTip($target); 181 | 182 | this.reposition($target, $tip, $target.attr('class')); 183 | $tip.fadeIn(150); 184 | }, 185 | 186 | hide : function ($target) { 187 | var $tip = this.getTip($target); 188 | 189 | $tip.fadeOut(150); 190 | }, 191 | 192 | // deprecate reload 193 | reload : function () { 194 | var $self = $(this); 195 | 196 | return ($self.data('fndtn-tooltips')) ? $self.foundationTooltips('destroy').foundationTooltips('init') : $self.foundationTooltips('init'); 197 | }, 198 | 199 | off : function () { 200 | $(this.scope).off('.fndtn.tooltip'); 201 | $(this.settings.tooltipClass).each(function (i) { 202 | $('[data-tooltip]').get(i).attr('title', $(this).text()); 203 | }).remove(); 204 | }, 205 | 206 | reflow : function () {} 207 | }; 208 | }(Foundation.zj, this, this.document)); 209 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.interchange.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.interchange = { 7 | name : 'interchange', 8 | 9 | version : '4.2.2', 10 | 11 | cache : {}, 12 | 13 | settings : { 14 | load_attr : 'interchange', 15 | 16 | named_queries : { 17 | 'default' : 'only screen and (min-width: 1px)', 18 | small : 'only screen and (min-width: 768px)', 19 | medium : 'only screen and (min-width: 1280px)', 20 | large : 'only screen and (min-width: 1440px)', 21 | landscape : 'only screen and (orientation: landscape)', 22 | portrait : 'only screen and (orientation: portrait)', 23 | retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 24 | 'only screen and (min--moz-device-pixel-ratio: 2),' + 25 | 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 26 | 'only screen and (min-device-pixel-ratio: 2),' + 27 | 'only screen and (min-resolution: 192dpi),' + 28 | 'only screen and (min-resolution: 2dppx)' 29 | }, 30 | 31 | directives : { 32 | replace : function (el, path) { 33 | if (/IMG/.test(el[0].nodeName)) { 34 | var path_parts = path.split('/'), 35 | path_file = path_parts[path_parts.length - 1], 36 | orig_path = el[0].src; 37 | 38 | if (new RegExp(path_file, 'i').test(el[0].src)) return; 39 | 40 | el[0].src = path; 41 | 42 | return el.trigger('replace', [el[0].src, orig_path]); 43 | } 44 | } 45 | } 46 | }, 47 | 48 | init : function (scope, method, options) { 49 | Foundation.inherit(this, 'throttle'); 50 | 51 | if (typeof method === 'object') { 52 | $.extend(true, this.settings, method); 53 | } 54 | 55 | this.events(); 56 | this.images(); 57 | 58 | if (typeof method !== 'string') { 59 | return this.settings.init; 60 | } else { 61 | return this[method].call(this, options); 62 | } 63 | }, 64 | 65 | events : function () { 66 | var self = this; 67 | 68 | $(window).on('resize.fndtn.interchange', self.throttle(function () { 69 | self.resize.call(self); 70 | }, 50)); 71 | }, 72 | 73 | resize : function () { 74 | var cache = this.cache; 75 | 76 | for (var uuid in cache) { 77 | if (cache.hasOwnProperty(uuid)) { 78 | var passed = this.results(uuid, cache[uuid]); 79 | 80 | if (passed) { 81 | this.settings.directives[passed 82 | .scenario[1]](passed.el, passed.scenario[0]); 83 | } 84 | } 85 | } 86 | 87 | }, 88 | 89 | results : function (uuid, scenarios) { 90 | var count = scenarios.length, 91 | results_arr = []; 92 | 93 | if (count > 0) { 94 | var el = $('[data-uuid="' + uuid + '"]'); 95 | 96 | for (var i = count - 1; i >= 0; i--) { 97 | var rule = scenarios[i][2]; 98 | if (this.settings.named_queries.hasOwnProperty(rule)) { 99 | var mq = matchMedia(this.settings.named_queries[rule]); 100 | } else { 101 | var mq = matchMedia(scenarios[i][2]); 102 | } 103 | if (mq.matches) { 104 | return {el: el, scenario: scenarios[i]}; 105 | } 106 | } 107 | } 108 | 109 | return false; 110 | }, 111 | 112 | images : function (force_update) { 113 | if (typeof this.cached_images === 'undefined' || force_update) { 114 | return this.update_images(); 115 | } 116 | 117 | return this.cached_images; 118 | }, 119 | 120 | update_images : function () { 121 | var images = document.getElementsByTagName('img'), 122 | count = images.length, 123 | data_attr = 'data-' + this.settings.load_attr; 124 | 125 | this.cached_images = []; 126 | 127 | for (var i = count - 1; i >= 0; i--) { 128 | this.loaded($(images[i]), (i === 0), function (image, last) { 129 | if (image) { 130 | var str = image.getAttribute(data_attr) || ''; 131 | 132 | if (str.length > 0) { 133 | this.cached_images.push(image); 134 | } 135 | } 136 | 137 | if (last) this.enhance(); 138 | 139 | }.bind(this)); 140 | } 141 | 142 | return 'deferred'; 143 | }, 144 | 145 | // based on jquery.imageready.js 146 | // @weblinc, @jsantell, (c) 2012 147 | 148 | loaded : function (image, last, callback) { 149 | function loaded () { 150 | callback(image[0], last); 151 | } 152 | 153 | function bindLoad () { 154 | this.one('load', loaded); 155 | 156 | if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { 157 | var src = this.attr( 'src' ), 158 | param = src.match( /\?/ ) ? '&' : '?'; 159 | 160 | param += 'random=' + (new Date()).getTime(); 161 | this.attr('src', src + param); 162 | } 163 | } 164 | 165 | if (!image.attr('src')) { 166 | loaded(); 167 | return; 168 | } 169 | 170 | if (image[0].complete || image[0].readyState === 4) { 171 | loaded(); 172 | } else { 173 | bindLoad.call(image); 174 | } 175 | }, 176 | 177 | enhance : function () { 178 | var count = this.images().length; 179 | 180 | for (var i = count - 1; i >= 0; i--) { 181 | this._object($(this.images()[i])); 182 | } 183 | 184 | return $(window).trigger('resize'); 185 | }, 186 | 187 | parse_params : function (path, directive, mq) { 188 | return [this.trim(path), this.convert_directive(directive), this.trim(mq)]; 189 | }, 190 | 191 | convert_directive : function (directive) { 192 | var trimmed = this.trim(directive); 193 | 194 | if (trimmed.length > 0) { 195 | return trimmed; 196 | } 197 | 198 | return 'replace'; 199 | }, 200 | 201 | _object : function(el) { 202 | var raw_arr = this.parse_data_attr(el), 203 | scenarios = [], count = raw_arr.length; 204 | 205 | if (count > 0) { 206 | for (var i = count - 1; i >= 0; i--) { 207 | var split = raw_arr[i].split(/\((.*?)(\))$/); 208 | 209 | if (split.length > 1) { 210 | var cached_split = split[0].split(','), 211 | params = this.parse_params(cached_split[0], 212 | cached_split[1], split[1]); 213 | 214 | scenarios.push(params); 215 | } 216 | } 217 | } 218 | 219 | return this.store(el, scenarios); 220 | }, 221 | 222 | uuid : function (separator) { 223 | var delim = separator || "-"; 224 | 225 | function S4() { 226 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 227 | } 228 | 229 | return (S4() + S4() + delim + S4() + delim + S4() 230 | + delim + S4() + delim + S4() + S4() + S4()); 231 | }, 232 | 233 | store : function (el, scenarios) { 234 | var uuid = this.uuid(), 235 | current_uuid = el.data('uuid'); 236 | 237 | if (current_uuid) return this.cache[current_uuid]; 238 | 239 | el.attr('data-uuid', uuid); 240 | 241 | return this.cache[uuid] = scenarios; 242 | }, 243 | 244 | trim : function(str) { 245 | if (typeof str === 'string') { 246 | return $.trim(str); 247 | } 248 | 249 | return str; 250 | }, 251 | 252 | parse_data_attr : function (el) { 253 | var raw = el.data(this.settings.load_attr).split(/\[(.*?)\]/), 254 | count = raw.length, output = []; 255 | 256 | for (var i = count - 1; i >= 0; i--) { 257 | if (raw[i].replace(/[\W\d]+/, '').length > 4) { 258 | output.push(raw[i]); 259 | } 260 | } 261 | 262 | return output; 263 | }, 264 | 265 | reflow : function () { 266 | this.images(true); 267 | } 268 | 269 | }; 270 | 271 | }(Foundation.zj, this, this.document)); 272 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/vendor/custom.modernizr.js: -------------------------------------------------------------------------------- 1 | /* Modernizr 2.6.2 (Custom Build) | MIT & BSD 2 | * Build: http://modernizr.com/download/#-inlinesvg-svg-svgclippaths-touch-shiv-mq-cssclasses-teststyles-prefixes-ie8compat-load 3 | */ 4 | ;window.Modernizr=function(a,b,c){function y(a){j.cssText=a}function z(a,b){return y(m.join(a+";")+(b||""))}function A(a,b){return typeof a===b}function B(a,b){return!!~(""+a).indexOf(b)}function C(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:A(f,"function")?f.bind(d||b):f}return!1}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n={svg:"http://www.w3.org/2000/svg"},o={},p={},q={},r=[],s=r.slice,t,u=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},v=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return u("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},w={}.hasOwnProperty,x;!A(w,"undefined")&&!A(w.call,"undefined")?x=function(a,b){return w.call(a,b)}:x=function(a,b){return b in a&&A(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=s.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(s.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(s.call(arguments)))};return e}),o.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:u(["@media (",m.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},o.svg=function(){return!!b.createElementNS&&!!b.createElementNS(n.svg,"svg").createSVGRect},o.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==n.svg},o.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(l.call(b.createElementNS(n.svg,"clipPath")))};for(var D in o)x(o,D)&&(t=D.toLowerCase(),e[t]=o[D](),r.push((e[t]?"":"no-")+t));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)x(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},y(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=m,e.mq=v,e.testStyles=u,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+r.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f 1 || ub < 0 || ub > 1) { 290 | return false; 291 | } 292 | 293 | return new Springy.Vector(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y)); 294 | } 295 | 296 | function intersect_line_box(p1, p2, p3, w, h) { 297 | var tl = {x: p3.x, y: p3.y}; 298 | var tr = {x: p3.x + w, y: p3.y}; 299 | var bl = {x: p3.x, y: p3.y + h}; 300 | var br = {x: p3.x + w, y: p3.y + h}; 301 | 302 | var result; 303 | if (result = intersect_line_line(p1, p2, tl, tr)) { return result; } // top 304 | if (result = intersect_line_line(p1, p2, tr, br)) { return result; } // right 305 | if (result = intersect_line_line(p1, p2, br, bl)) { return result; } // bottom 306 | if (result = intersect_line_line(p1, p2, bl, tl)) { return result; } // left 307 | 308 | return false; 309 | } 310 | 311 | return this; 312 | } 313 | 314 | })(); 315 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.reveal.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.reveal = { 7 | name: 'reveal', 8 | 9 | version : '4.2.2', 10 | 11 | locked : false, 12 | 13 | settings : { 14 | animation: 'fadeAndPop', 15 | animationSpeed: 250, 16 | closeOnBackgroundClick: true, 17 | closeOnEsc: true, 18 | dismissModalClass: 'close-reveal-modal', 19 | bgClass: 'reveal-modal-bg', 20 | open: function(){}, 21 | opened: function(){}, 22 | close: function(){}, 23 | closed: function(){}, 24 | bg : $('.reveal-modal-bg'), 25 | css : { 26 | open : { 27 | 'opacity': 0, 28 | 'visibility': 'visible', 29 | 'display' : 'block' 30 | }, 31 | close : { 32 | 'opacity': 1, 33 | 'visibility': 'hidden', 34 | 'display': 'none' 35 | } 36 | } 37 | }, 38 | 39 | init : function (scope, method, options) { 40 | Foundation.inherit(this, 'data_options delay'); 41 | 42 | if (typeof method === 'object') { 43 | $.extend(true, this.settings, method); 44 | } else if (typeof options !== 'undefined') { 45 | $.extend(true, this.settings, options); 46 | } 47 | 48 | if (typeof method !== 'string') { 49 | this.events(); 50 | 51 | return this.settings.init; 52 | } else { 53 | return this[method].call(this, options); 54 | } 55 | }, 56 | 57 | events : function () { 58 | var self = this; 59 | 60 | $(this.scope) 61 | .off('.fndtn.reveal') 62 | .on('click.fndtn.reveal', '[data-reveal-id]', function (e) { 63 | e.preventDefault(); 64 | 65 | if (!self.locked) { 66 | var element = $(this), 67 | ajax = element.data('reveal-ajax'); 68 | 69 | self.locked = true; 70 | 71 | if (typeof ajax === 'undefined') { 72 | self.open.call(self, element); 73 | } else { 74 | var url = ajax === true ? element.attr('href') : ajax; 75 | 76 | self.open.call(self, element, {url: url}); 77 | } 78 | } 79 | }) 80 | .on('click.fndtn.reveal', this.close_targets(), function (e) { 81 | e.preventDefault(); 82 | if (!self.locked) { 83 | var settings = $.extend({}, self.settings, self.data_options($('.reveal-modal.open'))); 84 | if ($(e.target)[0] === $('.' + settings.bgClass)[0] && !settings.closeOnBackgroundClick) { 85 | return; 86 | } 87 | 88 | self.locked = true; 89 | self.close.call(self, $(this).closest('.reveal-modal')); 90 | } 91 | }) 92 | .on('open.fndtn.reveal', '.reveal-modal', this.settings.open) 93 | .on('opened.fndtn.reveal', '.reveal-modal', this.settings.opened) 94 | .on('opened.fndtn.reveal', '.reveal-modal', this.open_video) 95 | .on('close.fndtn.reveal', '.reveal-modal', this.settings.close) 96 | .on('closed.fndtn.reveal', '.reveal-modal', this.settings.closed) 97 | .on('closed.fndtn.reveal', '.reveal-modal', this.close_video); 98 | 99 | $( 'body' ).bind( 'keyup.reveal', function ( event ) { 100 | var open_modal = $('.reveal-modal.open'), 101 | settings = $.extend({}, self.settings, self.data_options(open_modal)); 102 | if ( event.which === 27 && settings.closeOnEsc) { // 27 is the keycode for the Escape key 103 | open_modal.foundation('reveal', 'close'); 104 | } 105 | }); 106 | 107 | return true; 108 | }, 109 | 110 | open : function (target, ajax_settings) { 111 | if (target) { 112 | if (typeof target.selector !== 'undefined') { 113 | var modal = $('#' + target.data('reveal-id')); 114 | } else { 115 | var modal = $(this.scope); 116 | 117 | ajax_settings = target; 118 | } 119 | } else { 120 | var modal = $(this.scope); 121 | } 122 | 123 | if (!modal.hasClass('open')) { 124 | var open_modal = $('.reveal-modal.open'); 125 | 126 | if (typeof modal.data('css-top') === 'undefined') { 127 | modal.data('css-top', parseInt(modal.css('top'), 10)) 128 | .data('offset', this.cache_offset(modal)); 129 | } 130 | 131 | modal.trigger('open'); 132 | 133 | if (open_modal.length < 1) { 134 | this.toggle_bg(modal); 135 | } 136 | 137 | if (typeof ajax_settings === 'undefined' || !ajax_settings.url) { 138 | this.hide(open_modal, this.settings.css.close); 139 | this.show(modal, this.settings.css.open); 140 | } else { 141 | var self = this, 142 | old_success = typeof ajax_settings.success !== 'undefined' ? ajax_settings.success : null; 143 | 144 | $.extend(ajax_settings, { 145 | success: function (data, textStatus, jqXHR) { 146 | if ( $.isFunction(old_success) ) { 147 | old_success(data, textStatus, jqXHR); 148 | } 149 | 150 | modal.html(data); 151 | $(modal).foundation('section', 'reflow'); 152 | 153 | self.hide(open_modal, self.settings.css.close); 154 | self.show(modal, self.settings.css.open); 155 | } 156 | }); 157 | 158 | $.ajax(ajax_settings); 159 | } 160 | } 161 | }, 162 | 163 | close : function (modal) { 164 | 165 | var modal = modal && modal.length ? modal : $(this.scope), 166 | open_modals = $('.reveal-modal.open'); 167 | 168 | if (open_modals.length > 0) { 169 | this.locked = true; 170 | modal.trigger('close'); 171 | this.toggle_bg(modal); 172 | this.hide(open_modals, this.settings.css.close); 173 | } 174 | }, 175 | 176 | close_targets : function () { 177 | var base = '.' + this.settings.dismissModalClass; 178 | 179 | if (this.settings.closeOnBackgroundClick) { 180 | return base + ', .' + this.settings.bgClass; 181 | } 182 | 183 | return base; 184 | }, 185 | 186 | toggle_bg : function (modal) { 187 | if ($('.reveal-modal-bg').length === 0) { 188 | this.settings.bg = $('
', {'class': this.settings.bgClass}) 189 | .appendTo('body'); 190 | } 191 | 192 | if (this.settings.bg.filter(':visible').length > 0) { 193 | this.hide(this.settings.bg); 194 | } else { 195 | this.show(this.settings.bg); 196 | } 197 | }, 198 | 199 | show : function (el, css) { 200 | // is modal 201 | if (css) { 202 | if (/pop/i.test(this.settings.animation)) { 203 | css.top = $(window).scrollTop() - el.data('offset') + 'px'; 204 | var end_css = { 205 | top: $(window).scrollTop() + el.data('css-top') + 'px', 206 | opacity: 1 207 | }; 208 | 209 | return this.delay(function () { 210 | return el 211 | .css(css) 212 | .animate(end_css, this.settings.animationSpeed, 'linear', function () { 213 | this.locked = false; 214 | el.trigger('opened'); 215 | }.bind(this)) 216 | .addClass('open'); 217 | }.bind(this), this.settings.animationSpeed / 2); 218 | } 219 | 220 | if (/fade/i.test(this.settings.animation)) { 221 | var end_css = {opacity: 1}; 222 | 223 | return this.delay(function () { 224 | return el 225 | .css(css) 226 | .animate(end_css, this.settings.animationSpeed, 'linear', function () { 227 | this.locked = false; 228 | el.trigger('opened'); 229 | }.bind(this)) 230 | .addClass('open'); 231 | }.bind(this), this.settings.animationSpeed / 2); 232 | } 233 | 234 | return el.css(css).show().css({opacity: 1}).addClass('open').trigger('opened'); 235 | } 236 | 237 | // should we animate the background? 238 | if (/fade/i.test(this.settings.animation)) { 239 | return el.fadeIn(this.settings.animationSpeed / 2); 240 | } 241 | 242 | return el.show(); 243 | }, 244 | 245 | hide : function (el, css) { 246 | // is modal 247 | if (css) { 248 | if (/pop/i.test(this.settings.animation)) { 249 | var end_css = { 250 | top: - $(window).scrollTop() - el.data('offset') + 'px', 251 | opacity: 0 252 | }; 253 | 254 | return this.delay(function () { 255 | return el 256 | .animate(end_css, this.settings.animationSpeed, 'linear', function () { 257 | this.locked = false; 258 | el.css(css).trigger('closed'); 259 | }.bind(this)) 260 | .removeClass('open'); 261 | }.bind(this), this.settings.animationSpeed / 2); 262 | } 263 | 264 | if (/fade/i.test(this.settings.animation)) { 265 | var end_css = {opacity: 0}; 266 | 267 | return this.delay(function () { 268 | return el 269 | .animate(end_css, this.settings.animationSpeed, 'linear', function () { 270 | this.locked = false; 271 | el.css(css).trigger('closed'); 272 | }.bind(this)) 273 | .removeClass('open'); 274 | }.bind(this), this.settings.animationSpeed / 2); 275 | } 276 | 277 | return el.hide().css(css).removeClass('open').trigger('closed'); 278 | } 279 | 280 | // should we animate the background? 281 | if (/fade/i.test(this.settings.animation)) { 282 | return el.fadeOut(this.settings.animationSpeed / 2); 283 | } 284 | 285 | return el.hide(); 286 | }, 287 | 288 | close_video : function (e) { 289 | var video = $(this).find('.flex-video'), 290 | iframe = video.find('iframe'); 291 | 292 | if (iframe.length > 0) { 293 | iframe.attr('data-src', iframe[0].src); 294 | iframe.attr('src', 'about:blank'); 295 | video.hide(); 296 | } 297 | }, 298 | 299 | open_video : function (e) { 300 | var video = $(this).find('.flex-video'), 301 | iframe = video.find('iframe'); 302 | 303 | if (iframe.length > 0) { 304 | var data_src = iframe.attr('data-src'); 305 | if (typeof data_src === 'string') { 306 | iframe[0].src = iframe.attr('data-src'); 307 | } else { 308 | var src = iframe[0].src; 309 | iframe[0].src = undefined; 310 | iframe[0].src = src; 311 | } 312 | video.show(); 313 | } 314 | }, 315 | 316 | cache_offset : function (modal) { 317 | var offset = modal.show().height() + parseInt(modal.css('top'), 10); 318 | 319 | modal.hide(); 320 | 321 | return offset; 322 | }, 323 | 324 | off : function () { 325 | $(this.scope).off('.fndtn.reveal'); 326 | }, 327 | 328 | reflow : function () {} 329 | }; 330 | }(Foundation.zj, this, this.document)); 331 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.topbar.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.topbar = { 7 | name : 'topbar', 8 | 9 | version : '4.2.3', 10 | 11 | settings : { 12 | index : 0, 13 | stickyClass : 'sticky', 14 | custom_back_text: true, 15 | back_text: 'Back', 16 | is_hover: true, 17 | scrolltop : true, // jump to top when sticky nav menu toggle is clicked 18 | init : false 19 | }, 20 | 21 | init : function (section, method, options) { 22 | Foundation.inherit(this, 'data_options'); 23 | var self = this; 24 | 25 | if (typeof method === 'object') { 26 | $.extend(true, this.settings, method); 27 | } else if (typeof options !== 'undefined') { 28 | $.extend(true, this.settings, options); 29 | } 30 | 31 | if (typeof method !== 'string') { 32 | 33 | $('.top-bar, [data-topbar]').each(function () { 34 | $.extend(true, self.settings, self.data_options($(this))); 35 | self.settings.$w = $(window); 36 | self.settings.$topbar = $(this); 37 | self.settings.$section = self.settings.$topbar.find('section'); 38 | self.settings.$titlebar = self.settings.$topbar.children('ul').first(); 39 | self.settings.$topbar.data('index', 0); 40 | 41 | var breakpoint = $("
").insertAfter(self.settings.$topbar); 42 | self.settings.breakPoint = breakpoint.width(); 43 | breakpoint.remove(); 44 | 45 | self.assemble(); 46 | 47 | if (self.settings.$topbar.parent().hasClass('fixed')) { 48 | $('body').css('padding-top', self.outerHeight(self.settings.$topbar)); 49 | } 50 | }); 51 | 52 | if (!self.settings.init) { 53 | this.events(); 54 | } 55 | 56 | return this.settings.init; 57 | } else { 58 | // fire method 59 | return this[method].call(this, options); 60 | } 61 | }, 62 | 63 | events : function () { 64 | var self = this; 65 | var offst = this.outerHeight($('.top-bar, [data-topbar]')); 66 | $(this.scope) 67 | .off('.fndtn.topbar') 68 | .on('click.fndtn.topbar', '.top-bar .toggle-topbar, [data-topbar] .toggle-topbar', function (e) { 69 | var topbar = $(this).closest('.top-bar, [data-topbar]'), 70 | section = topbar.find('section, .section'), 71 | titlebar = topbar.children('ul').first(); 72 | 73 | e.preventDefault(); 74 | 75 | if (self.breakpoint()) { 76 | if (!self.rtl) { 77 | section.css({left: '0%'}); 78 | section.find('>.name').css({left: '100%'}); 79 | } else { 80 | section.css({right: '0%'}); 81 | section.find('>.name').css({right: '100%'}); 82 | } 83 | 84 | section.find('li.moved').removeClass('moved'); 85 | topbar.data('index', 0); 86 | 87 | topbar 88 | .toggleClass('expanded') 89 | .css('height', ''); 90 | } 91 | 92 | if (!topbar.hasClass('expanded')) { 93 | if (topbar.hasClass('fixed')) { 94 | topbar.parent().addClass('fixed'); 95 | topbar.removeClass('fixed'); 96 | $('body').css('padding-top',offst); 97 | } 98 | } else if (topbar.parent().hasClass('fixed')) { 99 | topbar.parent().removeClass('fixed'); 100 | topbar.addClass('fixed'); 101 | $('body').css('padding-top','0'); 102 | 103 | if (self.settings.scrolltop) { 104 | window.scrollTo(0,0); 105 | } 106 | } 107 | }) 108 | 109 | .on('mouseenter mouseleave', '.top-bar li', function (e) { 110 | if (!self.settings.is_hover) return; 111 | 112 | if (/enter|over/i.test(e.type)) { 113 | $(this).addClass('hover'); 114 | } else { 115 | $(this).removeClass('hover'); 116 | } 117 | }) 118 | 119 | .on('click.fndtn.topbar', '.top-bar li.has-dropdown', function (e) { 120 | if (self.breakpoint()) return; 121 | 122 | var li = $(this), 123 | target = $(e.target), 124 | topbar = li.closest('[data-topbar], .top-bar'), 125 | is_hover = topbar.data('topbar'); 126 | 127 | if (self.settings.is_hover && !Modernizr.touch) return; 128 | 129 | e.stopImmediatePropagation(); 130 | 131 | if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) { 132 | e.preventDefault(); 133 | } 134 | 135 | if (li.hasClass('hover')) { 136 | li 137 | .removeClass('hover') 138 | .find('li') 139 | .removeClass('hover'); 140 | } else { 141 | li.addClass('hover'); 142 | } 143 | }) 144 | 145 | .on('click.fndtn.topbar', '.top-bar .has-dropdown>a, [data-topbar] .has-dropdown>a', function (e) { 146 | if (self.breakpoint()) { 147 | e.preventDefault(); 148 | 149 | var $this = $(this), 150 | topbar = $this.closest('.top-bar, [data-topbar]'), 151 | section = topbar.find('section, .section'), 152 | titlebar = topbar.children('ul').first(), 153 | dropdownHeight = $this.next('.dropdown').outerHeight(), 154 | $selectedLi = $this.closest('li'); 155 | 156 | topbar.data('index', topbar.data('index') + 1); 157 | $selectedLi.addClass('moved'); 158 | 159 | if (!self.rtl) { 160 | section.css({left: -(100 * topbar.data('index')) + '%'}); 161 | section.find('>.name').css({left: 100 * topbar.data('index') + '%'}); 162 | } else { 163 | section.css({right: -(100 * topbar.data('index')) + '%'}); 164 | section.find('>.name').css({right: 100 * topbar.data('index') + '%'}); 165 | } 166 | 167 | topbar.css('height', self.outerHeight($this.siblings('ul'), true) + self.outerHeight(titlebar, true)); 168 | } 169 | }); 170 | 171 | $(window).on('resize.fndtn.topbar', function () { 172 | if (!self.breakpoint()) { 173 | $('.top-bar, [data-topbar]') 174 | .css('height', '') 175 | .removeClass('expanded') 176 | .find('li') 177 | .removeClass('hover'); 178 | } 179 | }.bind(this)); 180 | 181 | $('body').on('click.fndtn.topbar', function (e) { 182 | var parent = $(e.target).closest('[data-topbar], .top-bar'); 183 | 184 | if (parent.length > 0) { 185 | return; 186 | } 187 | 188 | $('.top-bar li, [data-topbar] li').removeClass('hover'); 189 | }); 190 | 191 | // Go up a level on Click 192 | $(this.scope).on('click.fndtn', '.top-bar .has-dropdown .back, [data-topbar] .has-dropdown .back', function (e) { 193 | e.preventDefault(); 194 | 195 | var $this = $(this), 196 | topbar = $this.closest('.top-bar, [data-topbar]'), 197 | titlebar = topbar.children('ul').first(), 198 | section = topbar.find('section, .section'), 199 | $movedLi = $this.closest('li.moved'), 200 | $previousLevelUl = $movedLi.parent(); 201 | 202 | topbar.data('index', topbar.data('index') - 1); 203 | 204 | if (!self.rtl) { 205 | section.css({left: -(100 * topbar.data('index')) + '%'}); 206 | section.find('>.name').css({left: 100 * topbar.data('index') + '%'}); 207 | } else { 208 | section.css({right: -(100 * topbar.data('index')) + '%'}); 209 | section.find('>.name').css({right: 100 * topbar.data('index') + '%'}); 210 | } 211 | 212 | if (topbar.data('index') === 0) { 213 | topbar.css('height', ''); 214 | } else { 215 | topbar.css('height', self.outerHeight($previousLevelUl, true) + self.outerHeight(titlebar, true)); 216 | } 217 | 218 | setTimeout(function () { 219 | $movedLi.removeClass('moved'); 220 | }, 300); 221 | }); 222 | }, 223 | 224 | breakpoint : function () { 225 | return $(document).width() <= this.settings.breakPoint || $('html').hasClass('lt-ie9'); 226 | }, 227 | 228 | assemble : function () { 229 | var self = this; 230 | // Pull element out of the DOM for manipulation 231 | this.settings.$section.detach(); 232 | 233 | this.settings.$section.find('.has-dropdown>a').each(function () { 234 | var $link = $(this), 235 | $dropdown = $link.siblings('.dropdown'), 236 | url = $link.attr('href'); 237 | 238 | if (url && url.length > 1) { 239 | var $titleLi = $('
  • ' + $link.text() +'
  • '); 240 | } else { 241 | var $titleLi = $('
  • '); 242 | } 243 | 244 | // Copy link to subnav 245 | if (self.settings.custom_back_text == true) { 246 | $titleLi.find('h5>a').html('« ' + self.settings.back_text); 247 | } else { 248 | $titleLi.find('h5>a').html('« ' + $link.html()); 249 | } 250 | $dropdown.prepend($titleLi); 251 | }); 252 | 253 | // Put element back in the DOM 254 | this.settings.$section.appendTo(this.settings.$topbar); 255 | 256 | // check for sticky 257 | this.sticky(); 258 | }, 259 | 260 | height : function (ul) { 261 | var total = 0, 262 | self = this; 263 | 264 | ul.find('> li').each(function () { total += self.outerHeight($(this), true); }); 265 | 266 | return total; 267 | }, 268 | 269 | sticky : function () { 270 | var klass = '.' + this.settings.stickyClass; 271 | if ($(klass).length > 0) { 272 | var distance = $(klass).length ? $(klass).offset().top: 0, 273 | $window = $(window); 274 | var offst = this.outerHeight($('.top-bar')); 275 | //Whe resize elements of the page on windows resize. Must recalculate distance 276 | $(window).resize(function() { 277 | clearTimeout(t_top); 278 | t_top = setTimeout (function() { 279 | distance = $(klass).offset().top; 280 | },105); 281 | }); 282 | $window.scroll(function() { 283 | if ($window.scrollTop() > (distance)) { 284 | $(klass).addClass("fixed"); 285 | $('body').css('padding-top',offst); 286 | } 287 | 288 | else if ($window.scrollTop() <= distance) { 289 | $(klass).removeClass("fixed"); 290 | $('body').css('padding-top','0'); 291 | } 292 | }); 293 | } 294 | }, 295 | 296 | off : function () { 297 | $(this.scope).off('.fndtn.topbar'); 298 | $(window).off('.fndtn.topbar'); 299 | }, 300 | 301 | reflow : function () {} 302 | }; 303 | }(Foundation.zj, this, this.document)); 304 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Foundation Responsive Library 3 | * http://foundation.zurb.com 4 | * Copyright 2013, ZURB 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | */ 8 | 9 | /*jslint unparam: true, browser: true, indent: 2 */ 10 | 11 | // Accommodate running jQuery or Zepto in noConflict() mode by 12 | // using an anonymous function to redefine the $ shorthand name. 13 | // See http://docs.jquery.com/Using_jQuery_with_Other_Libraries 14 | // and http://zeptojs.com/ 15 | var libFuncName = null; 16 | 17 | if (typeof jQuery === "undefined" && 18 | typeof Zepto === "undefined" && 19 | typeof $ === "function") { 20 | libFuncName = $; 21 | } else if (typeof jQuery === "function") { 22 | libFuncName = jQuery; 23 | } else if (typeof Zepto === "function") { 24 | libFuncName = Zepto; 25 | } else { 26 | throw new TypeError(); 27 | } 28 | 29 | (function ($, window, document, undefined) { 30 | 'use strict'; 31 | 32 | /* 33 | matchMedia() polyfill - Test a CSS media 34 | type/query in JS. Authors & copyright (c) 2012: 35 | Scott Jehl, Paul Irish, Nicholas Zakas. 36 | Dual MIT/BSD license 37 | 38 | https://github.com/paulirish/matchMedia.js 39 | */ 40 | 41 | window.matchMedia = window.matchMedia || (function( doc, undefined ) { 42 | 43 | "use strict"; 44 | 45 | var bool, 46 | docElem = doc.documentElement, 47 | refNode = docElem.firstElementChild || docElem.firstChild, 48 | // fakeBody required for 49 | fakeBody = doc.createElement( "body" ), 50 | div = doc.createElement( "div" ); 51 | 52 | div.id = "mq-test-1"; 53 | div.style.cssText = "position:absolute;top:-100em"; 54 | fakeBody.style.background = "none"; 55 | fakeBody.appendChild(div); 56 | 57 | return function(q){ 58 | 59 | div.innerHTML = "­"; 60 | 61 | docElem.insertBefore( fakeBody, refNode ); 62 | bool = div.offsetWidth === 42; 63 | docElem.removeChild( fakeBody ); 64 | 65 | return { 66 | matches: bool, 67 | media: q 68 | }; 69 | 70 | }; 71 | 72 | }( document )); 73 | 74 | // add dusty browser stuff 75 | if (!Array.prototype.filter) { 76 | Array.prototype.filter = function(fun /*, thisp */) { 77 | "use strict"; 78 | 79 | if (this == null) { 80 | throw new TypeError(); 81 | } 82 | 83 | var t = Object(this), 84 | len = t.length >>> 0; 85 | if (typeof fun !== "function") { 86 | return; 87 | } 88 | 89 | var res = [], 90 | thisp = arguments[1]; 91 | for (var i = 0; i < len; i++) { 92 | if (i in t) { 93 | var val = t[i]; // in case fun mutates this 94 | if (fun && fun.call(thisp, val, i, t)) { 95 | res.push(val); 96 | } 97 | } 98 | } 99 | 100 | return res; 101 | } 102 | } 103 | 104 | if (!Function.prototype.bind) { 105 | Function.prototype.bind = function (oThis) { 106 | if (typeof this !== "function") { 107 | // closest thing possible to the ECMAScript 5 internal IsCallable function 108 | throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 109 | } 110 | 111 | var aArgs = Array.prototype.slice.call(arguments, 1), 112 | fToBind = this, 113 | fNOP = function () {}, 114 | fBound = function () { 115 | return fToBind.apply(this instanceof fNOP && oThis 116 | ? this 117 | : oThis, 118 | aArgs.concat(Array.prototype.slice.call(arguments))); 119 | }; 120 | 121 | fNOP.prototype = this.prototype; 122 | fBound.prototype = new fNOP(); 123 | 124 | return fBound; 125 | }; 126 | } 127 | 128 | if (!Array.prototype.indexOf) { 129 | Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { 130 | "use strict"; 131 | if (this == null) { 132 | throw new TypeError(); 133 | } 134 | var t = Object(this); 135 | var len = t.length >>> 0; 136 | if (len === 0) { 137 | return -1; 138 | } 139 | var n = 0; 140 | if (arguments.length > 1) { 141 | n = Number(arguments[1]); 142 | if (n != n) { // shortcut for verifying if it's NaN 143 | n = 0; 144 | } else if (n != 0 && n != Infinity && n != -Infinity) { 145 | n = (n > 0 || -1) * Math.floor(Math.abs(n)); 146 | } 147 | } 148 | if (n >= len) { 149 | return -1; 150 | } 151 | var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); 152 | for (; k < len; k++) { 153 | if (k in t && t[k] === searchElement) { 154 | return k; 155 | } 156 | } 157 | return -1; 158 | } 159 | } 160 | 161 | // fake stop() for zepto. 162 | $.fn.stop = $.fn.stop || function() { 163 | return this; 164 | }; 165 | 166 | window.Foundation = { 167 | name : 'Foundation', 168 | 169 | version : '4.2.3', 170 | 171 | cache : {}, 172 | 173 | init : function (scope, libraries, method, options, response, /* internal */ nc) { 174 | var library_arr, 175 | args = [scope, method, options, response], 176 | responses = [], 177 | nc = nc || false; 178 | 179 | // disable library error catching, 180 | // used for development only 181 | if (nc) this.nc = nc; 182 | 183 | // check RTL 184 | this.rtl = /rtl/i.test($('html').attr('dir')); 185 | 186 | // set foundation global scope 187 | this.scope = scope || this.scope; 188 | 189 | if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) { 190 | if (/off/i.test(libraries)) return this.off(); 191 | 192 | library_arr = libraries.split(' '); 193 | 194 | if (library_arr.length > 0) { 195 | for (var i = library_arr.length - 1; i >= 0; i--) { 196 | responses.push(this.init_lib(library_arr[i], args)); 197 | } 198 | } 199 | } else { 200 | if (/reflow/i.test(libraries)) args[1] = 'reflow'; 201 | 202 | for (var lib in this.libs) { 203 | responses.push(this.init_lib(lib, args)); 204 | } 205 | } 206 | 207 | // if first argument is callback, add to args 208 | if (typeof libraries === 'function') { 209 | args.unshift(libraries); 210 | } 211 | 212 | return this.response_obj(responses, args); 213 | }, 214 | 215 | response_obj : function (response_arr, args) { 216 | for (var i = 0, len = args.length; i < len; i++) { 217 | if (typeof args[i] === 'function') { 218 | return args[i]({ 219 | errors: response_arr.filter(function (s) { 220 | if (typeof s === 'string') return s; 221 | }) 222 | }); 223 | } 224 | } 225 | 226 | return response_arr; 227 | }, 228 | 229 | init_lib : function (lib, args) { 230 | return this.trap(function () { 231 | if (this.libs.hasOwnProperty(lib)) { 232 | this.patch(this.libs[lib]); 233 | return this.libs[lib].init.apply(this.libs[lib], args); 234 | } 235 | else { 236 | return function () {}; 237 | } 238 | }.bind(this), lib); 239 | }, 240 | 241 | trap : function (fun, lib) { 242 | if (!this.nc) { 243 | try { 244 | return fun(); 245 | } catch (e) { 246 | return this.error({name: lib, message: 'could not be initialized', more: e.name + ' ' + e.message}); 247 | } 248 | } 249 | 250 | return fun(); 251 | }, 252 | 253 | patch : function (lib) { 254 | this.fix_outer(lib); 255 | lib.scope = this.scope; 256 | lib.rtl = this.rtl; 257 | }, 258 | 259 | inherit : function (scope, methods) { 260 | var methods_arr = methods.split(' '); 261 | 262 | for (var i = methods_arr.length - 1; i >= 0; i--) { 263 | if (this.lib_methods.hasOwnProperty(methods_arr[i])) { 264 | this.libs[scope.name][methods_arr[i]] = this.lib_methods[methods_arr[i]]; 265 | } 266 | } 267 | }, 268 | 269 | random_str : function (length) { 270 | var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 271 | 272 | if (!length) { 273 | length = Math.floor(Math.random() * chars.length); 274 | } 275 | 276 | var str = ''; 277 | for (var i = 0; i < length; i++) { 278 | str += chars[Math.floor(Math.random() * chars.length)]; 279 | } 280 | return str; 281 | }, 282 | 283 | libs : {}, 284 | 285 | // methods that can be inherited in libraries 286 | lib_methods : { 287 | set_data : function (node, data) { 288 | // this.name references the name of the library calling this method 289 | var id = [this.name,+new Date(),Foundation.random_str(5)].join('-'); 290 | 291 | Foundation.cache[id] = data; 292 | node.attr('data-' + this.name + '-id', id); 293 | return data; 294 | }, 295 | 296 | get_data : function (node) { 297 | return Foundation.cache[node.attr('data-' + this.name + '-id')]; 298 | }, 299 | 300 | remove_data : function (node) { 301 | if (node) { 302 | delete Foundation.cache[node.attr('data-' + this.name + '-id')]; 303 | node.attr('data-' + this.name + '-id', ''); 304 | } else { 305 | $('[data-' + this.name + '-id]').each(function () { 306 | delete Foundation.cache[$(this).attr('data-' + this.name + '-id')]; 307 | $(this).attr('data-' + this.name + '-id', ''); 308 | }); 309 | } 310 | }, 311 | 312 | throttle : function(fun, delay) { 313 | var timer = null; 314 | return function () { 315 | var context = this, args = arguments; 316 | clearTimeout(timer); 317 | timer = setTimeout(function () { 318 | fun.apply(context, args); 319 | }, delay); 320 | }; 321 | }, 322 | 323 | // parses data-options attribute on nodes and turns 324 | // them into an object 325 | data_options : function (el) { 326 | var opts = {}, ii, p, 327 | opts_arr = (el.attr('data-options') || ':').split(';'), 328 | opts_len = opts_arr.length; 329 | 330 | function isNumber (o) { 331 | return ! isNaN (o-0) && o !== null && o !== "" && o !== false && o !== true; 332 | } 333 | 334 | function trim(str) { 335 | if (typeof str === 'string') return $.trim(str); 336 | return str; 337 | } 338 | 339 | // parse options 340 | for (ii = opts_len - 1; ii >= 0; ii--) { 341 | p = opts_arr[ii].split(':'); 342 | 343 | if (/true/i.test(p[1])) p[1] = true; 344 | if (/false/i.test(p[1])) p[1] = false; 345 | if (isNumber(p[1])) p[1] = parseInt(p[1], 10); 346 | 347 | if (p.length === 2 && p[0].length > 0) { 348 | opts[trim(p[0])] = trim(p[1]); 349 | } 350 | } 351 | 352 | return opts; 353 | }, 354 | 355 | delay : function (fun, delay) { 356 | return setTimeout(fun, delay); 357 | }, 358 | 359 | // animated scrolling 360 | scrollTo : function (el, to, duration) { 361 | if (duration < 0) return; 362 | var difference = to - $(window).scrollTop(); 363 | var perTick = difference / duration * 10; 364 | 365 | this.scrollToTimerCache = setTimeout(function() { 366 | if (!isNaN(parseInt(perTick, 10))) { 367 | window.scrollTo(0, $(window).scrollTop() + perTick); 368 | this.scrollTo(el, to, duration - 10); 369 | } 370 | }.bind(this), 10); 371 | }, 372 | 373 | // not supported in core Zepto 374 | scrollLeft : function (el) { 375 | if (!el.length) return; 376 | return ('scrollLeft' in el[0]) ? el[0].scrollLeft : el[0].pageXOffset; 377 | }, 378 | 379 | // test for empty object or array 380 | empty : function (obj) { 381 | if (obj.length && obj.length > 0) return false; 382 | if (obj.length && obj.length === 0) return true; 383 | 384 | for (var key in obj) { 385 | if (hasOwnProperty.call(obj, key)) return false; 386 | } 387 | 388 | return true; 389 | } 390 | }, 391 | 392 | fix_outer : function (lib) { 393 | lib.outerHeight = function (el, bool) { 394 | if (typeof Zepto === 'function') { 395 | return el.height(); 396 | } 397 | 398 | if (typeof bool !== 'undefined') { 399 | return el.outerHeight(bool); 400 | } 401 | 402 | return el.outerHeight(); 403 | }; 404 | 405 | lib.outerWidth = function (el) { 406 | if (typeof Zepto === 'function') { 407 | return el.width(); 408 | } 409 | 410 | if (typeof bool !== 'undefined') { 411 | return el.outerWidth(bool); 412 | } 413 | 414 | return el.outerWidth(); 415 | }; 416 | }, 417 | 418 | error : function (error) { 419 | return error.name + ' ' + error.message + '; ' + error.more; 420 | }, 421 | 422 | // remove all foundation events. 423 | off: function () { 424 | $(this.scope).off('.fndtn'); 425 | $(window).off('.fndtn'); 426 | return true; 427 | }, 428 | 429 | zj : function () { 430 | if (typeof Zepto !== 'undefined') { 431 | return Zepto; 432 | } else { 433 | return jQuery; 434 | } 435 | }() 436 | }; 437 | 438 | $.fn.foundation = function () { 439 | var args = Array.prototype.slice.call(arguments, 0); 440 | 441 | return this.each(function () { 442 | Foundation.init.apply(Foundation, [this].concat(args)); 443 | return this; 444 | }); 445 | }; 446 | 447 | }(libFuncName, this, this.document)); 448 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.section.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.section = { 7 | name: 'section', 8 | 9 | version : '4.2.3', 10 | 11 | settings : { 12 | deep_linking: false, 13 | small_breakpoint: 768, 14 | one_up: true, 15 | section_selector : '[data-section]', 16 | region_selector : 'section, .section, [data-section-region]', 17 | title_selector : '.title, [data-section-title]', 18 | active_region_selector : 'section.active, .section.active, .active[data-section-region]', 19 | content_selector : '.content, [data-section-content]', 20 | nav_selector : '[data-section="vertical-nav"], [data-section="horizontal-nav"]', 21 | callback: function (){} 22 | }, 23 | 24 | init : function (scope, method, options) { 25 | var self = this; 26 | Foundation.inherit(this, 'throttle data_options position_right offset_right'); 27 | 28 | if (typeof method === 'object') { 29 | $.extend(true, self.settings, method); 30 | } 31 | 32 | if (typeof method !== 'string') { 33 | this.set_active_from_hash(); 34 | this.events(); 35 | 36 | return true; 37 | } else { 38 | return this[method].call(this, options); 39 | } 40 | }, 41 | 42 | events : function () { 43 | var self = this; 44 | 45 | $(this.scope) 46 | .on('click.fndtn.section', '[data-section] .title, [data-section] [data-section-title]', function (e) { 47 | var $this = $(this), 48 | section = $this.closest(self.settings.region_selector); 49 | 50 | if (section.children(self.settings.content_selector).length > 0) { 51 | self.toggle_active.call(this, e, self); 52 | self.reflow(); 53 | } 54 | }); 55 | 56 | $(window) 57 | .on('resize.fndtn.section', self.throttle(function () { 58 | self.resize.call(this); 59 | }, 30)) 60 | .on('hashchange', function () { 61 | if (!self.settings.toggled){ 62 | self.set_active_from_hash(); 63 | $(this).trigger('resize'); 64 | } 65 | }).trigger('resize'); 66 | 67 | $(document) 68 | .on('click.fndtn.section', function (e) { 69 | if ($(e.target).closest(self.settings.title_selector).length < 1) { 70 | $(self.settings.nav_selector) 71 | .children(self.settings.region_selector) 72 | .removeClass('active') 73 | .attr('style', ''); 74 | } 75 | }); 76 | 77 | }, 78 | 79 | toggle_active : function (e, self) { 80 | var $this = $(this), 81 | self = Foundation.libs.section, 82 | region = $this.closest(self.settings.region_selector), 83 | content = $this.siblings(self.settings.content_selector), 84 | parent = region.parent(), 85 | settings = $.extend({}, self.settings, self.data_options(parent)), 86 | prev_active_section = parent 87 | .children(self.settings.active_region_selector); 88 | 89 | self.settings.toggled = true; 90 | 91 | if (!settings.deep_linking && content.length > 0) { 92 | e.preventDefault(); 93 | } 94 | 95 | if (region.hasClass('active')) { 96 | // this is causing the style flash. 97 | if (self.small(parent) 98 | || self.is_vertical_nav(parent) 99 | || self.is_horizontal_nav(parent) 100 | || self.is_accordion(parent)) { 101 | if (prev_active_section[0] !== region[0] 102 | || (prev_active_section[0] === region[0] && !settings.one_up)) { 103 | region 104 | .removeClass('active') 105 | .attr('style', ''); 106 | } 107 | } 108 | } else { 109 | var prev_active_section = parent 110 | .children(self.settings.active_region_selector), 111 | title_height = self.outerHeight(region 112 | .children(self.settings.title_selector)); 113 | 114 | if (self.small(parent) || settings.one_up) { 115 | 116 | if (self.small(parent)) { 117 | prev_active_section.attr('style', ''); 118 | } else { 119 | prev_active_section.attr('style', 120 | 'visibility: hidden; padding-top: '+title_height+'px;'); 121 | } 122 | } 123 | 124 | if (self.small(parent)) { 125 | region.attr('style', ''); 126 | } else { 127 | region.css('padding-top', title_height); 128 | } 129 | 130 | region.addClass('active'); 131 | 132 | if (prev_active_section.length > 0) { 133 | prev_active_section 134 | .removeClass('active') 135 | .attr('style', ''); 136 | } 137 | 138 | // Toggle the content display attribute. This is done to 139 | // ensure accurate outerWidth measurements that account for 140 | // the scrollbar. 141 | if (self.is_vertical_tabs(parent)) { 142 | content.css('display', 'block'); 143 | 144 | if (prev_active_section !== null) { 145 | prev_active_section 146 | .children(self.settings.content_selector) 147 | .css('display', 'none'); 148 | } 149 | } 150 | } 151 | 152 | setTimeout(function () { 153 | self.settings.toggled = false; 154 | }, 300); 155 | 156 | settings.callback(); 157 | }, 158 | 159 | resize : function () { 160 | var self = Foundation.libs.section, 161 | sections = $(self.settings.section_selector); 162 | 163 | sections.each(function() { 164 | var $this = $(this), 165 | active_section = $this 166 | .children(self.settings.active_region_selector), 167 | settings = $.extend({}, self.settings, self.data_options($this)); 168 | 169 | if (active_section.length > 1) { 170 | active_section 171 | .not(':first') 172 | .removeClass('active') 173 | .attr('style', ''); 174 | } else if (active_section.length < 1 175 | && !self.is_vertical_nav($this) 176 | && !self.is_horizontal_nav($this) 177 | && !self.is_accordion($this)) { 178 | 179 | var first = $this.children(self.settings.region_selector).first(); 180 | 181 | if (settings.one_up || !self.small($this)) { 182 | first.addClass('active'); 183 | } 184 | 185 | if (self.small($this)) { 186 | first.attr('style', ''); 187 | } else { 188 | first.css('padding-top', self.outerHeight(first 189 | .children(self.settings.title_selector))); 190 | } 191 | } 192 | 193 | if (self.small($this)) { 194 | active_section.attr('style', ''); 195 | } else { 196 | active_section.css('padding-top', self.outerHeight(active_section 197 | .children(self.settings.title_selector))); 198 | } 199 | 200 | self.position_titles($this); 201 | 202 | if ( (self.is_horizontal_nav($this) && !self.small($this)) 203 | || self.is_vertical_tabs($this) && !self.small($this)) { 204 | self.position_content($this); 205 | } else { 206 | self.position_content($this, false); 207 | } 208 | }); 209 | }, 210 | 211 | is_vertical_nav : function (el) { 212 | return /vertical-nav/i.test(el.data('section')); 213 | }, 214 | 215 | is_horizontal_nav : function (el) { 216 | return /horizontal-nav/i.test(el.data('section')); 217 | }, 218 | 219 | is_accordion : function (el) { 220 | return /accordion/i.test(el.data('section')); 221 | }, 222 | 223 | is_horizontal_tabs : function (el) { 224 | return /^tabs$/i.test(el.data('section')); 225 | }, 226 | 227 | is_vertical_tabs : function (el) { 228 | return /vertical-tabs/i.test(el.data('section')); 229 | }, 230 | 231 | set_active_from_hash : function () { 232 | var hash = window.location.hash.substring(1), 233 | sections = $('[data-section]'), 234 | self = this; 235 | sections.each(function () { 236 | var section = $(this), 237 | settings = $.extend({}, self.settings, self.data_options(section)); 238 | 239 | if (hash.length > 0 && settings.deep_linking) { 240 | var regions = section 241 | .children(self.settings.region_selector) 242 | .attr('style', '') 243 | .removeClass('active'); 244 | 245 | var hash_regions = regions.map(function () { 246 | var content = $(self.settings.content_selector, this), 247 | content_slug = content.data('slug'); 248 | 249 | if (new RegExp(content_slug, 'i').test(hash)) 250 | return content; 251 | }); 252 | 253 | 254 | var count = hash_regions.length; 255 | 256 | for (var i = count - 1; i >= 0; i--) { 257 | $(hash_regions[i]).parent().addClass('active'); 258 | } 259 | } 260 | }); 261 | }, 262 | 263 | position_titles : function (section, off) { 264 | var self = this, 265 | titles = section 266 | .children(this.settings.region_selector) 267 | .map(function () { 268 | return $(this).children(self.settings.title_selector); 269 | }), 270 | previous_width = 0, 271 | previous_height = 0, 272 | self = this; 273 | 274 | if (typeof off === 'boolean') { 275 | titles.attr('style', ''); 276 | 277 | } else { 278 | titles.each(function () { 279 | if (self.is_vertical_tabs(section)) { 280 | $(this).css('top', previous_height); 281 | previous_height += self.outerHeight($(this)); 282 | } else { 283 | if (!self.rtl) { 284 | $(this).css('left', previous_width); 285 | } else { 286 | $(this).css('right', previous_width); 287 | } 288 | previous_width += self.outerWidth($(this)); 289 | } 290 | }); 291 | } 292 | }, 293 | 294 | position_content : function (section, off) { 295 | var self = this, 296 | regions = section.children(self.settings.region_selector), 297 | titles = regions 298 | .map(function () { 299 | return $(this).children(self.settings.title_selector); 300 | }), 301 | content = regions 302 | .map(function () { 303 | return $(this).children(self.settings.content_selector); 304 | }); 305 | 306 | if (typeof off === 'boolean') { 307 | content.attr('style', ''); 308 | section.attr('style', ''); 309 | 310 | // Reset the minHeight and maxWidth values (only applicable to 311 | // vertical tabs) 312 | content.css('minHeight', ''); 313 | content.css('maxWidth', ''); 314 | } else { 315 | if (self.is_vertical_tabs(section) 316 | && !self.small(section)) { 317 | var content_min_height = 0, 318 | content_min_width = Number.MAX_VALUE, 319 | title_width = null; 320 | 321 | regions.each(function () { 322 | var region = $(this), 323 | title = region.children(self.settings.title_selector), 324 | content = region.children(self.settings.content_selector), 325 | content_width = 0; 326 | 327 | title_width = self.outerWidth(title); 328 | content_width = self.outerWidth(section) - title_width; 329 | 330 | if (content_width < content_min_width) { 331 | content_min_width = content_width; 332 | } 333 | 334 | // Increment the minimum height of the content region 335 | // to align with the height of the titles. 336 | content_min_height += self.outerHeight(title); 337 | 338 | // Set all of the inactive tabs to 'display: none' 339 | // The CSS sets all of the tabs as 'display: block' 340 | // in order to account for scrollbars when measuring the width 341 | // of the content regions. 342 | if (!$(this).hasClass('active')) { 343 | content.css('display', 'none'); 344 | } 345 | }); 346 | 347 | regions.each(function () { 348 | var content = $(this).children(self.settings.content_selector); 349 | content.css('minHeight', content_min_height); 350 | 351 | // Remove 2 pixels to account for the right-shift in the CSS 352 | content.css('maxWidth', content_min_width - 2); 353 | }); 354 | 355 | } else { 356 | regions.each(function () { 357 | var region = $(this), 358 | title = region.children(self.settings.title_selector), 359 | content = region.children(self.settings.content_selector); 360 | if (!self.rtl) { 361 | content 362 | .css({left: title.position().left - 1, 363 | top: self.outerHeight(title) - 2}); 364 | } else { 365 | content 366 | .css({right: self.position_right(title) + 1, 367 | top: self.outerHeight(title) - 2}); 368 | } 369 | }); 370 | 371 | // temporary work around for Zepto outerheight calculation issues. 372 | if (typeof Zepto === 'function') { 373 | section.height(this.outerHeight($(titles[0]))); 374 | } else { 375 | section.height(this.outerHeight($(titles[0])) - 2); 376 | } 377 | } 378 | } 379 | }, 380 | 381 | position_right : function (el) { 382 | var self = this, 383 | section = el.closest(this.settings.section_selector), 384 | regions = section.children(this.settings.region_selector), 385 | section_width = el.closest(this.settings.section_selector).width(), 386 | offset = regions 387 | .map(function () { 388 | return $(this).children(self.settings.title_selector); 389 | }).length; 390 | return (section_width - el.position().left - el.width() * (el.index() + 1) - offset); 391 | }, 392 | 393 | reflow : function (scope) { 394 | var scope = scope || document; 395 | $(this.settings.section_selector, scope).trigger('resize'); 396 | }, 397 | 398 | small : function (el) { 399 | var settings = $.extend({}, this.settings, this.data_options(el)); 400 | 401 | if (this.is_horizontal_tabs(el)) { 402 | return false; 403 | } 404 | if (el && this.is_accordion(el)) { 405 | return true; 406 | } 407 | if ($('html').hasClass('lt-ie9')) { 408 | return true; 409 | } 410 | if ($('html').hasClass('ie8compat')) { 411 | return true; 412 | } 413 | return $(this.scope).width() < settings.small_breakpoint; 414 | }, 415 | 416 | off : function () { 417 | $(this.scope).off('.fndtn.section'); 418 | $(window).off('.fndtn.section'); 419 | $(document).off('.fndtn.section') 420 | } 421 | }; 422 | }(Foundation.zj, this, this.document)); 423 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.orbit.js: -------------------------------------------------------------------------------- 1 | ;(function ($, window, document, undefined) { 2 | 'use strict'; 3 | 4 | Foundation.libs = Foundation.libs || {}; 5 | 6 | Foundation.libs.orbit = { 7 | name: 'orbit', 8 | 9 | version: '4.2.0', 10 | 11 | settings: { 12 | timer_speed: 10000, 13 | pause_on_hover: true, 14 | resume_on_mouseout: false, 15 | animation_speed: 500, 16 | bullets: true, 17 | stack_on_small: true, 18 | navigation_arrows: true, 19 | slide_number: true, 20 | container_class: 'orbit-container', 21 | stack_on_small_class: 'orbit-stack-on-small', 22 | next_class: 'orbit-next', 23 | prev_class: 'orbit-prev', 24 | timer_container_class: 'orbit-timer', 25 | timer_paused_class: 'paused', 26 | timer_progress_class: 'orbit-progress', 27 | slides_container_class: 'orbit-slides-container', 28 | bullets_container_class: 'orbit-bullets', 29 | bullets_active_class: 'active', 30 | slide_number_class: 'orbit-slide-number', 31 | caption_class: 'orbit-caption', 32 | active_slide_class: 'active', 33 | orbit_transition_class: 'orbit-transitioning' 34 | }, 35 | 36 | init: function (scope, method, options) { 37 | var self = this; 38 | Foundation.inherit(self, 'data_options'); 39 | 40 | if (typeof method === 'object') { 41 | $.extend(true, self.settings, method); 42 | } 43 | 44 | if ($(scope).is('[data-orbit]')) { 45 | var scoped_self = $.extend(true, {}, self); 46 | scoped_self._init(idx, el); 47 | } 48 | 49 | $('[data-orbit]', scope).each(function(idx, el) { 50 | var scoped_self = $.extend(true, {}, self); 51 | scoped_self._init(idx, el); 52 | }); 53 | }, 54 | 55 | _container_html: function() { 56 | var self = this; 57 | return '
    '; 58 | }, 59 | 60 | _bullets_container_html: function($slides) { 61 | var self = this, 62 | $list = $('
      '); 63 | $slides.each(function(idx, slide) { 64 | var $item = $('
    1. '); 65 | if (idx === 0) { 66 | $item.addClass(self.settings.bullets_active_class); 67 | } 68 | $list.append($item); 69 | }); 70 | return $list; 71 | }, 72 | 73 | _slide_number_html: function(slide_number, total_slides) { 74 | var self = this, 75 | $container = $('
      '); 76 | $container.append('' + slide_number + ' of ' + total_slides + ''); 77 | return $container; 78 | }, 79 | 80 | _timer_html: function() { 81 | var self = this; 82 | if (typeof self.settings.timer_speed === 'number' && self.settings.timer_speed > 0) { 83 | return '
      '; 86 | } else { 87 | return ''; 88 | } 89 | }, 90 | 91 | _next_html: function() { 92 | var self = this; 93 | return 'Next '; 94 | }, 95 | 96 | _prev_html: function() { 97 | var self = this; 98 | return 'Prev '; 99 | }, 100 | 101 | _init: function (idx, slider) { 102 | var self = this, 103 | $slides_container = $(slider), 104 | $container = $slides_container.wrap(self._container_html()).parent(), 105 | $slides = $slides_container.children(); 106 | 107 | $.extend(true, self.settings, self.data_options($slides_container)); 108 | 109 | if (self.settings.navigation_arrows) { 110 | $container.append(self._prev_html()); 111 | $container.append(self._next_html()); 112 | } 113 | $slides_container.addClass(self.settings.slides_container_class); 114 | if (self.settings.stack_on_small) { 115 | $container.addClass(self.settings.stack_on_small_class); 116 | } 117 | if (self.settings.slide_number) { 118 | $container.append(self._slide_number_html(1, $slides.length)); 119 | } 120 | $container.append(self._timer_html()); 121 | if (self.settings.bullets) { 122 | $container.after(self._bullets_container_html($slides)); 123 | } 124 | // To better support the "sliding" effect it's easier 125 | // if we just clone the first and last slides 126 | $slides_container.append($slides.first().clone().attr('data-orbit-slide','')); 127 | $slides_container.prepend($slides.last().clone().attr('data-orbit-slide','')); 128 | // Make the first "real" slide active 129 | $slides_container.css(Foundation.rtl ? 'marginRight' : 'marginLeft', '-100%'); 130 | $slides.first().addClass(self.settings.active_slide_class); 131 | 132 | self._init_events($slides_container); 133 | self._init_dimensions($slides_container); 134 | self._start_timer($slides_container); 135 | }, 136 | 137 | _init_events: function ($slides_container) { 138 | var self = this, 139 | $container = $slides_container.parent(); 140 | 141 | $(window) 142 | .on('load.fndtn.orbit', function() { 143 | $slides_container.height(''); 144 | $slides_container.height($slides_container.height($container.height())); 145 | $slides_container.trigger('orbit:ready'); 146 | }) 147 | .on('resize.fndtn.orbit', function() { 148 | $slides_container.height(''); 149 | $slides_container.height($slides_container.height($container.height())); 150 | }); 151 | 152 | $(document).on('click.fndtn.orbit', '[data-orbit-link]', function(e) { 153 | e.preventDefault(); 154 | var id = $(e.currentTarget).attr('data-orbit-link'), 155 | $slide = $slides_container.find('[data-orbit-slide=' + id + ']').first(); 156 | 157 | if ($slide.length === 1) { 158 | self._reset_timer($slides_container, true); 159 | self._goto($slides_container, $slide.index(), function() {}); 160 | } 161 | }); 162 | 163 | $container.siblings('.' + self.settings.bullets_container_class) 164 | .on('click.fndtn.orbit', '[data-orbit-slide-number]', function(e) { 165 | e.preventDefault(); 166 | self._reset_timer($slides_container, true); 167 | self._goto($slides_container, $(e.currentTarget).data('orbit-slide-number'),function() {}); 168 | }); 169 | 170 | $container 171 | .on('mouseenter.fndtn.orbit', function(e) { 172 | if (self.settings.pause_on_hover) { 173 | self._stop_timer($slides_container); 174 | } 175 | }) 176 | .on('mouseleave.fndtn.orbit', function(e) { 177 | if (self.settings.resume_on_mouseout) { 178 | self._start_timer($slides_container); 179 | } 180 | }) 181 | .on('orbit:after-slide-change.fndtn.orbit', function(e, orbit) { 182 | var $slide_number = $container.find('.' + self.settings.slide_number_class); 183 | 184 | if ($slide_number.length === 1) { 185 | $slide_number.replaceWith(self._slide_number_html(orbit.slide_number, orbit.total_slides)); 186 | } 187 | }) 188 | .on('orbit:next-slide.fndtn.orbit click.fndtn.orbit', '.' + self.settings.next_class.split(" ").join("."), function(e) { 189 | e.preventDefault(); 190 | self._reset_timer($slides_container, true); 191 | self._goto($slides_container, 'next', function() {}); 192 | }) 193 | .on('orbit:prev-slide.fndtn.orbit click.fndtn.orbit', '.' + self.settings.prev_class.split(" ").join("."), function(e) { 194 | e.preventDefault(); 195 | self._reset_timer($slides_container, true); 196 | self._goto($slides_container, 'prev', function() {}); 197 | }) 198 | .on('orbit:toggle-play-pause.fndtn.orbit click.fndtn.orbit touchstart.fndtn.orbit', '.' + self.settings.timer_container_class, function(e) { 199 | e.preventDefault(); 200 | var $timer = $(e.currentTarget).toggleClass(self.settings.timer_paused_class), 201 | $slides_container = $timer.closest('.' + self.settings.container_class) 202 | .find('.' + self.settings.slides_container_class); 203 | 204 | if ($timer.hasClass(self.settings.timer_paused_class)) { 205 | self._stop_timer($slides_container); 206 | } else { 207 | self._start_timer($slides_container); 208 | } 209 | }) 210 | .on('touchstart.fndtn.orbit', function(e) { 211 | if (!e.touches) { e = e.originalEvent; } 212 | var data = { 213 | start_page_x: e.touches[0].pageX, 214 | start_page_y: e.touches[0].pageY, 215 | start_time: (new Date()).getTime(), 216 | delta_x: 0, 217 | is_scrolling: undefined 218 | }; 219 | $container.data('swipe-transition', data); 220 | e.stopPropagation(); 221 | }) 222 | .on('touchmove.fndtn.orbit', function(e) { 223 | if (!e.touches) { e = e.originalEvent; } 224 | // Ignore pinch/zoom events 225 | if(e.touches.length > 1 || e.scale && e.scale !== 1) return; 226 | 227 | var data = $container.data('swipe-transition'); 228 | if (typeof data === 'undefined') { 229 | data = {}; 230 | } 231 | 232 | data.delta_x = e.touches[0].pageX - data.start_page_x; 233 | 234 | if ( typeof data.is_scrolling === 'undefined') { 235 | data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) ); 236 | } 237 | 238 | if (!data.is_scrolling && !data.active) { 239 | e.preventDefault(); 240 | self._stop_timer($slides_container); 241 | var direction = (data.delta_x < 0) ? 'next' : 'prev'; 242 | data.active = true; 243 | self._goto($slides_container, direction, function() {}); 244 | } 245 | }) 246 | .on('touchend.fndtn.orbit', function(e) { 247 | $container.data('swipe-transition', {}); 248 | e.stopPropagation(); 249 | }); 250 | }, 251 | 252 | _init_dimensions: function ($slides_container) { 253 | var $container = $slides_container.parent(), 254 | $slides = $slides_container.children(); 255 | 256 | $slides_container.css('width', $slides.length * 100 + '%'); 257 | $slides.css('width', 100 / $slides.length + '%'); 258 | $slides_container.height($container.height()); 259 | $slides_container.css('width', $slides.length * 100 + '%'); 260 | }, 261 | 262 | _start_timer: function ($slides_container) { 263 | var self = this, 264 | $container = $slides_container.parent(); 265 | 266 | var callback = function() { 267 | self._reset_timer($slides_container, false); 268 | self._goto($slides_container, 'next', function() { 269 | self._start_timer($slides_container); 270 | }); 271 | }; 272 | 273 | var $timer = $container.find('.' + self.settings.timer_container_class), 274 | $progress = $timer.find('.' + self.settings.timer_progress_class), 275 | progress_pct = ($progress.width() / $timer.width()), 276 | delay = self.settings.timer_speed - (progress_pct * self.settings.timer_speed); 277 | 278 | $progress.animate({'width': '100%'}, delay, 'linear', callback); 279 | $slides_container.trigger('orbit:timer-started'); 280 | }, 281 | 282 | _stop_timer: function ($slides_container) { 283 | var self = this, 284 | $container = $slides_container.parent(), 285 | $timer = $container.find('.' + self.settings.timer_container_class), 286 | $progress = $timer.find('.' + self.settings.timer_progress_class), 287 | progress_pct = $progress.width() / $timer.width(); 288 | self._rebuild_timer($container, progress_pct * 100 + '%'); 289 | // $progress.stop(); 290 | $slides_container.trigger('orbit:timer-stopped'); 291 | $timer = $container.find('.' + self.settings.timer_container_class); 292 | $timer.addClass(self.settings.timer_paused_class); 293 | }, 294 | 295 | _reset_timer: function($slides_container, is_paused) { 296 | var self = this, 297 | $container = $slides_container.parent(); 298 | self._rebuild_timer($container, '0%'); 299 | if (typeof is_paused === 'boolean' && is_paused) { 300 | var $timer = $container.find('.' + self.settings.timer_container_class); 301 | $timer.addClass(self.settings.timer_paused_class); 302 | } 303 | }, 304 | 305 | _rebuild_timer: function ($container, width_pct) { 306 | // Zepto is unable to stop animations since they 307 | // are css-based. This is a workaround for that 308 | // limitation, which rebuilds the dom element 309 | // thus stopping the animation 310 | var self = this, 311 | $timer = $container.find('.' + self.settings.timer_container_class), 312 | $new_timer = $(self._timer_html()), 313 | $new_timer_progress = $new_timer.find('.' + self.settings.timer_progress_class); 314 | 315 | if (typeof Zepto === 'function') { 316 | $timer.remove(); 317 | $container.append($new_timer); 318 | $new_timer_progress.css('width', width_pct); 319 | } else if (typeof jQuery === 'function') { 320 | var $progress = $timer.find('.' + self.settings.timer_progress_class); 321 | $progress.css('width', width_pct); 322 | $progress.stop(); 323 | } 324 | }, 325 | 326 | _goto: function($slides_container, index_or_direction, callback) { 327 | var self = this, 328 | $container = $slides_container.parent(), 329 | $slides = $slides_container.children(), 330 | $active_slide = $slides_container.find('.' + self.settings.active_slide_class), 331 | active_index = $active_slide.index(), 332 | margin_position = Foundation.rtl ? 'marginRight' : 'marginLeft'; 333 | 334 | if ($container.hasClass(self.settings.orbit_transition_class)) { 335 | return false; 336 | } 337 | 338 | if (index_or_direction === 'prev') { 339 | if (active_index === 0) { 340 | active_index = $slides.length - 1; 341 | } 342 | else { 343 | active_index--; 344 | } 345 | } 346 | else if (index_or_direction === 'next') { 347 | active_index = (active_index+1) % $slides.length; 348 | } 349 | else if (typeof index_or_direction === 'number') { 350 | active_index = (index_or_direction % $slides.length); 351 | } 352 | if (active_index === ($slides.length - 1) && index_or_direction === 'next') { 353 | $slides_container.css(margin_position, '0%'); 354 | active_index = 1; 355 | } 356 | else if (active_index === 0 && index_or_direction === 'prev') { 357 | $slides_container.css(margin_position, '-' + ($slides.length - 1) * 100 + '%'); 358 | active_index = $slides.length - 2; 359 | } 360 | // Start transition, make next slide active 361 | $container.addClass(self.settings.orbit_transition_class); 362 | $active_slide.removeClass(self.settings.active_slide_class); 363 | $($slides[active_index]).addClass(self.settings.active_slide_class); 364 | // Make next bullet active 365 | var $bullets = $container.siblings('.' + self.settings.bullets_container_class); 366 | if ($bullets.length === 1) { 367 | $bullets.children().removeClass(self.settings.bullets_active_class); 368 | $($bullets.children()[active_index-1]).addClass(self.settings.bullets_active_class); 369 | } 370 | var new_margin_left = '-' + (active_index * 100) + '%'; 371 | // Check to see if animation will occur, otherwise perform 372 | // callbacks manually 373 | $slides_container.trigger('orbit:before-slide-change'); 374 | if ($slides_container.css(margin_position) === new_margin_left) { 375 | $container.removeClass(self.settings.orbit_transition_class); 376 | $slides_container.trigger('orbit:after-slide-change', [{slide_number: active_index, total_slides: $slides_container.children().length - 2}]); 377 | callback(); 378 | } else { 379 | var properties = {}; 380 | properties[margin_position] = new_margin_left; 381 | 382 | $slides_container.animate(properties, self.settings.animation_speed, 'linear', function() { 383 | $container.removeClass(self.settings.orbit_transition_class); 384 | $slides_container.trigger('orbit:after-slide-change', [{slide_number: active_index, total_slides: $slides_container.children().length - 2}]); 385 | callback(); 386 | }); 387 | } 388 | } 389 | }; 390 | }(Foundation.zj, this, this.document)); 391 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.clearing.js: -------------------------------------------------------------------------------- 1 | /*jslint unparam: true, browser: true, indent: 2 */ 2 | 3 | ;(function ($, window, document, undefined) { 4 | 'use strict'; 5 | 6 | Foundation.libs.clearing = { 7 | name : 'clearing', 8 | 9 | version : '4.2.2', 10 | 11 | settings : { 12 | templates : { 13 | viewing : '×' + 14 | '' 17 | }, 18 | 19 | // comma delimited list of selectors that, on click, will close clearing, 20 | // add 'div.clearing-blackout, div.visible-img' to close on background click 21 | close_selectors : '.clearing-close', 22 | 23 | // event initializers and locks 24 | init : false, 25 | locked : false 26 | }, 27 | 28 | init : function (scope, method, options) { 29 | var self = this; 30 | Foundation.inherit(this, 'set_data get_data remove_data throttle data_options'); 31 | 32 | if (typeof method === 'object') { 33 | options = $.extend(true, this.settings, method); 34 | } 35 | 36 | if (typeof method !== 'string') { 37 | $(this.scope).find('ul[data-clearing]').each(function () { 38 | var $el = $(this), 39 | options = options || {}, 40 | lis = $el.find('li'), 41 | settings = self.get_data($el); 42 | 43 | if (!settings && lis.length > 0) { 44 | options.$parent = $el.parent(); 45 | 46 | self.set_data($el, $.extend({}, self.settings, options, self.data_options($el))); 47 | 48 | self.assemble($el.find('li')); 49 | 50 | if (!self.settings.init) { 51 | self.events().swipe_events(); 52 | } 53 | } 54 | }); 55 | 56 | return this.settings.init; 57 | } else { 58 | // fire method 59 | return this[method].call(this, options); 60 | } 61 | }, 62 | 63 | // event binding and initial setup 64 | 65 | events : function () { 66 | var self = this; 67 | 68 | $(this.scope) 69 | .on('click.fndtn.clearing', 'ul[data-clearing] li', 70 | function (e, current, target) { 71 | var current = current || $(this), 72 | target = target || current, 73 | next = current.next('li'), 74 | settings = self.get_data(current.parent()), 75 | image = $(e.target); 76 | 77 | e.preventDefault(); 78 | if (!settings) self.init(); 79 | 80 | // if clearing is open and the current image is 81 | // clicked, go to the next image in sequence 82 | if (target.hasClass('visible') && 83 | current[0] === target[0] && 84 | next.length > 0 && self.is_open(current)) { 85 | target = next; 86 | image = target.find('img'); 87 | } 88 | 89 | // set current and target to the clicked li if not otherwise defined. 90 | self.open(image, current, target); 91 | self.update_paddles(target); 92 | }) 93 | 94 | .on('click.fndtn.clearing', '.clearing-main-next', 95 | function (e) { this.nav(e, 'next') }.bind(this)) 96 | .on('click.fndtn.clearing', '.clearing-main-prev', 97 | function (e) { this.nav(e, 'prev') }.bind(this)) 98 | .on('click.fndtn.clearing', this.settings.close_selectors, 99 | function (e) { Foundation.libs.clearing.close(e, this) }) 100 | .on('keydown.fndtn.clearing', 101 | function (e) { this.keydown(e) }.bind(this)); 102 | 103 | $(window).on('resize.fndtn.clearing', 104 | function () { this.resize() }.bind(this)); 105 | 106 | this.settings.init = true; 107 | return this; 108 | }, 109 | 110 | swipe_events : function () { 111 | var self = this; 112 | 113 | $(this.scope) 114 | .on('touchstart.fndtn.clearing', '.visible-img', function(e) { 115 | if (!e.touches) { e = e.originalEvent; } 116 | var data = { 117 | start_page_x: e.touches[0].pageX, 118 | start_page_y: e.touches[0].pageY, 119 | start_time: (new Date()).getTime(), 120 | delta_x: 0, 121 | is_scrolling: undefined 122 | }; 123 | 124 | $(this).data('swipe-transition', data); 125 | e.stopPropagation(); 126 | }) 127 | .on('touchmove.fndtn.clearing', '.visible-img', function(e) { 128 | if (!e.touches) { e = e.originalEvent; } 129 | // Ignore pinch/zoom events 130 | if(e.touches.length > 1 || e.scale && e.scale !== 1) return; 131 | 132 | var data = $(this).data('swipe-transition'); 133 | 134 | if (typeof data === 'undefined') { 135 | data = {}; 136 | } 137 | 138 | data.delta_x = e.touches[0].pageX - data.start_page_x; 139 | 140 | if ( typeof data.is_scrolling === 'undefined') { 141 | data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) ); 142 | } 143 | 144 | if (!data.is_scrolling && !data.active) { 145 | e.preventDefault(); 146 | var direction = (data.delta_x < 0) ? 'next' : 'prev'; 147 | data.active = true; 148 | self.nav(e, direction); 149 | } 150 | }) 151 | .on('touchend.fndtn.clearing', '.visible-img', function(e) { 152 | $(this).data('swipe-transition', {}); 153 | e.stopPropagation(); 154 | }); 155 | }, 156 | 157 | assemble : function ($li) { 158 | var $el = $li.parent(); 159 | $el.after('
      '); 160 | 161 | var holder = $('#foundationClearingHolder'), 162 | settings = this.get_data($el), 163 | grid = $el.detach(), 164 | data = { 165 | grid: '', 166 | viewing: settings.templates.viewing 167 | }, 168 | wrapper = '
      ' + data.viewing + 169 | data.grid + '
      '; 170 | 171 | return holder.after(wrapper).remove(); 172 | }, 173 | 174 | // event callbacks 175 | 176 | open : function ($image, current, target) { 177 | var root = target.closest('.clearing-assembled'), 178 | container = root.find('div').first(), 179 | visible_image = container.find('.visible-img'), 180 | image = visible_image.find('img').not($image); 181 | 182 | if (!this.locked()) { 183 | // set the image to the selected thumbnail 184 | image 185 | .attr('src', this.load($image)) 186 | .css('visibility', 'hidden'); 187 | 188 | this.loaded(image, function () { 189 | image.css('visibility', 'visible'); 190 | // toggle the gallery 191 | root.addClass('clearing-blackout'); 192 | container.addClass('clearing-container'); 193 | visible_image.show(); 194 | this.fix_height(target) 195 | .caption(visible_image.find('.clearing-caption'), $image) 196 | .center(image) 197 | .shift(current, target, function () { 198 | target.siblings().removeClass('visible'); 199 | target.addClass('visible'); 200 | }); 201 | }.bind(this)); 202 | } 203 | }, 204 | 205 | close : function (e, el) { 206 | e.preventDefault(); 207 | 208 | var root = (function (target) { 209 | if (/blackout/.test(target.selector)) { 210 | return target; 211 | } else { 212 | return target.closest('.clearing-blackout'); 213 | } 214 | }($(el))), container, visible_image; 215 | 216 | if (el === e.target && root) { 217 | container = root.find('div').first(); 218 | visible_image = container.find('.visible-img'); 219 | this.settings.prev_index = 0; 220 | root.find('ul[data-clearing]') 221 | .attr('style', '').closest('.clearing-blackout') 222 | .removeClass('clearing-blackout'); 223 | container.removeClass('clearing-container'); 224 | visible_image.hide(); 225 | } 226 | 227 | return false; 228 | }, 229 | 230 | is_open : function (current) { 231 | return current.parent().attr('style').length > 0; 232 | }, 233 | 234 | keydown : function (e) { 235 | var clearing = $('.clearing-blackout').find('ul[data-clearing]'); 236 | 237 | if (e.which === 39) this.go(clearing, 'next'); 238 | if (e.which === 37) this.go(clearing, 'prev'); 239 | if (e.which === 27) $('a.clearing-close').trigger('click'); 240 | }, 241 | 242 | nav : function (e, direction) { 243 | var clearing = $('.clearing-blackout').find('ul[data-clearing]'); 244 | 245 | e.preventDefault(); 246 | this.go(clearing, direction); 247 | }, 248 | 249 | resize : function () { 250 | var image = $('.clearing-blackout .visible-img').find('img'); 251 | 252 | if (image.length) { 253 | this.center(image); 254 | } 255 | }, 256 | 257 | // visual adjustments 258 | fix_height : function (target) { 259 | var lis = target.parent().children(), 260 | self = this; 261 | 262 | lis.each(function () { 263 | var li = $(this), 264 | image = li.find('img'); 265 | 266 | if (li.height() > self.outerHeight(image)) { 267 | li.addClass('fix-height'); 268 | } 269 | }) 270 | .closest('ul') 271 | .width(lis.length * 100 + '%'); 272 | 273 | return this; 274 | }, 275 | 276 | update_paddles : function (target) { 277 | var visible_image = target 278 | .closest('.carousel') 279 | .siblings('.visible-img'); 280 | 281 | if (target.next().length > 0) { 282 | visible_image 283 | .find('.clearing-main-next') 284 | .removeClass('disabled'); 285 | } else { 286 | visible_image 287 | .find('.clearing-main-next') 288 | .addClass('disabled'); 289 | } 290 | 291 | if (target.prev().length > 0) { 292 | visible_image 293 | .find('.clearing-main-prev') 294 | .removeClass('disabled'); 295 | } else { 296 | visible_image 297 | .find('.clearing-main-prev') 298 | .addClass('disabled'); 299 | } 300 | }, 301 | 302 | center : function (target) { 303 | if (!this.rtl) { 304 | target.css({ 305 | marginLeft : -(this.outerWidth(target) / 2), 306 | marginTop : -(this.outerHeight(target) / 2) 307 | }); 308 | } else { 309 | target.css({ 310 | marginRight : -(this.outerWidth(target) / 2), 311 | marginTop : -(this.outerHeight(target) / 2) 312 | }); 313 | } 314 | return this; 315 | }, 316 | 317 | // image loading and preloading 318 | 319 | load : function ($image) { 320 | if ($image[0].nodeName === "A") { 321 | var href = $image.attr('href'); 322 | } else { 323 | var href = $image.parent().attr('href'); 324 | } 325 | 326 | this.preload($image); 327 | 328 | if (href) return href; 329 | return $image.attr('src'); 330 | }, 331 | 332 | preload : function ($image) { 333 | this 334 | .img($image.closest('li').next()) 335 | .img($image.closest('li').prev()); 336 | }, 337 | 338 | loaded : function (image, callback) { 339 | // based on jquery.imageready.js 340 | // @weblinc, @jsantell, (c) 2012 341 | 342 | function loaded () { 343 | callback(); 344 | } 345 | 346 | function bindLoad () { 347 | this.one('load', loaded); 348 | 349 | if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { 350 | var src = this.attr( 'src' ), 351 | param = src.match( /\?/ ) ? '&' : '?'; 352 | 353 | param += 'random=' + (new Date()).getTime(); 354 | this.attr('src', src + param); 355 | } 356 | } 357 | 358 | if (!image.attr('src')) { 359 | loaded(); 360 | return; 361 | } 362 | 363 | if (image[0].complete || image[0].readyState === 4) { 364 | loaded(); 365 | } else { 366 | bindLoad.call(image); 367 | } 368 | }, 369 | 370 | img : function (img) { 371 | if (img.length) { 372 | var new_img = new Image(), 373 | new_a = img.find('a'); 374 | 375 | if (new_a.length) { 376 | new_img.src = new_a.attr('href'); 377 | } else { 378 | new_img.src = img.find('img').attr('src'); 379 | } 380 | } 381 | return this; 382 | }, 383 | 384 | // image caption 385 | 386 | caption : function (container, $image) { 387 | var caption = $image.data('caption'); 388 | 389 | if (caption) { 390 | container 391 | .html(caption) 392 | .show(); 393 | } else { 394 | container 395 | .text('') 396 | .hide(); 397 | } 398 | return this; 399 | }, 400 | 401 | // directional methods 402 | 403 | go : function ($ul, direction) { 404 | var current = $ul.find('.visible'), 405 | target = current[direction](); 406 | 407 | if (target.length) { 408 | target 409 | .find('img') 410 | .trigger('click', [current, target]); 411 | } 412 | }, 413 | 414 | shift : function (current, target, callback) { 415 | var clearing = target.parent(), 416 | old_index = this.settings.prev_index || target.index(), 417 | direction = this.direction(clearing, current, target), 418 | left = parseInt(clearing.css('left'), 10), 419 | width = this.outerWidth(target), 420 | skip_shift; 421 | 422 | // we use jQuery animate instead of CSS transitions because we 423 | // need a callback to unlock the next animation 424 | if (target.index() !== old_index && !/skip/.test(direction)){ 425 | if (/left/.test(direction)) { 426 | this.lock(); 427 | clearing.animate({left : left + width}, 300, this.unlock()); 428 | } else if (/right/.test(direction)) { 429 | this.lock(); 430 | clearing.animate({left : left - width}, 300, this.unlock()); 431 | } 432 | } else if (/skip/.test(direction)) { 433 | // the target image is not adjacent to the current image, so 434 | // do we scroll right or not 435 | skip_shift = target.index() - this.settings.up_count; 436 | this.lock(); 437 | 438 | if (skip_shift > 0) { 439 | clearing.animate({left : -(skip_shift * width)}, 300, this.unlock()); 440 | } else { 441 | clearing.animate({left : 0}, 300, this.unlock()); 442 | } 443 | } 444 | 445 | callback(); 446 | }, 447 | 448 | direction : function ($el, current, target) { 449 | var lis = $el.find('li'), 450 | li_width = this.outerWidth(lis) + (this.outerWidth(lis) / 4), 451 | up_count = Math.floor(this.outerWidth($('.clearing-container')) / li_width) - 1, 452 | target_index = lis.index(target), 453 | response; 454 | 455 | this.settings.up_count = up_count; 456 | 457 | if (this.adjacent(this.settings.prev_index, target_index)) { 458 | if ((target_index > up_count) 459 | && target_index > this.settings.prev_index) { 460 | response = 'right'; 461 | } else if ((target_index > up_count - 1) 462 | && target_index <= this.settings.prev_index) { 463 | response = 'left'; 464 | } else { 465 | response = false; 466 | } 467 | } else { 468 | response = 'skip'; 469 | } 470 | 471 | this.settings.prev_index = target_index; 472 | 473 | return response; 474 | }, 475 | 476 | adjacent : function (current_index, target_index) { 477 | for (var i = target_index + 1; i >= target_index - 1; i--) { 478 | if (i === current_index) return true; 479 | } 480 | return false; 481 | }, 482 | 483 | // lock management 484 | 485 | lock : function () { 486 | this.settings.locked = true; 487 | }, 488 | 489 | unlock : function () { 490 | this.settings.locked = false; 491 | }, 492 | 493 | locked : function () { 494 | return this.settings.locked; 495 | }, 496 | 497 | // plugin management/browser quirks 498 | 499 | outerHTML : function (el) { 500 | // support FireFox < 11 501 | return el.outerHTML || new XMLSerializer().serializeToString(el); 502 | }, 503 | 504 | off : function () { 505 | $(this.scope).off('.fndtn.clearing'); 506 | $(window).off('.fndtn.clearing'); 507 | this.remove_data(); // empty settings cache 508 | this.settings.init = false; 509 | }, 510 | 511 | reflow : function () { 512 | this.init(); 513 | } 514 | }; 515 | 516 | }(Foundation.zj, this, this.document)); 517 | -------------------------------------------------------------------------------- /main_app/static/foundation/js/foundation/foundation.forms.js: -------------------------------------------------------------------------------- 1 | (function ($, window, document, undefined) { 2 | 'use strict'; 3 | 4 | Foundation.libs.forms = { 5 | name: 'forms', 6 | 7 | version: '4.2.3', 8 | 9 | cache: {}, 10 | 11 | settings: { 12 | disable_class: 'no-custom', 13 | last_combo : null 14 | }, 15 | 16 | init: function (scope, method, options) { 17 | 18 | if (typeof method === 'object') { 19 | $.extend(true, this.settings, method); 20 | } 21 | 22 | if (typeof method !== 'string') { 23 | if (!this.settings.init) { 24 | this.events(); 25 | } 26 | 27 | this.assemble(); 28 | 29 | return this.settings.init; 30 | } else { 31 | return this[method].call(this, options); 32 | } 33 | }, 34 | 35 | assemble: function () { 36 | $('form.custom input[type="radio"]', $(this.scope)) 37 | .not('[data-customforms="disabled"]') 38 | .not('.' + this.settings.disable_class) 39 | .each(this.append_custom_markup); 40 | $('form.custom input[type="checkbox"]', $(this.scope)) 41 | .not('[data-customforms="disabled"]') 42 | .not('.' + this.settings.disable_class) 43 | .each(this.append_custom_markup); 44 | $('form.custom select', $(this.scope)) 45 | .not('[data-customforms="disabled"]') 46 | .not('.' + this.settings.disable_class) 47 | .not('[multiple=multiple]') 48 | .each(this.append_custom_select); 49 | }, 50 | 51 | events: function () { 52 | var self = this; 53 | 54 | $(this.scope) 55 | .on('click.fndtn.forms', 'form.custom span.custom.checkbox', function (e) { 56 | e.preventDefault(); 57 | e.stopPropagation(); 58 | self.toggle_checkbox($(this)); 59 | }) 60 | .on('click.fndtn.forms', 'form.custom span.custom.radio', function (e) { 61 | e.preventDefault(); 62 | e.stopPropagation(); 63 | self.toggle_radio($(this)); 64 | }) 65 | .on('change.fndtn.forms', 'form.custom select', function (e, force_refresh) { 66 | if ($(this).is('[data-customforms="disabled"]')) return; 67 | self.refresh_custom_select($(this), force_refresh); 68 | }) 69 | .on('click.fndtn.forms', 'form.custom label', function (e) { 70 | if ($(e.target).is('label')) { 71 | var $associatedElement = $('#' + self.escape($(this).attr('for'))).not('[data-customforms="disabled"]'), 72 | $customCheckbox, 73 | $customRadio; 74 | 75 | if ($associatedElement.length !== 0) { 76 | if ($associatedElement.attr('type') === 'checkbox') { 77 | e.preventDefault(); 78 | $customCheckbox = $(this).find('span.custom.checkbox'); 79 | //the checkbox might be outside after the label or inside of another element 80 | if ($customCheckbox.length === 0) { 81 | $customCheckbox = $associatedElement.add(this).siblings('span.custom.checkbox').first(); 82 | } 83 | self.toggle_checkbox($customCheckbox); 84 | } else if ($associatedElement.attr('type') === 'radio') { 85 | e.preventDefault(); 86 | $customRadio = $(this).find('span.custom.radio'); 87 | //the radio might be outside after the label or inside of another element 88 | if ($customRadio.length === 0) { 89 | $customRadio = $associatedElement.add(this).siblings('span.custom.radio').first(); 90 | } 91 | self.toggle_radio($customRadio); 92 | } 93 | } 94 | } 95 | }) 96 | .on('mousedown.fndtn.forms', 'form.custom div.custom.dropdown', function () { 97 | return false; 98 | }) 99 | .on('click.fndtn.forms', 'form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector', function (e) { 100 | var $this = $(this), 101 | $dropdown = $this.closest('div.custom.dropdown'), 102 | $select = getFirstPrevSibling($dropdown, 'select'); 103 | 104 | // make sure other dropdowns close 105 | if (!$dropdown.hasClass('open')) $(self.scope).trigger('click'); 106 | 107 | e.preventDefault(); 108 | if (false === $select.is(':disabled')) { 109 | $dropdown.toggleClass('open'); 110 | 111 | if ($dropdown.hasClass('open')) { 112 | $(self.scope).on('click.fndtn.forms.customdropdown', function () { 113 | $dropdown.removeClass('open'); 114 | $(self.scope).off('.fndtn.forms.customdropdown'); 115 | }); 116 | } else { 117 | $(self.scope).on('.fndtn.forms.customdropdown'); 118 | } 119 | return false; 120 | } 121 | }) 122 | .on('click.fndtn.forms touchend.fndtn.forms', 'form.custom div.custom.dropdown li', function (e) { 123 | var $this = $(this), 124 | $customDropdown = $this.closest('div.custom.dropdown'), 125 | $select = getFirstPrevSibling($customDropdown, 'select'), 126 | selectedIndex = 0; 127 | 128 | e.preventDefault(); 129 | e.stopPropagation(); 130 | 131 | if (!$(this).hasClass('disabled')) { 132 | $('div.dropdown').not($customDropdown).removeClass('open'); 133 | 134 | var $oldThis = $this.closest('ul') 135 | .find('li.selected'); 136 | $oldThis.removeClass('selected'); 137 | 138 | $this.addClass('selected'); 139 | 140 | $customDropdown.removeClass('open') 141 | .find('a.current') 142 | .text($this.text()); 143 | 144 | $this.closest('ul').find('li').each(function (index) { 145 | if ($this[0] === this) { 146 | selectedIndex = index; 147 | } 148 | }); 149 | $select[0].selectedIndex = selectedIndex; 150 | 151 | //store the old value in data 152 | $select.data('prevalue', $oldThis.html()); 153 | $select.trigger('change'); 154 | } 155 | }); 156 | 157 | $(window).on('keydown', function (e) { 158 | var focus = document.activeElement, 159 | self = Foundation.libs.forms, 160 | dropdown = $('.custom.dropdown.open'); 161 | 162 | if (dropdown.length > 0) { 163 | e.preventDefault(); 164 | 165 | if (e.which === 13) { 166 | dropdown.find('li.selected').trigger('click'); 167 | } 168 | 169 | if (e.which === 27) { 170 | dropdown.removeClass('open'); 171 | } 172 | 173 | if (e.which >= 65 && e.which <= 90) { 174 | var next = self.go_to(dropdown, e.which), 175 | current = dropdown.find('li.selected'); 176 | 177 | if (next) { 178 | current.removeClass('selected'); 179 | self.scrollTo(next.addClass('selected'), 300); 180 | } 181 | } 182 | 183 | if (e.which === 38) { 184 | var current = dropdown.find('li.selected'), 185 | prev = current.prev(':not(.disabled)'); 186 | 187 | if (prev.length > 0) { 188 | prev.parent()[0].scrollTop = prev.parent().scrollTop() - self.outerHeight(prev); 189 | current.removeClass('selected'); 190 | prev.addClass('selected'); 191 | } 192 | } else if (e.which === 40) { 193 | var current = dropdown.find('li.selected'), 194 | next = current.next(':not(.disabled)'); 195 | 196 | if (next.length > 0) { 197 | next.parent()[0].scrollTop = next.parent().scrollTop() + self.outerHeight(next); 198 | current.removeClass('selected'); 199 | next.addClass('selected'); 200 | } 201 | } 202 | } 203 | }); 204 | 205 | this.settings.init = true; 206 | }, 207 | 208 | go_to: function (dropdown, character) { 209 | var lis = dropdown.find('li'), 210 | count = lis.length; 211 | 212 | if (count > 0) { 213 | for (var i = 0; i < count; i++) { 214 | var first_letter = lis.eq(i).text().charAt(0).toLowerCase(); 215 | if (first_letter === String.fromCharCode(character).toLowerCase()) return lis.eq(i); 216 | } 217 | } 218 | }, 219 | 220 | scrollTo: function (el, duration) { 221 | if (duration < 0) return; 222 | var parent = el.parent(); 223 | var li_height = this.outerHeight(el); 224 | var difference = (li_height * (el.index())) - parent.scrollTop(); 225 | var perTick = difference / duration * 10; 226 | 227 | this.scrollToTimerCache = setTimeout(function () { 228 | if (!isNaN(parseInt(perTick, 10))) { 229 | parent[0].scrollTop = parent.scrollTop() + perTick; 230 | this.scrollTo(el, duration - 10); 231 | } 232 | }.bind(this), 10); 233 | }, 234 | 235 | append_custom_markup: function (idx, sel) { 236 | var $this = $(sel), 237 | type = $this.attr('type'), 238 | $span = $this.next('span.custom.' + type); 239 | 240 | if (!$this.parent().hasClass('switch')) { 241 | $this.addClass('hidden-field'); 242 | } 243 | 244 | if ($span.length === 0) { 245 | $span = $('').insertAfter($this); 246 | } 247 | 248 | $span.toggleClass('checked', $this.is(':checked')); 249 | $span.toggleClass('disabled', $this.is(':disabled')); 250 | }, 251 | 252 | append_custom_select: function (idx, sel) { 253 | var self = Foundation.libs.forms, 254 | $this = $(sel), 255 | $customSelect = $this.next('div.custom.dropdown'), 256 | $customList = $customSelect.find('ul'), 257 | $selectCurrent = $customSelect.find(".current"), 258 | $selector = $customSelect.find(".selector"), 259 | $options = $this.find('option'), 260 | $selectedOption = $options.filter(':selected'), 261 | copyClasses = $this.attr('class') ? $this.attr('class').split(' ') : [], 262 | maxWidth = 0, 263 | liHtml = '', 264 | $listItems, 265 | $currentSelect = false; 266 | 267 | if ($customSelect.length === 0) { 268 | var customSelectSize = $this.hasClass('small') ? 'small' : $this.hasClass('medium') ? 'medium' : $this.hasClass('large') ? 'large' : $this.hasClass('expand') ? 'expand' : ''; 269 | 270 | $customSelect = $('
        '); 274 | 275 | $selector = $customSelect.find(".selector"); 276 | $customList = $customSelect.find("ul"); 277 | 278 | liHtml = $options.map(function () { 279 | var copyClasses = $(this).attr('class') ? $(this).attr('class') : ''; 280 | return "
      • " + $(this).html() + "
      • "; 281 | }).get().join(''); 282 | 283 | $customList.append(liHtml); 284 | 285 | $currentSelect = $customSelect 286 | .prepend('' + $selectedOption.html() + '') 287 | .find(".current"); 288 | 289 | $this.after($customSelect) 290 | .addClass('hidden-field'); 291 | } else { 292 | liHtml = $options.map(function () { 293 | return "
      • " + $(this).html() + "
      • "; 294 | }) 295 | .get().join(''); 296 | 297 | $customList.html('') 298 | .append(liHtml); 299 | 300 | } // endif $customSelect.length === 0 301 | 302 | self.assign_id($this, $customSelect); 303 | $customSelect.toggleClass('disabled', $this.is(':disabled')); 304 | $listItems = $customList.find('li'); 305 | 306 | // cache list length 307 | self.cache[$customSelect.data('id')] = $listItems.length; 308 | 309 | $options.each(function (index) { 310 | if (this.selected) { 311 | $listItems.eq(index).addClass('selected'); 312 | 313 | if ($currentSelect) { 314 | $currentSelect.html($(this).html()); 315 | } 316 | } 317 | if ($(this).is(':disabled')) { 318 | $listItems.eq(index).addClass('disabled'); 319 | } 320 | }); 321 | 322 | // 323 | // If we're not specifying a predetermined form size. 324 | // 325 | if (!$customSelect.is('.small, .medium, .large, .expand')) { 326 | 327 | // ------------------------------------------------------------------------------------ 328 | // This is a work-around for when elements are contained within hidden parents. 329 | // For example, when custom-form elements are inside of a hidden reveal modal. 330 | // 331 | // We need to display the current custom list element as well as hidden parent elements 332 | // in order to properly calculate the list item element's width property. 333 | // ------------------------------------------------------------------------------------- 334 | 335 | $customSelect.addClass('open'); 336 | // 337 | // Quickly, display all parent elements. 338 | // This should help us calcualate the width of the list item's within the drop down. 339 | // 340 | var self = Foundation.libs.forms; 341 | self.hidden_fix.adjust($customList); 342 | 343 | maxWidth = (self.outerWidth($listItems) > maxWidth) ? self.outerWidth($listItems) : maxWidth; 344 | 345 | Foundation.libs.forms.hidden_fix.reset(); 346 | 347 | $customSelect.removeClass('open'); 348 | 349 | } // endif 350 | 351 | }, 352 | 353 | assign_id: function ($select, $customSelect) { 354 | var id = [+new Date(), Foundation.random_str(5)].join('-'); 355 | $select.attr('data-id', id); 356 | $customSelect.attr('data-id', id); 357 | }, 358 | 359 | refresh_custom_select: function ($select, force_refresh) { 360 | var self = this; 361 | var maxWidth = 0, 362 | $customSelect = $select.next(), 363 | $options = $select.find('option'), 364 | $listItems = $customSelect.find('li'); 365 | 366 | if ($listItems.length !== this.cache[$customSelect.data('id')] || force_refresh) { 367 | $customSelect.find('ul').html(''); 368 | 369 | $options.each(function () { 370 | var $li = $('
      • ' + $(this).html() + '
      • '); 371 | $customSelect.find('ul').append($li); 372 | }); 373 | 374 | // re-populate 375 | $options.each(function (index) { 376 | if (this.selected) { 377 | $customSelect.find('li').eq(index).addClass('selected'); 378 | $customSelect.find('.current').html($(this).html()); 379 | } 380 | if ($(this).is(':disabled')) { 381 | $customSelect.find('li').eq(index).addClass('disabled'); 382 | } 383 | }); 384 | 385 | // fix width 386 | $customSelect.removeAttr('style') 387 | .find('ul').removeAttr('style'); 388 | $customSelect.find('li').each(function () { 389 | $customSelect.addClass('open'); 390 | if (self.outerWidth($(this)) > maxWidth) { 391 | maxWidth = self.outerWidth($(this)); 392 | } 393 | $customSelect.removeClass('open'); 394 | }); 395 | 396 | $listItems = $customSelect.find('li'); 397 | // cache list length 398 | this.cache[$customSelect.data('id')] = $listItems.length; 399 | } 400 | }, 401 | 402 | toggle_checkbox: function ($element) { 403 | var $input = $element.prev(), 404 | input = $input[0]; 405 | 406 | if (false === $input.is(':disabled')) { 407 | input.checked = ((input.checked) ? false : true); 408 | $element.toggleClass('checked'); 409 | 410 | $input.trigger('change'); 411 | } 412 | }, 413 | 414 | toggle_radio: function ($element) { 415 | var $input = $element.prev(), 416 | $form = $input.closest('form.custom'), 417 | input = $input[0]; 418 | 419 | if (false === $input.is(':disabled')) { 420 | $form.find('input[type="radio"][name="' + this.escape($input.attr('name')) + '"]') 421 | .next().not($element).removeClass('checked'); 422 | 423 | if (!$element.hasClass('checked')) { 424 | $element.toggleClass('checked'); 425 | } 426 | 427 | input.checked = $element.hasClass('checked'); 428 | 429 | $input.trigger('change'); 430 | } 431 | }, 432 | 433 | escape: function (text) { 434 | if (!text) return ''; 435 | return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 436 | }, 437 | 438 | hidden_fix: { 439 | /** 440 | * Sets all hidden parent elements and self to visibile. 441 | * 442 | * @method adjust 443 | * @param {jQuery Object} $child 444 | */ 445 | 446 | // We'll use this to temporarily store style properties. 447 | tmp: [], 448 | 449 | // We'll use this to set hidden parent elements. 450 | hidden: null, 451 | 452 | adjust: function ($child) { 453 | // Internal reference. 454 | var _self = this; 455 | 456 | // Set all hidden parent elements, including this element. 457 | _self.hidden = $child.parents(); 458 | _self.hidden = _self.hidden.add($child).filter(":hidden"); 459 | 460 | // Loop through all hidden elements. 461 | _self.hidden.each(function () { 462 | 463 | // Cache the element. 464 | var $elem = $(this); 465 | 466 | // Store the style attribute. 467 | // Undefined if element doesn't have a style attribute. 468 | _self.tmp.push($elem.attr('style')); 469 | 470 | // Set the element's display property to block, 471 | // but ensure it's visibility is hidden. 472 | $elem.css({ 473 | 'visibility': 'hidden', 474 | 'display': 'block' 475 | }); 476 | }); 477 | 478 | }, // end adjust 479 | 480 | /** 481 | * Resets the elements previous state. 482 | * 483 | * @method reset 484 | */ 485 | reset: function () { 486 | // Internal reference. 487 | var _self = this; 488 | // Loop through our hidden element collection. 489 | _self.hidden.each(function (i) { 490 | // Cache this element. 491 | var $elem = $(this), 492 | _tmp = _self.tmp[i]; // Get the stored 'style' value for this element. 493 | 494 | // If the stored value is undefined. 495 | if (_tmp === undefined) 496 | // Remove the style attribute. 497 | $elem.removeAttr('style'); 498 | else 499 | // Otherwise, reset the element style attribute. 500 | $elem.attr('style', _tmp); 501 | }); 502 | // Reset the tmp array. 503 | _self.tmp = []; 504 | // Reset the hidden elements variable. 505 | _self.hidden = null; 506 | 507 | } // end reset 508 | }, 509 | 510 | off: function () { 511 | $(this.scope).off('.fndtn.forms'); 512 | }, 513 | 514 | reflow : function () {} 515 | }; 516 | 517 | var getFirstPrevSibling = function($el, selector) { 518 | var $el = $el.prev(); 519 | while ($el.length) { 520 | if ($el.is(selector)) return $el; 521 | $el = $el.prev(); 522 | } 523 | return $(); 524 | }; 525 | }(Foundation.zj, this, this.document)); 526 | --------------------------------------------------------------------------------