From 36b6082dfa768cbf35d40dc2c82706dfae0b687b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 14 Dec 2018 17:24:23 +0100 Subject: flask server click script --- megapixels/app/server/api/image.py | 40 ++++++++++++++++++++++++++++++++++++++ megapixels/app/server/create.py | 27 +++++++++++++++++++++++++ megapixels/app/server/static | 1 + 3 files changed, 68 insertions(+) create mode 100644 megapixels/app/server/api/image.py create mode 100644 megapixels/app/server/create.py create mode 120000 megapixels/app/server/static (limited to 'megapixels/app/server') diff --git a/megapixels/app/server/api/image.py b/megapixels/app/server/api/image.py new file mode 100644 index 00000000..f2f4a4f9 --- /dev/null +++ b/megapixels/app/server/api/image.py @@ -0,0 +1,40 @@ +from flask import Blueprint, render_template, abort +# from jinja2 import TemplateNotFound + +router = Blueprint('image', __name__) + +@router.route('//test', methods=['POST']) +def test(name): + # dataset = +@router.route('//face', methods=['POST']) +def upload(name): + file = request.files['query_img'] + fn = file.filename + if fn.endswith('blob'): + fn = 'filename.jpg' + + basename, ext = os.path.splitext(fn) + print("got {}, type {}".format(basename, ext)) + if ext.lower() not in valid_exts: + return jsonify({ 'error': 'not an image' }) + + uploaded_fn = datetime.now().isoformat() + "_" + basename + uploaded_fn = sanitize_re.sub('', uploaded_fn) + uploaded_img_path = "static/uploaded/" + uploaded_fn + ext + uploaded_img_path = uploaded_img_path.lower() + print('query: {}'.format(uploaded_img_path)) + + img = Image.open(file.stream).convert('RGB') + # img.save(uploaded_img_path) + # vec = db.load_feature_vector_from_file(uploaded_img_path) + vec = fe.extract(img) + # print(vec.shape) + + results = db.search(vec, limit=limit) + query = { + 'timing': time.time() - start, + } + print(results) + return jsonify({ + 'results': results, + }) diff --git a/megapixels/app/server/create.py b/megapixels/app/server/create.py new file mode 100644 index 00000000..1119ee8f --- /dev/null +++ b/megapixels/app/server/create.py @@ -0,0 +1,27 @@ +from flask import Flask, Blueprint +from flask_sqlalchemy import SQLAlchemy +from app.models.sql_factory import connection_url + +from app.server.api import router as api_router + +# from app.server.views.assets import assets + +db = SQLAlchemy() + +def create_app(script_info=None): + app = Flask(__name__, static_url_path='') + app.config['SQLALCHEMY_DATABASE_URI'] = connection_url + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + + db.init_app(app) + app.register_blueprint(api) + + @app.route('/', methods=['GET']) + def index(): + return app.send_static_file('index.html') + + @app.shell_context_processor + def shell_context(): + return { 'app': app, 'db': db } + + return app diff --git a/megapixels/app/server/static b/megapixels/app/server/static new file mode 120000 index 00000000..1dc7a639 --- /dev/null +++ b/megapixels/app/server/static @@ -0,0 +1 @@ +../../../site/public \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 38746f284b17400d4e2555509ea60df5912b824a Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 14 Dec 2018 18:10:27 +0100 Subject: all the sql stuff communicating nicely --- megapixels/app/models/sql_factory.py | 61 ++++++++++++++++++++++++++--- megapixels/app/server/api.py | 72 +++++++++++++++++++++++++++++++++++ megapixels/app/server/api/image.py | 40 ------------------- megapixels/app/server/create.py | 23 +++++++---- megapixels/commands/faiss/build_db.py | 36 ++---------------- 5 files changed, 147 insertions(+), 85 deletions(-) create mode 100644 megapixels/app/server/api.py delete mode 100644 megapixels/app/server/api/image.py (limited to 'megapixels/app/server') diff --git a/megapixels/app/models/sql_factory.py b/megapixels/app/models/sql_factory.py index 525492f1..2a18d6af 100644 --- a/megapixels/app/models/sql_factory.py +++ b/megapixels/app/models/sql_factory.py @@ -1,9 +1,15 @@ import os +import glob +import time +import pandas as pd from sqlalchemy import create_engine, Table, Column, String, Integer, DateTime, Float from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base +from app.utils.file_utils import load_recipe, load_csv_safe +from app.settings import app_cfg as cfg + connection_url = "mysql+mysqldb://{}:{}@{}/{}".format( os.getenv("DB_USER"), os.getenv("DB_PASS"), @@ -11,8 +17,49 @@ connection_url = "mysql+mysqldb://{}:{}@{}/{}".format( os.getenv("DB_NAME") ) -# Session = sessionmaker(bind=engine) -# session = Session() +datasets = {} +loaded = False + +def list_datasets(): + return [{ + 'name': name, + 'tables': list(datasets[name].tables.keys()), + } for name in datasets.keys()] + +def get_dataset(name): + return datasets[name] if name in datasets else None + +def get_table(name, table_name): + dataset = get_dataset(name) + return dataset.get_table(table_name) if dataset else None + +def load_sql_datasets(replace=False, base_model=None): + global datasets, loaded + if loaded: + return datasets + engine = create_engine(connection_url) if replace else None + for path in glob.iglob(os.path.join(cfg.DIR_FAISS_METADATA, "*")): + dataset = load_sql_dataset(path, replace, engine, base_model) + datasets[dataset.name] = dataset + loaded = True + return datasets + +def load_sql_dataset(path, replace=False, engine=None, base_model=None): + name = os.path.basename(path) + dataset = SqlDataset(name, base_model=base_model) + + for fn in glob.iglob(os.path.join(path, "*.csv")): + key = os.path.basename(fn).replace(".csv", "") + table = dataset.get_table(key) + if table is None: + continue + if replace: + print('loading dataset {}'.format(fn)) + df = pd.read_csv(fn) + # fix columns that are named "index", a sql reserved word + df.columns = table.__table__.columns.keys() + df.to_sql(name=table.__tablename__, con=engine, if_exists='replace', index=False) + return dataset class SqlDataset: """ @@ -21,18 +68,18 @@ class SqlDataset: - names will be fixed to work in SQL (index -> id) - we can then have more generic models for fetching this info after doing a FAISS query """ - def __init__(self, name, base_model=None): + def __init__(self, name, engine=None, base_model=None): self.name = name self.tables = {} if base_model is None: - engine = create_engine(connection_url) + self.engine = create_engine(connection_url) base_model = declarative_base(engine) self.base_model = base_model def get_table(self, type): if type in self.tables: return self.tables[type] - elif type == 'uuid': + elif type == 'uuids': self.tables[type] = self.uuid_table() elif type == 'roi': self.tables[type] = self.roi_table() @@ -96,3 +143,7 @@ class SqlDataset: roll = Column(Float, nullable=False) yaw = Column(Float, nullable=False) return Pose + + +# Session = sessionmaker(bind=engine) +# session = Session() diff --git a/megapixels/app/server/api.py b/megapixels/app/server/api.py new file mode 100644 index 00000000..e7db11f1 --- /dev/null +++ b/megapixels/app/server/api.py @@ -0,0 +1,72 @@ +from flask import Blueprint, jsonify + +from app.models.sql_factory import list_datasets, get_dataset, get_table + +# from jinja2 import TemplateNotFound + +# import os +# import sys +# import json +# import time +# import argparse +# import cv2 as cv +# import numpy as np +# from datetime import datetime +# from flask import Flask, request, render_template, jsonify +# from PIL import Image # todo: try to remove PIL dependency +# import re + +# sanitize_re = re.compile('[\W]+') +# valid_exts = ['.gif', '.jpg', '.jpeg', '.png'] + +# from dotenv import load_dotenv +# load_dotenv() + +# from feature_extractor import FeatureExtractor + +# DEFAULT_LIMIT = 50 + +api = Blueprint('api', __name__) + +@api.route('/') +def index(): + return jsonify({ 'datasets': list_datasets() }) + +@api.route('/dataset//test', methods=['POST']) +def test(dataset='test'): + dataset = get_dataset(dataset) + print('hiiiiii') + return jsonify({ 'test': 'OK', 'dataset': dataset }) + +# @router.route('//face', methods=['POST']) +# def upload(name): +# file = request.files['query_img'] +# fn = file.filename +# if fn.endswith('blob'): +# fn = 'filename.jpg' + +# basename, ext = os.path.splitext(fn) +# print("got {}, type {}".format(basename, ext)) +# if ext.lower() not in valid_exts: +# return jsonify({ 'error': 'not an image' }) + +# uploaded_fn = datetime.now().isoformat() + "_" + basename +# uploaded_fn = sanitize_re.sub('', uploaded_fn) +# uploaded_img_path = "static/uploaded/" + uploaded_fn + ext +# uploaded_img_path = uploaded_img_path.lower() +# print('query: {}'.format(uploaded_img_path)) + +# img = Image.open(file.stream).convert('RGB') +# # img.save(uploaded_img_path) +# # vec = db.load_feature_vector_from_file(uploaded_img_path) +# vec = fe.extract(img) +# # print(vec.shape) + +# results = db.search(vec, limit=limit) +# query = { +# 'timing': time.time() - start, +# } +# print(results) +# return jsonify({ +# 'results': results, +# }) diff --git a/megapixels/app/server/api/image.py b/megapixels/app/server/api/image.py deleted file mode 100644 index f2f4a4f9..00000000 --- a/megapixels/app/server/api/image.py +++ /dev/null @@ -1,40 +0,0 @@ -from flask import Blueprint, render_template, abort -# from jinja2 import TemplateNotFound - -router = Blueprint('image', __name__) - -@router.route('//test', methods=['POST']) -def test(name): - # dataset = -@router.route('//face', methods=['POST']) -def upload(name): - file = request.files['query_img'] - fn = file.filename - if fn.endswith('blob'): - fn = 'filename.jpg' - - basename, ext = os.path.splitext(fn) - print("got {}, type {}".format(basename, ext)) - if ext.lower() not in valid_exts: - return jsonify({ 'error': 'not an image' }) - - uploaded_fn = datetime.now().isoformat() + "_" + basename - uploaded_fn = sanitize_re.sub('', uploaded_fn) - uploaded_img_path = "static/uploaded/" + uploaded_fn + ext - uploaded_img_path = uploaded_img_path.lower() - print('query: {}'.format(uploaded_img_path)) - - img = Image.open(file.stream).convert('RGB') - # img.save(uploaded_img_path) - # vec = db.load_feature_vector_from_file(uploaded_img_path) - vec = fe.extract(img) - # print(vec.shape) - - results = db.search(vec, limit=limit) - query = { - 'timing': time.time() - start, - } - print(results) - return jsonify({ - 'results': results, - }) diff --git a/megapixels/app/server/create.py b/megapixels/app/server/create.py index 1119ee8f..9efed669 100644 --- a/megapixels/app/server/create.py +++ b/megapixels/app/server/create.py @@ -1,10 +1,8 @@ -from flask import Flask, Blueprint +from flask import Flask, Blueprint, jsonify from flask_sqlalchemy import SQLAlchemy -from app.models.sql_factory import connection_url +from app.models.sql_factory import connection_url, load_sql_datasets -from app.server.api import router as api_router - -# from app.server.views.assets import assets +from app.server.api import api db = SQLAlchemy() @@ -14,8 +12,10 @@ def create_app(script_info=None): app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db.init_app(app) - app.register_blueprint(api) - + datasets = load_sql_datasets(replace=False, base_model=db.Model) + + app.register_blueprint(api, url_prefix='/api') + @app.route('/', methods=['GET']) def index(): return app.send_static_file('index.html') @@ -24,4 +24,13 @@ def create_app(script_info=None): def shell_context(): return { 'app': app, 'db': db } + @app.route("/site-map") + def site_map(): + links = [] + for rule in app.url_map.iter_rules(): + # url = url_for(rule.endpoint, **(rule.defaults or {})) + # print(url) + links.append((rule.endpoint)) + return(jsonify(links)) + return app diff --git a/megapixels/commands/faiss/build_db.py b/megapixels/commands/faiss/build_db.py index 52c4980f..0f979e41 100644 --- a/megapixels/commands/faiss/build_db.py +++ b/megapixels/commands/faiss/build_db.py @@ -2,44 +2,14 @@ Load all the CSV files into MySQL """ -import os -import glob import click -import time -import pandas as pd -from app.models.sql_factory import engine, SqlDataset -from app.utils.file_utils import load_recipe, load_csv_safe -from app.settings import app_cfg as cfg +from app.models.sql_factory import load_sql_datasets @click.command() @click.pass_context def cli(ctx): """import the various CSVs into MySQL """ - load_sql_datasets(clobber=True) - -def load_sql_datasets(path, clobber=False): - datasets = {} - for path in glob.iglob(os.path.join(cfg.DIR_FAISS_METADATA, "*")): - dataset = load_sql_dataset(path, clobber) - datasets[dataset.name] = dataset - -def load_sql_dataset(path, clobber=False): - name = os.path.basename(path) - dataset = SqlDataset(name) - - for fn in glob.iglob(os.path.join(path, "*.csv")): - key = os.path.basename(fn).replace(".csv", "") - table = dataset.get_table(key) - if table is None: - continue - if clobber: - df = pd.read_csv(fn) - - # fix columns that are named "index", a sql reserved word - df.columns = table.__table__.columns.keys() - - df.to_sql(name=table.__tablename__, con=engine, if_exists='replace', index=False) - - return dataset \ No newline at end of file + print('Loading CSV datasets into SQL...') + load_sql_datasets(replace=True) -- cgit v1.2.3-70-g09d2 From 2ee8cd6a77c3efed77e58d706f4ee76418770e54 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 14 Dec 2018 18:17:15 +0100 Subject: sub apis workin --- megapixels/app/models/sql_factory.py | 11 +++++++---- megapixels/app/server/api.py | 15 +++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'megapixels/app/server') diff --git a/megapixels/app/models/sql_factory.py b/megapixels/app/models/sql_factory.py index 2a18d6af..e35c3e15 100644 --- a/megapixels/app/models/sql_factory.py +++ b/megapixels/app/models/sql_factory.py @@ -21,10 +21,7 @@ datasets = {} loaded = False def list_datasets(): - return [{ - 'name': name, - 'tables': list(datasets[name].tables.keys()), - } for name in datasets.keys()] + return [dataset.describe() for dataset in datasets.values()] def get_dataset(name): return datasets[name] if name in datasets else None @@ -76,6 +73,12 @@ class SqlDataset: base_model = declarative_base(engine) self.base_model = base_model + def describe(self): + return { + 'name': self.name, + 'tables': list(self.tables.keys()), + } + def get_table(self, type): if type in self.tables: return self.tables[type] diff --git a/megapixels/app/server/api.py b/megapixels/app/server/api.py index e7db11f1..428c53b1 100644 --- a/megapixels/app/server/api.py +++ b/megapixels/app/server/api.py @@ -32,11 +32,18 @@ api = Blueprint('api', __name__) def index(): return jsonify({ 'datasets': list_datasets() }) -@api.route('/dataset//test', methods=['POST']) -def test(dataset='test'): - dataset = get_dataset(dataset) +@api.route('/dataset/') +def show(name): + dataset = get_dataset(name) + if dataset: + return jsonify(dataset.describe()) + else: + return jsonify({ 'status': 404 }) + +@api.route('/dataset//test', methods=['POST']) +def test(name): print('hiiiiii') - return jsonify({ 'test': 'OK', 'dataset': dataset }) + return jsonify({ 'test': 'OK', 'dataset': name }) # @router.route('//face', methods=['POST']) # def upload(name): -- cgit v1.2.3-70-g09d2 From 485cf0e4665c660d4e5e1fba00a95bc8036809c6 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 15 Dec 2018 16:40:34 +0100 Subject: db stuff --- faiss/__init__.py | 0 faiss/server.py | 68 ----------------- megapixels/app/server/api.py | 97 +++++++++---------------- site/public/datasets/lfw/index.html | 67 +++++++++++++---- site/public/datasets/vgg_face2/index.html | 84 +++++++++++++++++++++ site/public/index.html | 1 + site/public/research/00_introduction/index.html | 86 ++++++++++++++++++++++ site/public/research/index.html | 2 +- 8 files changed, 259 insertions(+), 146 deletions(-) delete mode 100644 faiss/__init__.py delete mode 100644 faiss/server.py create mode 100644 site/public/datasets/vgg_face2/index.html create mode 100644 site/public/research/00_introduction/index.html (limited to 'megapixels/app/server') diff --git a/faiss/__init__.py b/faiss/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/faiss/server.py b/faiss/server.py deleted file mode 100644 index a8c660fa..00000000 --- a/faiss/server.py +++ /dev/null @@ -1,68 +0,0 @@ -#!python - -import os -import sys -import json -import time -import argparse -import cv2 as cv -import numpy as np -from datetime import datetime -from flask import Flask, request, render_template, jsonify -from PIL import Image # todo: try to remove PIL dependency -import re - -sanitize_re = re.compile('[\W]+') -valid_exts = ['.gif', '.jpg', '.jpeg', '.png'] - -from dotenv import load_dotenv -load_dotenv() - -from feature_extractor import FeatureExtractor - -DEFAULT_LIMIT = 50 - -app = Flask(__name__, static_url_path="/search/static", static_folder="static") - -# static api routes - this routing is actually handled in the JS -@app.route('/', methods=['GET']) -def index(): - return app.send_static_file('metadata.html') - -# search using an uploaded file -@app.route('/search/api/upload', methods=['POST']) -def upload(): - file = request.files['query_img'] - fn = file.filename - if fn.endswith('blob'): - fn = 'filename.jpg' - - basename, ext = os.path.splitext(fn) - print("got {}, type {}".format(basename, ext)) - if ext.lower() not in valid_exts: - return jsonify({ 'error': 'not an image' }) - - uploaded_fn = datetime.now().isoformat() + "_" + basename - uploaded_fn = sanitize_re.sub('', uploaded_fn) - uploaded_img_path = "static/uploaded/" + uploaded_fn + ext - uploaded_img_path = uploaded_img_path.lower() - print('query: {}'.format(uploaded_img_path)) - - img = Image.open(file.stream).convert('RGB') - # img.save(uploaded_img_path) - # vec = db.load_feature_vector_from_file(uploaded_img_path) - vec = fe.extract(img) - # print(vec.shape) - - results = db.search(vec, limit=limit) - query = { - 'timing': time.time() - start, - } - print(results) - return jsonify({ - 'results': results, - }) - -if __name__=="__main__": - app.run("0.0.0.0", debug=False) - diff --git a/megapixels/app/server/api.py b/megapixels/app/server/api.py index 428c53b1..c5e27dd2 100644 --- a/megapixels/app/server/api.py +++ b/megapixels/app/server/api.py @@ -1,30 +1,13 @@ -from flask import Blueprint, jsonify +import os +import re +import time +from flask import Blueprint, request, jsonify +from PIL import Image # todo: try to remove PIL dependency from app.models.sql_factory import list_datasets, get_dataset, get_table -# from jinja2 import TemplateNotFound - -# import os -# import sys -# import json -# import time -# import argparse -# import cv2 as cv -# import numpy as np -# from datetime import datetime -# from flask import Flask, request, render_template, jsonify -# from PIL import Image # todo: try to remove PIL dependency -# import re - -# sanitize_re = re.compile('[\W]+') -# valid_exts = ['.gif', '.jpg', '.jpeg', '.png'] - -# from dotenv import load_dotenv -# load_dotenv() - -# from feature_extractor import FeatureExtractor - -# DEFAULT_LIMIT = 50 +sanitize_re = re.compile('[\W]+') +valid_exts = ['.gif', '.jpg', '.jpeg', '.png'] api = Blueprint('api', __name__) @@ -40,40 +23,32 @@ def show(name): else: return jsonify({ 'status': 404 }) -@api.route('/dataset//test', methods=['POST']) -def test(name): - print('hiiiiii') - return jsonify({ 'test': 'OK', 'dataset': name }) - -# @router.route('//face', methods=['POST']) -# def upload(name): -# file = request.files['query_img'] -# fn = file.filename -# if fn.endswith('blob'): -# fn = 'filename.jpg' - -# basename, ext = os.path.splitext(fn) -# print("got {}, type {}".format(basename, ext)) -# if ext.lower() not in valid_exts: -# return jsonify({ 'error': 'not an image' }) - -# uploaded_fn = datetime.now().isoformat() + "_" + basename -# uploaded_fn = sanitize_re.sub('', uploaded_fn) -# uploaded_img_path = "static/uploaded/" + uploaded_fn + ext -# uploaded_img_path = uploaded_img_path.lower() -# print('query: {}'.format(uploaded_img_path)) - -# img = Image.open(file.stream).convert('RGB') -# # img.save(uploaded_img_path) -# # vec = db.load_feature_vector_from_file(uploaded_img_path) -# vec = fe.extract(img) -# # print(vec.shape) - -# results = db.search(vec, limit=limit) -# query = { -# 'timing': time.time() - start, -# } -# print(results) -# return jsonify({ -# 'results': results, -# }) +@api.route('/dataset//face', methods=['POST']) +def upload(name): + file = request.files['query_img'] + fn = file.filename + if fn.endswith('blob'): + fn = 'filename.jpg' + + basename, ext = os.path.splitext(fn) + print("got {}, type {}".format(basename, ext)) + if ext.lower() not in valid_exts: + return jsonify({ 'error': 'not an image' }) + + img = Image.open(file.stream).convert('RGB') + + # vec = db.load_feature_vector_from_file(uploaded_img_path) + # vec = fe.extract(img) + # print(vec.shape) + # results = db.search(vec, limit=limit) + + query = { + 'timing': time.time() - start, + } + results = [] + + print(results) + return jsonify({ + 'query': query, + 'results': results, + }) diff --git a/site/public/datasets/lfw/index.html b/site/public/datasets/lfw/index.html index 39052b44..e080229f 100644 --- a/site/public/datasets/lfw/index.html +++ b/site/public/datasets/lfw/index.html @@ -26,23 +26,50 @@
-

Labeled Faces in The Wild

-
Created
2007
Images
13,233
People
5,749
Created From
Yahoo News images
Search available
Searchable

Labeled Faces in The Wild (LFW) is amongst the most widely used facial recognition training datasets in the world and is the first of its kind to be created entirely from images that were posted online. The LFW dataset includes 13,233 images of 5,749 people that were collected between 2002-2004. Use the tools below to check if you were included in this dataset or scroll down to read the analysis.

+

Labeled Faces in the Wild

+
Created
2007
Images
13,233
People
5,749
Created From
Yahoo News images
Search available
Searchable

Labeled Faces in The Wild (LFW) is amongst the most widely used facial recognition training datasets in the world and is the first of its kind to be created entirely from images posted online. The LFW dataset includes 13,233 images of 5,749 people that were collected between 2002-2004. Use the tools below to check if you were included in this dataset or scroll down to read the analysis.

{INSERT IMAGE SEARCH MODULE}

{INSERT TEXT SEARCH MODULE}

-
Eight out of 5,749 people in the Labeled Faces in the Wild dataset. The face recognition training dataset is created entirely from photos downloaded from the Internet.
Eight out of 5,749 people in the Labeled Faces in the Wild dataset. The face recognition training dataset is created entirely from photos downloaded from the Internet.

INTRO

-

It began in 2002. Researchers at University of Massachusetts Amherst were developing algorithms for facial recognition and they needed more data. Between 2002-2004 they scraped Yahoo News for images of public figures. Two years later they cleaned up the dataset and repackaged it as Labeled Faces in the Wild (LFW).

-

Since then the LFW dataset has become one of the most widely used datasets used for evaluating face recognition algorithms. The associated research paper “Labeled Faces in the Wild: A Database for Studying Face Recognition in Unconstrained Environments” has been cited 996 times reaching 45 different countries throughout the world.

-

The faces come from news stories and are mostly celebrities from the entertainment industry, politicians, and villains. It’s a sampling of current affairs and breaking news that has come to pass. The images, detached from their original context now server a new purpose: to train, evaluate, and improve facial recognition.

-

As the most widely used facial recognition dataset, it can be said that each individual in LFW has, in a small way, contributed to the current state of the art in facial recognition surveillance. John Cusack, Julianne Moore, Barry Bonds, Osama bin Laden, and even Moby are amongst these biometric pillars, exemplar faces provided the visual dimensions of a new computer vision future.

-
The entire LFW dataset cropped to facial regions
The entire LFW dataset cropped to facial regions

In addition to commercial use as an evaluation tool, alll of the faces in LFW dataset are prepackaged into a popular machine learning code framework called scikit-learn.

-

Facts

-

The person with the most images is: -The person with the least images is:

-

Commercial Use

+
load file: lfw_names_gender_kg_min.csv
+Name, Images, Gender, Description
+
+
Eighteen of the 5,749 people in the Labeled Faces in the Wild Dataset. The most widely used face dataset for benchmarking commercial face recognition algorithms.
Eighteen of the 5,749 people in the Labeled Faces in the Wild Dataset. The most widely used face dataset for benchmarking commercial face recognition algorithms.

Intro

+

Three paragraphs describing the LFW dataset in a format that can be easily replicated for the other datasets. Nothing too custom. An analysis of the initial research papers with context relative to all the other dataset papers.

+
 all 5,749 people in the LFW Dataset sorted from most to least images collected.
all 5,749 people in the LFW Dataset sorted from most to least images collected.

LFW by the Numbers

+
    +
  • Was first published in 2007
  • +
  • Developed out of a prior dataset from Berkely called "Faces in the Wild" or "Names and Faces" [^lfw_original_paper]
  • +
  • Includes 13,233 images and 5,749 different people [^lfw_website]
  • +
  • There are about 3 men for every 1 woman (4,277 men and 1,472 women)[^lfw_website]
  • +
  • The person with the most images is George W. Bush with 530
  • +
  • Most people (70%) in the dataset have only 1 image
  • +
  • Thre are 1,680 people in the dataset with 2 or more images [^lfw_website]
  • +
  • Two out of 4 of the original authors received funding from the Office of Director of National Intelligence and IARPA for their 2016 LFW survey follow up report
  • +
  • The LFW dataset includes over 500 actors, 30 models, 10 presidents, 24 football players, 124 basketball players, 11 kings, and 2 queens
  • +
  • In all the LFW publications provided by the authors the words "ethics", "consent", and "privacy" appear 0 times [^lfw_original_paper], [^lfw_survey], [^lfw_tech_report] , [^lfw_website]
  • +
  • The word "future" appears 71 times
  • +
+

Facts

+
    +
  • Was created for the purpose of improving "unconstrained face recognition" [^lfw_original_paper]
  • +
  • All images in LFW were obtained "in the wild" meaning without any consent from the subject or from the photographer
  • +
  • The faces were detected using the Viola-Jones haarcascade face detector [^lfw_website] [^lfw_survey]
  • +
  • Is considered the "most popular benchmark for face recognition" [^lfw_baidu]
  • +
  • Is "the most widely used evaluation set in the field of facial recognition" [^lfw_pingan]
  • +
  • Is used by several of the largest tech companies in the world including "Google, Facebook, Microsoft Research Asia, Baidu, Tencent, SenseTime, Face++ and Chinese University of Hong Kong." [^lfw_pingan]
  • +
+

need citations

+
    +
  • All images were copied from Yahoo News between 2002 - 2004 [^lfw_original_paper]
  • +
  • SenseTime, who has relied on LFW for benchmarking their facial recognition performance, is the leading provider of surveillance to the Chinese Government (need citation)
  • +
+
 former President George W. Bush
former President George W. Bush
+
 Colin Powel (236), Tony Blair (144), and Donald Rumsfeld (121)
Colin Powel (236), Tony Blair (144), and Donald Rumsfeld (121)

People and Companies using the LFW Dataset

+

This section describes who is using the dataset and for what purposes. It should include specific examples of people or companies with citations and screenshots. This section is followed up by the graph, the map, and then the supplementary material.

The LFW dataset is used by numerous companies for benchmarking algorithms and in some cases training. According to the benchmarking results page [^lfw_results] provided by the authors, over 2 dozen companies have contributed their benchmark results.

According to BiometricUpdate.com [^lfw_pingan], LFW is "the most widely used evaluation set in the field of facial recognition, LFW attracts a few dozen teams from around the globe including Google, Facebook, Microsoft Research Asia, Baidu, Tencent, SenseTime, Face++ and Chinese University of Hong Kong."

According to researchers at the Baidu Research – Institute of Deep Learning "LFW has been the most popular evaluation benchmark for face recognition, and played a very important role in facilitating the face recognition society to improve algorithm. [^lfw_baidu]."

+

In addition to commercial use as an evaluation tool, alll of the faces in LFW dataset are prepackaged into a popular machine learning code framework called scikit-learn.

load file: lfw_commercial_use.csv
 name_display,company_url,example_url,country,description
 
@@ -83,13 +110,18 @@ name_display,company_url,example_url,country,description

The LFW face recognition training and evaluation dataset is a historically important face dataset as it was the first popular dataset to be created entirely from Internet images, paving the way for a global trend towards downloading anyone’s face from the Internet and adding it to a dataset. As will be evident with other datasets, LFW’s approach has now become the norm.

For all the 5,000 people in this datasets, their face is forever a part of facial recognition history. It would be impossible to remove anyone from the dataset because it is so ubiquitous. For their rest of the lives and forever after, these 5,000 people will continue to be used for training facial recognition surveillance.

Right to Removal

-

If you are affected by disclosure of your identity in this dataset please do contact the authors, many state that they are willing to remove images upon request. The authors of the LFW can be reached from the emails posted in their paper:

+

If you are affected by disclosure of your identity in this dataset please do contact the authors. Many have stated that they are willing to remove images upon request. The authors of the LFW dataset provide the following email for inquiries:

You can use the following message to request removal from the dataset:

+

To: Gary Huang mailto:gbhuang@cs.umass.edu

+

Subject: Request for Removal from LFW Face Dataset

Dear [researcher name],

-

I am writing to you about the "LFW Dataset". Recently I have discovered that your dataset includes my identity and no longer wish to be included in your dataset

-

MegaPixels is an educational art project developed for academic purposes. In no way does this project aim to villify the researchers who produced the datasets. The aim of this project is to encourage discourse around ethics and consent in artificial intelligence by providing information about these datasets that is otherwise difficult to obtain or inaccessible to other researchers.

+

I am writing to you about the "Labeled Faces in The Wild Dataset". Recently I discovered that your dataset includes my identity and I no longer wish to be included in your dataset.

+

The dataset is being used thousands of companies around the world to improve facial recognition software including usage by governments for the purpose of law enforcement, national security, tracking consumers in retail environments, and tracking individuals through public spaces.

+

My name as it appears in your dataset is [your name]. Please remove all images from your dataset and inform your newsletter subscribers to likewise update their copies.

+

- [your name]

+

Supplementary Data

-

Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos.

+

Researchers, journ

@@ -221,6 +253,9 @@ imageio.imwrite('lfw_montage_full.png', montage) montage_960 = imutils.resize(montage, width=960) imageio.imwrite('lfw_montage_960.jpg', montage_960) +

Disclaimer

+

MegaPixels is an educational art project designed to encourage discourse about facial recognition datasets. Any ethical or legal issues should be directed to the researcher's parent organizations. Except where necessary for contact or clarity, the names of researchers have been subsituted by their parent organization. In no way does this project aim to villify researchers who produced the datasets.

+

Read more about MegaPixels Code of Conduct


    diff --git a/site/public/datasets/vgg_face2/index.html b/site/public/datasets/vgg_face2/index.html new file mode 100644 index 00000000..24a1059b --- /dev/null +++ b/site/public/datasets/vgg_face2/index.html @@ -0,0 +1,84 @@ + + + + MegaPixels + + + + + + + + + +
    + + +
    MegaPixels
    + The Darkside of Datasets +
    + +
    +
    + +

    VGG Faces2

    +
    Created
    2018
    Images
    3.3M
    People
    9,000
    Created From
    Scraping search engines
    Search available
    [Searchable](#)

    VGG Face2 is the updated version of the VGG Face dataset and now includes over 3.3M face images from over 9K people. The identities were selected by taking the top 500K identities in Google's Knowledge Graph of celebrities and then selecting only the names that yielded enough training images. The dataset was created in the UK but funded by Office of Director of National Intelligence in the United States.

    +

    {INSERT IMAGE SEARCH MODULE}

    +

    {INSERT TEXT SEARCH MODULE}

    +
    load file: lfw_names_gender_kg_min.csv
    +Name, Images, Gender, Description
    +
    +

    VGG Face2 by the Numbers

    +
      +
    • 1,331 actresses, 139 presidents
    • +
    • 3 husbands and 16 wives
    • +
    • 2 snooker player
    • +
    • 1 guru
    • +
    • 1 pornographic actress
    • +
    • 3 computer programmer
    • +
    +

    Names and descriptions

    +
      +
    • The original VGGF2 name list has been updated with the results returned from Google Knowledge
    • +
    • Names with a similarity score greater than 0.75 where automatically updated. Scores computed using import difflib; seq = difflib.SequenceMatcher(a=a.lower(), b=b.lower()); score = seq.ratio()
    • +
    • The 97 names with a score of 0.75 or lower were manually reviewed and includes name changes validating using Wikipedia.org results for names such as "Bruce Jenner" to "Caitlyn Jenner", spousal last-name changes, and discretionary changes to improve search results such as combining nicknames with full name when appropriate, for example changing "Aleksandar Petrović" to "Aleksandar 'Aco' Petrović" and minor changes such as "Mohammad Ali" to "Muhammad Ali"
    • +
    • The 'Description` text was automatically added when the Knowledge Graph score was greater than 250
    • +
    +

    TODO

    +
      +
    • create name list, and populate with Knowledge graph information like LFW
    • +
    • make list of interesting number stats, by the numbers
    • +
    • make list of interesting important facts
    • +
    • write intro abstract
    • +
    • write analysis of usage
    • +
    • find examples, citations, and screenshots of useage
    • +
    • find list of companies using it for table
    • +
    • create montages of the dataset, like LFW
    • +
    • create right to removal information
    • +
    +
    + +
    + + + + + \ No newline at end of file diff --git a/site/public/index.html b/site/public/index.html index 51006b59..91ff467a 100644 --- a/site/public/index.html +++ b/site/public/index.html @@ -71,6 +71,7 @@ + diff --git a/site/public/research/00_introduction/index.html b/site/public/research/00_introduction/index.html new file mode 100644 index 00000000..8f598f5b --- /dev/null +++ b/site/public/research/00_introduction/index.html @@ -0,0 +1,86 @@ + + + + MegaPixels + + + + + + + + + +
    + + +
    MegaPixels
    + The Darkside of Datasets +
    + +
    +
    + +
    +

    Untitled Page

    +
    +
    +
    Posted
    +
    2018-12-31
    +
    +
    +
    By
    +
    Adam Harvey
    +
    + +
    +
    + +

    It was the early 2000s. Face recognition was new and no one seemed sure exactly how well it was going to perform in practice. In theory, face recognition was poised to be a game changer, a force multiplier, a strategic military advantage, a way to make cities safer and to secure borders. This was the future John Ashcroft demanded with the Total Information Awareness act of the 2003 and that spooks had dreamed of for decades. It was a future that academics at Carnegie Mellon Universtiy and Colorado State University would help build. It was also a future that celebrities would play a significant role in building. And to the surprise of ordinary Internet users like myself and perhaps you, it was a future that millions of Internet users would unwittingly play role in creating.

    +

    Now the future has arrived and it doesn't make sense. Facial recognition works yet it doesn't actually work. Facial recognition is cheap and accessible but also expensive and out of control. Facial recognition research has achieved headline grabbing superhuman accuracies over 99.9% yet facial recognition is also dangerously inaccurate. During a trial installation at Sudkreuz station in Berlin in 2018, 20% of the matches were wrong, a number so low that it should not have any connection to law enforcement or justice. And in London, the Metropolitan police had been using facial recognition software that mistakenly identified an alarming 98% of people as criminals 1, which perhaps is a crime itself.

    +

    MegaPixels is an online art project that explores the history of facial recognition from the perspective of datasets. To paraphrase the artist Trevor Paglen, whoever controls the dataset controls the meaning. MegaPixels aims to unravel the meanings behind the data and expose the darker corners of the biometric industry that have contributed to its growth. MegaPixels does not start with a conclusion, a moralistic slant, or a

    +

    Whether or not to build facial recognition was a question that can no longer be asked. As an outspoken critic of face recognition I've developed, and hopefully furthered, my understanding during the last 10 years I've spent working with computer vision. Though I initially disagreed, I've come to see technocratic perspective as a non-negotiable reality. As Oren (nytimes article) wrote in NYT Op-Ed "the horse is out of the barn" and the only thing we can do collectively or individually is to steer towards the least worse outcome. Computational communication has entered a new era and it's both exciting and frightening to explore the potentials and opportunities. In 1997 getting access to 1 teraFLOPS of computational power would have cost you $55 million and required a strategic partnership with the Department of Defense. At the time of writing, anyone can rent 1 teraFLOPS on a cloud GPU marketplace for less than $1/day. 2.

    +

    I hope that this project will illuminate the darker areas of strange world of facial recognition that have not yet received attention and encourage discourse in academic, industry, and . By no means do I believe discourse can save the day. Nor do I think creating artwork can. In fact, I'm not exactly sure what the outcome of this project will be. The project is not so much what I publish here but what happens after. This entire project is only a prologue.

    +

    As McLuhan wrote, "You can't have a static, fixed position in the electric age". And in our hyper-connected age of mass surveillance, artificial intelligece, and unevenly distributed virtual futures the most irrational thing to be is rational. Increasingly the world is becoming a contradiction where people use surveillance to protest surveillance, use

    +

    Like many projects, MegaPixels had spent years meandering between formats, unfeasible budgets, and was generally too niche of a subject. The basic idea for this project, as proposed to the original Glass Room installation in 2016 in NYC, was to build an interactive mirror that showed people if they had been included in the LFW facial recognition dataset. The idea was based on my reaction to all the datasets I'd come across during research for the CV Dazzle project. I'd noticed strange datasets created for training and testing face detection algorithms. Most were created in labratory settings and their interpretation of face data was very strict.

    +

    About the name

    +

    About the funding

    +

    About me

    +

    About the team

    +

    Conclusion

    +

    for other post

    +

    It was the early 2000s. Face recognition was new and no one seemed sure how well it was going to perform in practice. In theory, face recognition was poised to be a game changer, a force multiplier, a strategic military advantage, a way to make cities safer and to secure the borders. It was the future that John Ashcroft demanded with the Total Information Awareness act of the 2003. It was a future that academics helped build. It was a future that celebrities helped build. And it was a future that

    +

    A decade earlier the Department of Homeland Security and the Counterdrug Technology Development Program Office initated a feasibilty study called FERET (FacE REcognition Technology) to "develop automatic face recognition capabilities that could be employed to assist security, intelligence, and law enforcement personnel in the performance of their duties [^feret_website]."

    +

    One problem with FERET dataset was that the photos were in controlled settings. For face recognition to work it would have to be used in uncontrolled settings. Even newer datasets such as the Multi-PIE (Pose, Illumination, and Expression) from Carnegie Mellon University included only indoor photos of cooperative subjects. Not only were the photos completely unrealistic, CMU's Multi-Pie included only 18 individuals and cost $500 for academic use [^cmu_multipie_cost], took years to create, and required consent from every participant.

    +
    +
    +
    1. Sharman, Jon. "Metropolitan Police's facial recognition technology 98% inaccurate, figures show". 2018. https://www.independent.co.uk/news/uk/home-news/met-police-facial-recognition-success-south-wales-trial-home-office-false-positive-a8345036.html

    2. +
    3. Calle, Dan. "Supercomptuers". 1997. http://ei.cs.vt.edu/~history/SUPERCOM.Calle.HTML

    4. +
    +
    +
    + +
    + + + + + \ No newline at end of file diff --git a/site/public/research/index.html b/site/public/research/index.html index cf9546e1..59a5fee9 100644 --- a/site/public/research/index.html +++ b/site/public/research/index.html @@ -28,7 +28,7 @@

    Research Blog

    The darkside of datasets and the future of computer vision

    -
    +
    -- cgit v1.2.3-70-g09d2 From e0b0b2f976c61225a178c7715caf2656a1f6741f Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 15 Dec 2018 21:32:51 +0100 Subject: moving stuff --- builder/README.md | 21 - builder/__init__.py | 0 builder/builder.py | 90 -- builder/parser.py | 172 --- builder/paths.py | 6 - builder/s3.py | 61 - client/actions.js | 9 + client/app.js | 46 + client/common/activeLink.component.js | 16 + client/common/classifier.component.js | 99 ++ client/common/common.css | 347 +++++ client/common/detectionBoxes.component.js | 15 + client/common/detectionList.component.js | 16 + client/common/footer.component.js | 10 + client/common/gate.component.js | 21 + client/common/header.component.js | 1 + client/common/index.js | 36 + client/common/keyframe.component.js | 118 ++ client/common/keyframes.component.js | 95 ++ client/common/loader.component.js | 10 + client/common/sidebar.component.js | 37 + client/common/table.component.js | 121 ++ client/common/video.component.js | 47 + client/index.js | 19 + client/metadata/index.js | 25 + client/session.js | 5 + client/store.js | 38 + client/types.js | 21 + client/util.js | 167 +++ faiss/requirements.txt | 11 - faiss/run.sh | 1 - faiss/static/css/app.css | 289 ----- faiss/static/favicon.ico | Bin 15086 -> 0 bytes faiss/static/img/play.png | Bin 1231 -> 0 bytes faiss/static/index.html | 83 -- faiss/static/js/app.js | 491 ------- faiss/static/js/dataUriToBlob.js | 58 - faiss/static/js/metadata-app.js | 50 - faiss/static/js/store2.min.js | 5 - faiss/static/metadata.html | 11 - faiss/static/search.html | 1 - faiss/util.py | 29 - faiss/wsgi.py | 5 - megapixels/app/builder/README.md | 21 + megapixels/app/builder/builder.py | 90 ++ megapixels/app/builder/parser.py | 172 +++ megapixels/app/builder/paths.py | 6 + megapixels/app/builder/s3.py | 61 + megapixels/app/server/create.py | 10 +- megapixels/app/settings/app_cfg.py | 15 +- megapixels/commands/faiss/build_faiss.py | 2 - old/faiss/requirements.txt | 11 + old/faiss/run.sh | 1 + old/faiss/static/css/app.css | 289 +++++ old/faiss/static/favicon.ico | Bin 0 -> 15086 bytes old/faiss/static/img/play.png | Bin 0 -> 1231 bytes old/faiss/static/index.html | 83 ++ old/faiss/static/js/app.js | 491 +++++++ old/faiss/static/js/dataUriToBlob.js | 58 + old/faiss/static/js/metadata-app.js | 50 + old/faiss/static/js/store2.min.js | 5 + old/faiss/static/metadata.html | 11 + old/faiss/static/search.html | 1 + old/faiss/util.py | 29 + old/faiss/wsgi.py | 5 + old/server/app/README.md | 17 + old/server/app/__init__.py | 39 + old/server/app/basemodels.py | 5 + old/server/app/favicon.ico | Bin 0 -> 318 bytes old/server/app/index.html | 161 +++ old/server/app/main/__init__.py | 5 + old/server/app/main/errors.py | 32 + old/server/app/main/forms.py | 60 + old/server/app/main/img_proc_config.py | 20 + old/server/app/main/paths.py | 19 + old/server/app/main/tasks.py | 374 ++++++ old/server/app/main/utils.py | 37 + old/server/app/main/views.py | 300 +++++ old/server/app/static/css/bootstrap.min.css | 6 + old/server/app/static/css/dullbrown-theme.css | 502 +++++++ old/server/app/static/css/projector.css | 52 + old/server/app/static/js/app.js | 158 +++ old/server/app/static/js/upload.js | 319 +++++ old/server/app/static/js/util.js | 32 + old/server/app/static/js/vendor/ExifReader.js | 1363 ++++++++++++++++++++ old/server/app/static/js/vendor/canvas-to-blob.js | 111 ++ .../app/static/js/vendor/jquery-3.3.1.min.js | 2 + old/server/app/static/js/vendor/nanobar.min.js | 3 + old/server/app/static/js/vendor/prefixfree.js | 527 ++++++++ old/server/app/templates/403.html | 35 + old/server/app/templates/404.html | 33 + old/server/app/templates/500.html | 34 + old/server/app/templates/base.html | 33 + old/server/app/templates/celery.html | 43 + old/server/app/templates/display.html | 69 + old/server/app/templates/index.html | 161 +++ old/server/celery_worker.py | 7 + old/server/config.py | 78 ++ old/server/deploy.sh | 20 + old/server/dulldream.wsgi.py | 13 + old/server/run-celery.sh | 3 + old/server/run-dev.sh | 1 + old/server/run-gunicorn.sh | 2 + old/server/run-redis.sh | 2 + old/server/run.py | 12 + scraper/client/actions.js | 9 - scraper/client/app.js | 46 - scraper/client/common/activeLink.component.js | 16 - scraper/client/common/classifier.component.js | 99 -- scraper/client/common/common.css | 347 ----- scraper/client/common/detectionBoxes.component.js | 15 - scraper/client/common/detectionList.component.js | 16 - scraper/client/common/footer.component.js | 10 - scraper/client/common/gate.component.js | 21 - scraper/client/common/header.component.js | 1 - scraper/client/common/index.js | 36 - scraper/client/common/keyframe.component.js | 118 -- scraper/client/common/keyframes.component.js | 95 -- scraper/client/common/loader.component.js | 10 - scraper/client/common/sidebar.component.js | 37 - scraper/client/common/table.component.js | 121 -- scraper/client/common/video.component.js | 47 - scraper/client/index.js | 19 - scraper/client/metadata/index.js | 25 - scraper/client/session.js | 5 - scraper/client/store.js | 38 - scraper/client/types.js | 21 - scraper/client/util.js | 167 --- server/app/README.md | 17 - server/app/__init__.py | 39 - server/app/basemodels.py | 5 - server/app/favicon.ico | Bin 318 -> 0 bytes server/app/index.html | 161 --- server/app/main/__init__.py | 5 - server/app/main/errors.py | 32 - server/app/main/forms.py | 60 - server/app/main/img_proc_config.py | 20 - server/app/main/paths.py | 19 - server/app/main/tasks.py | 374 ------ server/app/main/utils.py | 37 - server/app/main/views.py | 300 ----- server/app/static/css/bootstrap.min.css | 6 - server/app/static/css/dullbrown-theme.css | 502 ------- server/app/static/css/projector.css | 52 - server/app/static/js/app.js | 158 --- server/app/static/js/upload.js | 319 ----- server/app/static/js/util.js | 32 - server/app/static/js/vendor/ExifReader.js | 1363 -------------------- server/app/static/js/vendor/canvas-to-blob.js | 111 -- server/app/static/js/vendor/jquery-3.3.1.min.js | 2 - server/app/static/js/vendor/nanobar.min.js | 3 - server/app/static/js/vendor/prefixfree.js | 527 -------- server/app/templates/403.html | 35 - server/app/templates/404.html | 33 - server/app/templates/500.html | 34 - server/app/templates/base.html | 33 - server/app/templates/celery.html | 43 - server/app/templates/display.html | 69 - server/app/templates/index.html | 161 --- server/celery_worker.py | 7 - server/config.py | 78 -- server/deploy.sh | 20 - server/dulldream.wsgi.py | 13 - server/run-celery.sh | 3 - server/run-dev.sh | 1 - server/run-gunicorn.sh | 2 - server/run-redis.sh | 2 - server/run.py | 12 - 168 files changed, 7409 insertions(+), 7404 deletions(-) delete mode 100644 builder/README.md delete mode 100644 builder/__init__.py delete mode 100644 builder/builder.py delete mode 100644 builder/parser.py delete mode 100644 builder/paths.py delete mode 100644 builder/s3.py create mode 100644 client/actions.js create mode 100644 client/app.js create mode 100644 client/common/activeLink.component.js create mode 100644 client/common/classifier.component.js create mode 100644 client/common/common.css create mode 100644 client/common/detectionBoxes.component.js create mode 100644 client/common/detectionList.component.js create mode 100644 client/common/footer.component.js create mode 100644 client/common/gate.component.js create mode 100644 client/common/header.component.js create mode 100644 client/common/index.js create mode 100644 client/common/keyframe.component.js create mode 100644 client/common/keyframes.component.js create mode 100644 client/common/loader.component.js create mode 100644 client/common/sidebar.component.js create mode 100644 client/common/table.component.js create mode 100644 client/common/video.component.js create mode 100644 client/index.js create mode 100644 client/metadata/index.js create mode 100644 client/session.js create mode 100644 client/store.js create mode 100644 client/types.js create mode 100644 client/util.js delete mode 100644 faiss/requirements.txt delete mode 100644 faiss/run.sh delete mode 100644 faiss/static/css/app.css delete mode 100644 faiss/static/favicon.ico delete mode 100644 faiss/static/img/play.png delete mode 100644 faiss/static/index.html delete mode 100644 faiss/static/js/app.js delete mode 100644 faiss/static/js/dataUriToBlob.js delete mode 100644 faiss/static/js/metadata-app.js delete mode 100644 faiss/static/js/store2.min.js delete mode 100644 faiss/static/metadata.html delete mode 100644 faiss/static/search.html delete mode 100644 faiss/util.py delete mode 100644 faiss/wsgi.py create mode 100644 megapixels/app/builder/README.md create mode 100644 megapixels/app/builder/builder.py create mode 100644 megapixels/app/builder/parser.py create mode 100644 megapixels/app/builder/paths.py create mode 100644 megapixels/app/builder/s3.py create mode 100644 old/faiss/requirements.txt create mode 100644 old/faiss/run.sh create mode 100644 old/faiss/static/css/app.css create mode 100644 old/faiss/static/favicon.ico create mode 100644 old/faiss/static/img/play.png create mode 100644 old/faiss/static/index.html create mode 100644 old/faiss/static/js/app.js create mode 100644 old/faiss/static/js/dataUriToBlob.js create mode 100644 old/faiss/static/js/metadata-app.js create mode 100644 old/faiss/static/js/store2.min.js create mode 100644 old/faiss/static/metadata.html create mode 100644 old/faiss/static/search.html create mode 100644 old/faiss/util.py create mode 100644 old/faiss/wsgi.py create mode 100644 old/server/app/README.md create mode 100644 old/server/app/__init__.py create mode 100644 old/server/app/basemodels.py create mode 100644 old/server/app/favicon.ico create mode 100644 old/server/app/index.html create mode 100644 old/server/app/main/__init__.py create mode 100644 old/server/app/main/errors.py create mode 100644 old/server/app/main/forms.py create mode 100644 old/server/app/main/img_proc_config.py create mode 100644 old/server/app/main/paths.py create mode 100644 old/server/app/main/tasks.py create mode 100644 old/server/app/main/utils.py create mode 100644 old/server/app/main/views.py create mode 100644 old/server/app/static/css/bootstrap.min.css create mode 100644 old/server/app/static/css/dullbrown-theme.css create mode 100644 old/server/app/static/css/projector.css create mode 100644 old/server/app/static/js/app.js create mode 100644 old/server/app/static/js/upload.js create mode 100644 old/server/app/static/js/util.js create mode 100644 old/server/app/static/js/vendor/ExifReader.js create mode 100644 old/server/app/static/js/vendor/canvas-to-blob.js create mode 100644 old/server/app/static/js/vendor/jquery-3.3.1.min.js create mode 100644 old/server/app/static/js/vendor/nanobar.min.js create mode 100644 old/server/app/static/js/vendor/prefixfree.js create mode 100644 old/server/app/templates/403.html create mode 100644 old/server/app/templates/404.html create mode 100644 old/server/app/templates/500.html create mode 100644 old/server/app/templates/base.html create mode 100644 old/server/app/templates/celery.html create mode 100644 old/server/app/templates/display.html create mode 100644 old/server/app/templates/index.html create mode 100644 old/server/celery_worker.py create mode 100644 old/server/config.py create mode 100755 old/server/deploy.sh create mode 100644 old/server/dulldream.wsgi.py create mode 100755 old/server/run-celery.sh create mode 100755 old/server/run-dev.sh create mode 100755 old/server/run-gunicorn.sh create mode 100755 old/server/run-redis.sh create mode 100644 old/server/run.py delete mode 100644 scraper/client/actions.js delete mode 100644 scraper/client/app.js delete mode 100644 scraper/client/common/activeLink.component.js delete mode 100644 scraper/client/common/classifier.component.js delete mode 100644 scraper/client/common/common.css delete mode 100644 scraper/client/common/detectionBoxes.component.js delete mode 100644 scraper/client/common/detectionList.component.js delete mode 100644 scraper/client/common/footer.component.js delete mode 100644 scraper/client/common/gate.component.js delete mode 100644 scraper/client/common/header.component.js delete mode 100644 scraper/client/common/index.js delete mode 100644 scraper/client/common/keyframe.component.js delete mode 100644 scraper/client/common/keyframes.component.js delete mode 100644 scraper/client/common/loader.component.js delete mode 100644 scraper/client/common/sidebar.component.js delete mode 100644 scraper/client/common/table.component.js delete mode 100644 scraper/client/common/video.component.js delete mode 100644 scraper/client/index.js delete mode 100644 scraper/client/metadata/index.js delete mode 100644 scraper/client/session.js delete mode 100644 scraper/client/store.js delete mode 100644 scraper/client/types.js delete mode 100644 scraper/client/util.js delete mode 100644 server/app/README.md delete mode 100644 server/app/__init__.py delete mode 100644 server/app/basemodels.py delete mode 100644 server/app/favicon.ico delete mode 100644 server/app/index.html delete mode 100644 server/app/main/__init__.py delete mode 100644 server/app/main/errors.py delete mode 100644 server/app/main/forms.py delete mode 100644 server/app/main/img_proc_config.py delete mode 100644 server/app/main/paths.py delete mode 100644 server/app/main/tasks.py delete mode 100644 server/app/main/utils.py delete mode 100644 server/app/main/views.py delete mode 100644 server/app/static/css/bootstrap.min.css delete mode 100644 server/app/static/css/dullbrown-theme.css delete mode 100644 server/app/static/css/projector.css delete mode 100644 server/app/static/js/app.js delete mode 100644 server/app/static/js/upload.js delete mode 100644 server/app/static/js/util.js delete mode 100644 server/app/static/js/vendor/ExifReader.js delete mode 100644 server/app/static/js/vendor/canvas-to-blob.js delete mode 100644 server/app/static/js/vendor/jquery-3.3.1.min.js delete mode 100644 server/app/static/js/vendor/nanobar.min.js delete mode 100644 server/app/static/js/vendor/prefixfree.js delete mode 100644 server/app/templates/403.html delete mode 100644 server/app/templates/404.html delete mode 100644 server/app/templates/500.html delete mode 100644 server/app/templates/base.html delete mode 100644 server/app/templates/celery.html delete mode 100644 server/app/templates/display.html delete mode 100644 server/app/templates/index.html delete mode 100644 server/celery_worker.py delete mode 100644 server/config.py delete mode 100755 server/deploy.sh delete mode 100644 server/dulldream.wsgi.py delete mode 100755 server/run-celery.sh delete mode 100755 server/run-dev.sh delete mode 100755 server/run-gunicorn.sh delete mode 100755 server/run-redis.sh delete mode 100644 server/run.py (limited to 'megapixels/app/server') diff --git a/builder/README.md b/builder/README.md deleted file mode 100644 index 1a6d3a1e..00000000 --- a/builder/README.md +++ /dev/null @@ -1,21 +0,0 @@ -Megapixels Static Site Generator -================================ - -The index, blog, and about other pages are built using this static site generator. - -## Metadata - -``` -status: published|draft|private -title: From 1 to 100 Pixels -desc: High resolution insights from low resolution imagery -slug: from-1-to-100-pixels -published: 2018-12-04 -updated: 2018-12-04 -authors: Adam Harvey, Berit Gilma, Matthew Stender -``` - -## S3 Assets - -Static assets: `v1/site/about/assets/picture.jpg` -Dataset assets: `v1/datasets/lfw/assets/picture.jpg` diff --git a/builder/__init__.py b/builder/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/builder/builder.py b/builder/builder.py deleted file mode 100644 index 620fc710..00000000 --- a/builder/builder.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/python - -from dotenv import load_dotenv -load_dotenv() - -import os -import glob -from jinja2 import Environment, FileSystemLoader, select_autoescape - -import s3 -import parser -from paths import * - -env = Environment( - loader=FileSystemLoader(template_path), - autoescape=select_autoescape([]) -) - -def build_page(fn, research_posts): - metadata, sections = parser.read_metadata(fn) - - if metadata is None: - print("{} has no metadata".format(fn)) - return - - print(metadata['url']) - - dirname = os.path.dirname(fn) - output_path = public_path + metadata['url'] - output_fn = os.path.join(output_path, "index.html") - - skip_h1 = False - - if metadata['url'] == '/': - template = env.get_template("home.html") - elif 'research/' in fn: - skip_h1 = True - template = env.get_template("research.html") - else: - template = env.get_template("page.html") - - if 'datasets/' in fn: - s3_dir = s3_datasets_path - else: - s3_dir = s3_site_path - - s3_path = s3.make_s3_path(s3_dir, metadata['path']) - - if 'index.md' in fn: - s3.sync_directory(dirname, s3_dir, metadata) - - content = parser.parse_markdown(sections, s3_path, skip_h1=skip_h1) - - html = template.render( - metadata=metadata, - content=content, - research_posts=research_posts, - latest_research_post=research_posts[-1], - ) - - os.makedirs(output_path, exist_ok=True) - with open(output_fn, "w") as file: - file.write(html) - - print("______") - -def build_research_index(research_posts): - metadata, sections = parser.read_metadata('../site/content/research/index.md') - template = env.get_template("page.html") - s3_path = s3.make_s3_path(s3_site_path, metadata['path']) - content = parser.parse_markdown(sections, s3_path, skip_h1=False) - content += parser.parse_research_index(research_posts) - html = template.render( - metadata=metadata, - content=content, - research_posts=research_posts, - latest_research_post=research_posts[-1], - ) - output_fn = public_path + '/research/index.html' - with open(output_fn, "w") as file: - file.write(html) - -def build_site(): - research_posts = parser.read_research_post_index() - for fn in glob.iglob(os.path.join(content_path, "**/*.md"), recursive=True): - build_page(fn, research_posts) - build_research_index(research_posts) - -if __name__ == '__main__': - build_site() diff --git a/builder/parser.py b/builder/parser.py deleted file mode 100644 index dd3643bf..00000000 --- a/builder/parser.py +++ /dev/null @@ -1,172 +0,0 @@ -import os -import re -import glob -import mistune - -import s3 -from paths import * - -renderer = mistune.Renderer(escape=False) -markdown = mistune.Markdown(renderer=renderer) - -def fix_images(lines, s3_path): - real_lines = [] - block = "\n\n".join(lines) - for line in block.split("\n"): - if "![" in line: - line = line.replace('![', '') - alt_text, tail = line.split('](', 1) - url, tail = tail.split(')', 1) - if ':' in alt_text: - tail, alt_text = alt_text.split(':', 1) - img_tag = "{}".format(s3_path + url, alt_text.replace("'", "")) - if len(alt_text): - line = "
    {}
    {}
    ".format(img_tag, alt_text) - else: - line = "
    {}
    ".format(img_tag, alt_text) - real_lines.append(line) - return "\n".join(real_lines) - -def format_section(lines, s3_path, type=''): - if len(lines): - lines = fix_images(lines, s3_path) - if type: - return "
    {}
    ".format(type, markdown(lines)) - else: - return "
    " + markdown(lines) + "
    " - return "" - -def format_metadata(section): - meta = [] - for line in section.split('\n'): - key, value = line[2:].split(': ', 1) - meta.append("
    {}
    {}
    ".format(key, value)) - return "
    {}
    ".format(''.join(meta)) - -def parse_markdown(sections, s3_path, skip_h1=False): - groups = [] - current_group = [] - for section in sections: - if skip_h1 and section.startswith('# '): - continue - elif section.startswith('+ '): - groups.append(format_section(current_group, s3_path)) - groups.append(format_metadata(section)) - current_group = [] - elif '![wide:' in section: - groups.append(format_section(current_group, s3_path)) - groups.append(format_section([section], s3_path, type='wide')) - current_group = [] - elif '![' in section: - groups.append(format_section(current_group, s3_path)) - groups.append(format_section([section], s3_path, type='images')) - current_group = [] - else: - current_group.append(section) - groups.append(format_section(current_group, s3_path)) - content = "".join(groups) - return content - -def parse_research_index(research_posts): - content = "
    " - for post in research_posts: - s3_path = s3.make_s3_path(s3_site_path, post['path']) - if 'image' in post: - post_image = s3_path + post['image'] - else: - post_image = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' - row = "
    Research post

    {}

    {}

    ".format( - post['path'], - post_image, - post['title'], - post['tagline']) - content += row - content += '
    ' - return content - -def read_metadata(fn): - with open(fn, "r") as file: - data = file.read() - data = data.replace("\n ", "\n") - if "\n" in data: - data = data.replace("\r", "") - else: - data = data.replace("\r", "\n") - sections = data.split("\n\n") - return parse_metadata(fn, sections) - -default_metadata = { - 'status': 'published', - 'title': 'Untitled Page', - 'desc': '', - 'slug': '', - 'published': '2018-12-31', - 'updated': '2018-12-31', - 'authors': 'Adam Harvey', - 'sync': 'true', - 'tagline': '', -} - -def parse_metadata_section(metadata, section): - for line in section.split("\n"): - if ': ' not in line: - continue - key, value = line.split(': ', 1) - metadata[key.lower()] = value - -def parse_metadata(fn, sections): - found_meta = False - metadata = {} - valid_sections = [] - for section in sections: - if not found_meta and ': ' in section: - found_meta = True - parse_metadata_section(metadata, section) - continue - if '-----' in section: - continue - if found_meta: - valid_sections.append(section) - - if 'title' not in metadata: - print('warning: {} has no title'.format(fn)) - for key in default_metadata: - if key not in metadata: - metadata[key] = default_metadata[key] - - basedir = os.path.dirname(fn.replace(content_path, '')) - basename = os.path.basename(fn) - if basedir == '/': - metadata['path'] = '/' - metadata['url'] = '/' - elif basename == 'index.md': - metadata['path'] = basedir + '/' - metadata['url'] = metadata['path'] - else: - metadata['path'] = basedir + '/' - metadata['url'] = metadata['path'] + basename.replace('.md', '') + '/' - - if metadata['status'] == 'published|draft|private': - metadata['status'] = 'published' - - metadata['sync'] = metadata['sync'] != 'false' - - metadata['author_html'] = '
    '.join(metadata['authors'].split(',')) - return metadata, valid_sections - -def read_research_post_index(): - posts = [] - for fn in sorted(glob.glob('../site/content/research/*/index.md')): - metadata, valid_sections = read_metadata(fn) - if metadata is None or metadata['status'] == 'private' or metadata['status'] == 'draft': - continue - posts.append(metadata) - if not len(posts): - posts.append({ - 'title': 'Placeholder', - 'slug': 'placeholder', - 'date': 'Placeholder', - 'url': '/', - }) - return posts - diff --git a/builder/paths.py b/builder/paths.py deleted file mode 100644 index 356f2f3d..00000000 --- a/builder/paths.py +++ /dev/null @@ -1,6 +0,0 @@ - -s3_site_path = "v1/site" -s3_datasets_path = "v1" # datasets is already in the filename -public_path = "../site/public" -content_path = "../site/content" -template_path = "../site/templates" diff --git a/builder/s3.py b/builder/s3.py deleted file mode 100644 index 41ecdf61..00000000 --- a/builder/s3.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import glob -import boto3 -from paths import * - -session = boto3.session.Session() - -s3_client = session.client( - service_name='s3', - aws_access_key_id=os.getenv('S3_KEY'), - aws_secret_access_key=os.getenv('S3_SECRET'), - endpoint_url=os.getenv('S3_ENDPOINT'), - region_name=os.getenv('S3_REGION'), -) - -def sync_directory(base_fn, s3_path, metadata): - fns = {} - for fn in glob.glob(os.path.join(base_fn, 'assets/*')): - fns[os.path.basename(fn)] = True - - if not metadata['sync']: - return - - remote_path = s3_path + metadata['url'] - - directory = s3_client.list_objects(Bucket=os.getenv('S3_BUCKET'), Prefix=remote_path) - prefixes = [] - - if 'Contents' in directory: - for obj in directory['Contents']: - s3_fn = obj['Key'] - fn = os.path.basename(s3_fn) - local_fn = os.path.join(base_fn, 'assets', fn) - if fn in fns: - del fns[fn] - if obj['LastModified'].timestamp() < os.path.getmtime(os.path.join(local_fn)): - print("s3 update {}".format(s3_fn)) - s3_client.upload_file( - local_fn, - os.getenv('S3_BUCKET'), - s3_fn, - ExtraArgs={ 'ACL': 'public-read' }) - else: - print("s3 delete {}".format(s3_fn)) - response = s3_client.delete_object( - Bucket=os.getenv('S3_BUCKET'), - Key=s3_fn, - ) - - for fn in fns: - local_fn = os.path.join(base_fn, 'assets', fn) - s3_fn = os.path.join(remote_path, 'assets', fn) - print("s3 create {}".format(s3_fn)) - s3_client.upload_file( - local_fn, - os.getenv('S3_BUCKET'), - s3_fn, - ExtraArgs={ 'ACL': 'public-read' }) - -def make_s3_path(s3_dir, metadata_path): - return "{}/{}/{}{}".format(os.getenv('S3_ENDPOINT'), os.getenv('S3_BUCKET'), s3_dir, metadata_path) diff --git a/client/actions.js b/client/actions.js new file mode 100644 index 00000000..ba899f06 --- /dev/null +++ b/client/actions.js @@ -0,0 +1,9 @@ +import * as search from './search/search.actions' +import * as review from './review/review.actions' +import * as metadata from './metadata/metadata.actions' + +export { + search, + review, + metadata, +} diff --git a/client/app.js b/client/app.js new file mode 100644 index 00000000..6c008ec6 --- /dev/null +++ b/client/app.js @@ -0,0 +1,46 @@ +import React, { Component } from 'react' +import { ConnectedRouter } from 'connected-react-router' +import { Route, Switch } from 'react-router' + +import { Header, Sidebar, Footer } from './common' +import * as Metadata from './metadata' +import * as Search from './search' +import * as Review from './review' + +export default class App extends Component { + render() { + return ( + +
    +
    +
    + +
    + + + + + + + + + + + + +

    NOT FOUND

    } /> + + + + + + +
    +
    +
    +
    +
    +
    + ) + } +} diff --git a/client/common/activeLink.component.js b/client/common/activeLink.component.js new file mode 100644 index 00000000..59f63881 --- /dev/null +++ b/client/common/activeLink.component.js @@ -0,0 +1,16 @@ +import React from 'react' +import { NavLink } from 'react-router-dom' + +export default function ActiveLink({ + to, + className = 'navlink', + children +}) { + return ( + + + {children} + + + ) +} diff --git a/client/common/classifier.component.js b/client/common/classifier.component.js new file mode 100644 index 00000000..af6a4934 --- /dev/null +++ b/client/common/classifier.component.js @@ -0,0 +1,99 @@ +import React, { Component } from 'react' +import { courtesyS } from '../util' + +import { TableTuples, DetectionList, Keyframe } from '.' + +export default class Classifier extends Component { + render() { + const { + tag, + sha256, + verified, + keyframes = {}, + labels, + summary, + aspectRatio = 1.777, + showAll, + } = this.props + let totalDetections = 0 + const keys = Object + .keys(keyframes) + .map(s => parseInt(s, 10)) + const validKeyframes = keys + .sort((a, b) => a - b) + .map(frame => { + const detections = keyframes[frame] + if (detections.length || showAll) { + totalDetections += detections.length + return { frame, detections } + } + return null + }) + .filter(f => !!f) + const detectionLookup = validKeyframes + .reduce((a, b) => { + b.detections.reduce((aa, { idx }) => { + if (!(idx in aa)) aa[idx] = [labels[idx], 0] + aa[idx][1] += 1 + return aa + }, a) + return a + }, {}) + const detectionCounts = Object.keys(detectionLookup) + .map(idx => detectionLookup[idx]) + .sort((a, b) => b[1] - a[1]) + + if (summary) { + return ( +
    +

    {tag}{' Detections'}

    + +
    + ) + } + return ( +
    +

    {tag}

    +

    Detections

    + +

    Frames

    +
      +
    • + {'Displaying '}{validKeyframes.length}{' / '}{courtesyS(keys.length, 'frame')} +
    • +
    • + {courtesyS(totalDetections, 'detection')}{' found'} +
    • +
    +
    + {validKeyframes.map(({ frame, detections }) => ( + + + + ))} +
    +
    + ) + } +} diff --git a/client/common/common.css b/client/common/common.css new file mode 100644 index 00000000..4b939df0 --- /dev/null +++ b/client/common/common.css @@ -0,0 +1,347 @@ +/* css boilerplate */ + +* { box-sizing: border-box; } +html,body { + margin: 0; padding: 0; + width: 100%; height: 100%; +} +body { + font-family: Helvetica, sans-serif; + font-weight: 300; +} + +h1 { + +} +h2 { + font-weight: normal; + margin: 10px 0; + padding: 3px; + font-size: 24px; +} +h3 { + font-weight: normal; + margin: 10px 0 0 0; + padding: 3px; + font-size: 18px; +} +h4 { + font-weight: 300; + font-size: 12px; + letter-spacing: 2px; + color: #888; + text-transform: uppercase; + margin: 5px 10px; + margin-top: 20px; +} +h4:first-child { + margin-top: 10px; +} + +.app { + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: flex-start; +} + +/* header stuff */ + +header { + width: 100%; + background: #11f; + color: white; + align-items: stretch; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + z-index: 3; +} +header > section { + justify-content: flex-start; + align-items: center; + display: flex; + flex: 1 0; + font-weight: bold; +} +header > section:last-of-type { + justify-content: flex-end; +} + +/* sidebar / body columns */ + +.sidebar { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + height: 100%; + float: left; + width: 200px; + flex: 0 0 200px; + padding: 10px; + margin-right: 10px; +} +.sidebar a { + display: block; + padding: 10px 10px; + text-decoration: none; + color: #444; +} +.sidebar a.active { + font-weight: bold; + color: #222; +} +.body { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + flex-grow: 1; +} +.body > div { + padding-bottom: 40px; +} + +/* buttons / forms */ + +.btn:focus, .btn:hover { + background: #f1f1fc; + color: #4b48d6 !important; + text-decoration: none; +} +.btn { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: #fff; + border: .05rem solid; + border-radius: 2px; + margin-right: 5px; + color: #11f; + cursor: pointer; + display: inline-block; + font-size: .8rem; + height: 1.8rem; + line-height: 1rem; + outline: none; + padding: .35rem .4rem; + text-align: center; + text-decoration: none; + -webkit-transition: all .2s ease; + -o-transition: all .2s ease; + transition: all .2s ease; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + vertical-align: middle; + white-space: nowrap; +} +.btn.reset, +.btn.panic { + color: #b00; +} +.btn.btn-primary { + background: #11f; + border-color: #11f; + color: white; +} +.btn[disabled] { + color: #bbb !important; + border-color: #bbb !important; + background: white !important; + cursor: default; +} +.btn.btn-primary:focus, +.btn.btn-primary:hover { + background: #0808ee; + color: white !important; +} +.row .btn { + margin: 0 5px 0 0; +} +input[type=text] { + border: 1px solid #888; + padding: 4px; + font-size: 15px; +} + + +/* tables on metadata pages */ + +table { + border: 0; + margin: 0; + padding: 0; + border-spacing: 0; +} +.tableObject td, +.tableObject th { + padding: 3px; + vertical-align: top; +} +.tableObject hr { + width: 100%; + color: transparent; + border: 0; + border-bottom: 1px solid #bbb; + align: left; + margin: 3px 0; + padding: 0; +} +.tableObject th, +.tableTuples th { + min-width: 145px; + text-align: left; + text-transform: capitalize; + padding: 3px; + padding-right: 10px; + font-weight: 300; + color: #333; +} +.tableTuples td { + text-align: right; + padding: 3px; +} +.tableObject td { + font-weight: normal; + color: #000; +} +.tableObject .tableObject { + border: 1px solid #ddd; +} +.tableArray { + border: 1px solid #ddd; + border-spacing: 0; +} +.tableArray td { + border-bottom: 1px solid #ddd; +} +.gray { + font-size: 12px; + color: #888; + display: block; +} +.sha256.heading { + margin: 20px 0 0px; +} +.gray span { + padding-right: 5px; +} +.gray { + margin-bottom: 10px; +} +.gray a { + color: #666; +} + +.verified { + color: #080; + font-weight: bold; +} +.unverified { + color: #f00; + font-weight: 300; +} + +.loading, .error { + font-weight: normal; + margin: 10px 0; + padding: 3px; + font-size: 24px; +} + +.title { + text-transform: capitalize; +} +.rect { + position: absolute; +} +.rect { border: 1px solid rgba(0,0,255); background-color: rgba(0,0,255,0.1); } + +/* videos / video preloader */ + +video { + max-width: 640px; + margin: 10px 0; +} +.video { + margin: 0 0 10px 0; +} +.video .bg { + cursor: pointer; + position: relative; + background-size: cover; +} +.video .play { + position: absolute; + top: 50%; + left: 50%; + transform: translate3d(-50%, -50%, 0); + width: 20%; + height: 20%; + background-image: url(/search/static/img/play.png); + background-position: center center; + background-size: contain; + background-repeat: no-repeat; +} +.desktop .video .play:hover { + -webkit-filter: invert(60%) sepia(100%) saturate(500%) hue-rotate(160deg); +} + +/* spectre.css loader */ + +.loaderWrapper { + display: inline-block; + position: relative; + width: .8rem; + height: .8rem; + padding: 10px; +} +.loader { + color: transparent !important; + min-height: .8rem; + pointer-events: none; + position: relative; +} + +.loader::after { + animation: loader 500ms infinite linear; + border: .1rem solid #5755d9; + border-radius: 50%; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: .8rem; + left: 50%; + margin-left: -.4rem; + margin-top: -.4rem; + position: absolute; + top: 50%; + width: .8rem; + z-index: 1; +} + +.loader.loader-lg { + min-height: 2rem; +} + +.loader.loader-lg::after { + height: 1.6rem; + margin-left: -.8rem; + margin-top: -.8rem; + width: 1.6rem; +} + +@keyframes loader { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/client/common/detectionBoxes.component.js b/client/common/detectionBoxes.component.js new file mode 100644 index 00000000..c4872ea8 --- /dev/null +++ b/client/common/detectionBoxes.component.js @@ -0,0 +1,15 @@ +import React from 'react' + +import { px } from '../util' + +export default function DetectionBoxes({ detections, width, height }) { + return detections.map(({ rect }, i) => ( + rect && +
    + )) +} diff --git a/client/common/detectionList.component.js b/client/common/detectionList.component.js new file mode 100644 index 00000000..416e66d8 --- /dev/null +++ b/client/common/detectionList.component.js @@ -0,0 +1,16 @@ +import React from 'react' + +export default function DetectionList({ detections, labels, tag, showEmpty }) { + return ( + + {tag &&

    {tag}

    } + {!detections.length && showEmpty && } + {detections.map(({ idx, score, rect }, i) => ( + + ))} +
    + ) +} diff --git a/client/common/footer.component.js b/client/common/footer.component.js new file mode 100644 index 00000000..7c82b44b --- /dev/null +++ b/client/common/footer.component.js @@ -0,0 +1,10 @@ +import React from 'react' +import { Link } from 'react-router-dom' +import { connect } from 'react-redux' + +export default function Footer(props) { + return ( +
    +
    + ); +} diff --git a/client/common/gate.component.js b/client/common/gate.component.js new file mode 100644 index 00000000..9bf9287b --- /dev/null +++ b/client/common/gate.component.js @@ -0,0 +1,21 @@ +import React from 'react' +import { connect } from 'react-redux' + +function Gate(props) { + const { app, tag, View } = props + const data = app[tag] + if (!data) return null + if (data === 'loading') { + return
    {tag}{': Loading'}
    + } + if (data.err) { + return
    {tag}{' Error: '}{data.err}
    + } + return +} + +const mapStateToProps = state => ({ + app: state.metadata +}) + +export default connect(mapStateToProps)(Gate) diff --git a/client/common/header.component.js b/client/common/header.component.js new file mode 100644 index 00000000..84fe306f --- /dev/null +++ b/client/common/header.component.js @@ -0,0 +1 @@ +/* imported from main vcat application */ diff --git a/client/common/index.js b/client/common/index.js new file mode 100644 index 00000000..ad9fe5e1 --- /dev/null +++ b/client/common/index.js @@ -0,0 +1,36 @@ +import Header from 'vcat-header' + +import ActiveLink from './activeLink.component' +import Classifier from './classifier.component' +import DetectionBoxes from './detectionBoxes.component' +import DetectionList from './detectionList.component' +// import Header from './header.component' +import Footer from './footer.component' +import Loader from './loader.component' +import Sidebar from './sidebar.component' +import Gate from './gate.component' +import Keyframe from './keyframe.component' +import Keyframes from './keyframes.component' +import Video from './video.component' +import { TableObject, TableArray, TableTuples, TableRow, TableCell } from './table.component' +import './common.css' + +export { + Header, + Footer, + Sidebar, + Loader, + Gate, + TableObject, + TableArray, + TableTuples, + TableRow, + TableCell, + ActiveLink, + Classifier, + DetectionList, + DetectionBoxes, + Keyframe, + Keyframes, + Video, +} diff --git a/client/common/keyframe.component.js b/client/common/keyframe.component.js new file mode 100644 index 00000000..c77db3ac --- /dev/null +++ b/client/common/keyframe.component.js @@ -0,0 +1,118 @@ +import React from 'react' +import { Link } from 'react-router-dom' +import { imageUrl, timestamp, keyframeUri, widths, verify } from '../util' +import { DetectionBoxes } from '.' + +import * as searchActions from '../search/search.actions' + +export default function Keyframe({ + verified, + sha256, + frame, + score, + isSaved, + fps = 25, + size = 'th', + className, + showHash, + showFrame, + showTimestamp, + showScore, + showSearchButton, + showSaveButton, + to, + children, + detectionList = [], + aspectRatio = 1.777, + onClick, + reviewActions, +}) { + if (!sha256) return null + const width = widths[size] + const height = Math.round(width / aspectRatio) + return ( +
    +
    + + {'Frame + {detectionList.map(({ labels, detections }, i) => ( + + ))} + + {(reviewActions && (showSearchButton || showSaveButton)) && + + } +
    + {(showHash || showFrame || showTimestamp || showScore) && + + } + {children} +
    + ) +} + +const PossiblyExternalLink = props => { + if (props.onClick) { + return props.children + } + if (props.to.match(/^http/)) { + return {props.children} + } + return +} diff --git a/client/common/keyframes.component.js b/client/common/keyframes.component.js new file mode 100644 index 00000000..62eda45e --- /dev/null +++ b/client/common/keyframes.component.js @@ -0,0 +1,95 @@ +import React from 'react' +import { Link } from 'react-router-dom' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import { Keyframe } from '.' +import * as reviewActions from '../review/review.actions' +import * as searchActions from '../search/search.actions' + +function Keyframes(props) { + // console.log(props) + let { + frames, + groupByHash, + } = props + let minDistance = 0 + if (frames && frames.length) { + minDistance = frames[0].distance || 0 + } + if (!groupByHash) { + return ( + + ) + } + const frameGroups = frames.reduce((a, b) => { + if (a[b.hash]) { + a[b.hash].push(b) + } else { + a[b.hash] = [b] + } + return a + }, {}) + return Object.keys(frameGroups) + .map(hash => [frameGroups[hash].length, hash]) + .sort((a, b) => b[0] - a[0]) + .map(([count, hash]) => ( + + )) +} + +function KeyframeList(props) { + let { + saved = {}, + frames, + options, + review, + search, + minDistance, + label, + count, + ...frameProps + } = props + if (!frames) return null + return ( +
    + {label &&

    {label} ({count})

    } + {frames.map(({ hash, frame, verified, distance }) => ( + review.toggleSaved({ verified, hash, frame })} + reviewActions={review} + {...frameProps} + /> + ))} +
    + ) +} + +const mapStateToProps = state => ({ + saved: state.review.saved, + options: state.search.options, +}) + +const mapDispatchToProps = dispatch => ({ + review: bindActionCreators({ ...reviewActions }, dispatch), + search: bindActionCreators({ ...searchActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(Keyframes) diff --git a/client/common/loader.component.js b/client/common/loader.component.js new file mode 100644 index 00000000..6795424b --- /dev/null +++ b/client/common/loader.component.js @@ -0,0 +1,10 @@ +import React, { Component } from 'react' + +export default function Loader() { + return ( +
    +
    +
    +
    + ) +} \ No newline at end of file diff --git a/client/common/sidebar.component.js b/client/common/sidebar.component.js new file mode 100644 index 00000000..487f3289 --- /dev/null +++ b/client/common/sidebar.component.js @@ -0,0 +1,37 @@ +import React, { Component } from 'react' +import { NavLink } from 'react-router-dom' +import { connect } from 'react-redux' + +class Sidebar extends Component { + render() { + const { hash } = this.props + if (!hash) { + return ( +
    +
    + ) + } + return ( +
    +

    Media

    + Summary + Media Record + Media Info + Sugarcube + +

    Keyframes

    + Keyframe + +

    Detectors

    + Places 365 + Coco +
    + ) + } +} + +const mapStateToProps = state => ({ + hash: state.metadata.hash, +}) + +export default connect(mapStateToProps)(Sidebar) diff --git a/client/common/table.component.js b/client/common/table.component.js new file mode 100644 index 00000000..76a1d57c --- /dev/null +++ b/client/common/table.component.js @@ -0,0 +1,121 @@ +import React from 'react' + +import { formatName } from '../util' + +const __HR__ = '__HR__' + +export function TableObject({ tag, object, order, summary }) { + if (!object) return null + if (object === 'loading') { + return
    {tag}{': Loading'}
    + } + if (object.err) { + return
    {tag}{' Error: '}{object.err}
    + } + let objects = Object.keys(object) + if (order) { + const grouped = objects.reduce((a, b) => { + const index = order.indexOf(b) + if (index !== -1) { + a.order.push([index, b]) + } else { + a.alpha.push(b) + } + return a + }, { order: [], alpha: [] }) + objects = grouped.order + .sort((a, b) => a[0] - b[0]) + .map(([i, s]) => s) + if (!summary) { + objects = objects + // .concat([__HR__]) + .concat(grouped.alpha.sort()) + } + } else { + objects = objects.sort() + } + return ( +
    + {tag &&

    {tag}

    } +
    Title
    + + {objects.map((key, i) => ( + + ))} + +
    +
    + ) +} + +export function TableArray({ tag, list }) { + if (!list) return null + return ( +
    + {tag &&

    {tag}

    } + + + {list.map((value, i) => ( + + + + ))} + +
    +
    + ) +} + +export function TableTuples({ tag, list }) { + if (!list) return null + return ( +
    + {tag &&

    {tag}

    } + + + {list.map(([key, ...values], i) => ( + + + {values.map((value, j) => ( + + ))} + + ))} + +
    {formatName(key)}
    +
    + ) +} + +export function TableRow({ name, value }) { + if (name === __HR__) { + return ( + + +
    + + + ) + } + return ( + + {formatName(name)} + + + ) +} + +export function TableCell({ value }) { + if (value && typeof value === 'object') { + if (value._raw) { + value = value.value + } else if (value.length) { + value = + } else { + value = + } + } + return ( + {value} + ) +} diff --git a/client/common/video.component.js b/client/common/video.component.js new file mode 100644 index 00000000..e5525bf6 --- /dev/null +++ b/client/common/video.component.js @@ -0,0 +1,47 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { imageUrl, widths } from '../util' + +import { Gate } from '.' + +class Video extends Component { + state = { + playing: false, + } + + render() { + const { app, data, size } = this.props + const { playing } = this.state + const { sugarcube } = data.metadata + const url = sugarcube.fp.replace('/var/www/files/', 'https://cube.syrianarchive.org/') + const { sha256, verified } = app.mediainfo + const { video } = app.mediainfo.metadata.mediainfo + const keyframe = app.keyframe.metadata.keyframe.basic[0] + return ( +
    + {playing + ?
    + ) + } +} + +const mapStateToProps = () => ({ + tag: 'sugarcube', +}) + +export default connect(mapStateToProps)(props => ( + +)) diff --git a/client/index.js b/client/index.js new file mode 100644 index 00000000..eddc5fb2 --- /dev/null +++ b/client/index.js @@ -0,0 +1,19 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import { AppContainer } from 'react-hot-loader' +import { Provider } from 'react-redux' + +import App from './app' + +import { store, history } from './store' + +const container = document.createElement('div') +document.body.appendChild(container) + +ReactDOM.render( + + + + + , container +) diff --git a/client/metadata/index.js b/client/metadata/index.js new file mode 100644 index 00000000..0eef814e --- /dev/null +++ b/client/metadata/index.js @@ -0,0 +1,25 @@ +import Heading from './heading.component' +import MediaInfo from './mediaInfo.component' +import MediaRecord from './mediaRecord.component' +import Summary from './summary.component' +import KeyframeList from './keyframeList.component' +import KeyframeSingle from './keyframeSingle.component' +import KeyframeStatus from './keyframeStatus.component' +import Coco from './coco.component' +import Places365 from './places365.component' +import Sugarcube from './sugarcube.component' + +import './metadata.css' + +export { + Heading, + MediaRecord, + MediaInfo, + Summary, + KeyframeList, + KeyframeSingle, + KeyframeStatus, + Coco, + Places365, + Sugarcube, +} diff --git a/client/session.js b/client/session.js new file mode 100644 index 00000000..5bfae7eb --- /dev/null +++ b/client/session.js @@ -0,0 +1,5 @@ +import Storage from 'store2' + +const session = Storage.namespace('vcat.search') + +export default session diff --git a/client/store.js b/client/store.js new file mode 100644 index 00000000..043af351 --- /dev/null +++ b/client/store.js @@ -0,0 +1,38 @@ +import { applyMiddleware, compose, combineReducers, createStore } from 'redux' +import { connectRouter, routerMiddleware } from 'connected-react-router' +import { createBrowserHistory } from 'history' +import thunk from 'redux-thunk' +import { login } from './util' + +import metadataReducer from './metadata/metadata.reducer' +import searchReducer from './search/search.reducer' +import reviewReducer from './review/review.reducer' + +const rootReducer = combineReducers({ + auth: (state = login()) => state, + metadata: metadataReducer, + search: searchReducer, + review: reviewReducer, +}) + +function configureStore(initialState = {}, history) { + const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose + + const store = createStore( + connectRouter(history)(rootReducer), // new root reducer with router state + initialState, + composeEnhancers( + applyMiddleware( + thunk, + routerMiddleware(history) + ), + ), + ) + + return store +} + +const history = createBrowserHistory() +const store = configureStore({}, history) + +export { store, history } diff --git a/client/types.js b/client/types.js new file mode 100644 index 00000000..e3c64691 --- /dev/null +++ b/client/types.js @@ -0,0 +1,21 @@ +export const asType = (type, name) => [type, name].join('_').toUpperCase() +export const tagAsType = (type, names) => ( + names.reduce((tags, name) => { + tags[name] = asType(type, name) + return tags + }, {}) +) + +export const metadata = tagAsType('metadata', [ + 'loading', 'loaded', 'loaded_many', 'error', 'set_hash' +]) + +export const search = tagAsType('search', [ + 'loading', 'loaded', 'error', 'panic', 'update_options', +]) + +export const review = tagAsType('review', [ + 'loading', 'loaded', 'error', 'save', 'unsave', 'refresh', 'clear', 'dedupe', 'create', 'set_count' +]) + +export const init = '@@INIT' diff --git a/client/util.js b/client/util.js new file mode 100644 index 00000000..ad303c64 --- /dev/null +++ b/client/util.js @@ -0,0 +1,167 @@ +/* Mobile check */ + +export const isiPhone = !!((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))) +export const isiPad = !!(navigator.userAgent.match(/iPad/i)) +export const isAndroid = !!(navigator.userAgent.match(/Android/i)) +export const isMobile = isiPhone || isiPad || isAndroid +export const isDesktop = !isMobile + +const htmlClassList = document.body.parentNode.classList +htmlClassList.add(isDesktop ? 'desktop' : 'mobile') + +/* Default image dimensions */ + +export const widths = { + th: 160, + sm: 320, + md: 640, + lg: 1280, +} + +/* Formatting functions */ + +const acronyms = 'id url cc sa fp md5 sha256'.split(' ').map(s => '_' + s) +const acronymsUpperCase = acronyms.map(s => s.toUpperCase()) + +export const formatName = s => { + acronyms.forEach((acronym, i) => s = s.replace(acronym, acronymsUpperCase[i])) + return s.replace(/_/g, ' ') +} + +// Use to pad frame numbers with zeroes +export const pad = (n, m) => { + let s = String(n || 0) + while (s.length < m) { + s = '0' + s + } + return s +} + +// Verified is 0/1 when retrieved from SQL, but 'verified' or 'unverified' when retrieved elsewhere +export const isVerified = verified => verified === 1 || verified === '1' || verified === 'verified' +export const verify = verified => isVerified(verified) ? 'verified' : 'unverified' + +export const courtesyS = (n, s) => n + ' ' + (n === 1 ? s : s + 's') + +export const padSeconds = n => n < 10 ? '0' + n : n + +export const timestamp = (n = 0, fps = 25) => { + n /= fps + let s = padSeconds(Math.round(n) % 60) + n = Math.floor(n / 60) + if (n > 60) { + return Math.floor(n / 60) + ':' + padSeconds(n % 60) + ':' + s + } + return (n % 60) + ':' + s +} + +export const percent = n => (n * 100).toFixed(1) + '%' + +export const px = (n, w) => Math.round(n * w) + 'px' + +export const clamp = (n, a, b) => n < a ? a : n < b ? n : b + +/* URLs */ + +export const hashPath = sha256 => { + if (!sha256 || sha256.length < 9) { + throw new Error('Invalid sha256') + } + return [ + sha256.slice(0, 3), + sha256.slice(3, 6), + sha256.slice(6, 9), + sha256, + ].join('/') +} + +export const imageUrl = (verified, sha256, frame, size = 'th') => [ + 'https://' + process.env.S3_HOST + '/v1/media/keyframes', + isVerified(verified) ? null : 'unverified', + hashPath(sha256), + pad(frame, 6), + size, + 'index.jpg' +].filter(s => !!s).join('/') + +export const metadataUri = (sha256, tag) => '/metadata/' + sha256 + '/' + tag + '/' +export const keyframeUri = (sha256, frame) => '/metadata/' + sha256 + '/keyframe/' + pad(frame, 6) + '/' + +export const preloadImage = opt => { + let { verified, hash, frame, url } = opt + if (hash && frame) { + url = imageUrl(verified, hash, frame, 'md') + } + const image = new Image() + let loaded = false + image.onload = () => { + if (loaded) return + loaded = true + image.onload = null + } + // console.log(img.src) + image.crossOrigin = 'anonymous' + image.src = url + if (image.complete) { + image.onload() + } +} + +/* AJAX */ + +let cachedAuth = null +let token = '' +let username = '' + +export const post = (uri, data, credentials) => { + login() + let headers + if (data instanceof FormData) { + headers = { + Accept: 'application/json, application/xml, text/play, text/html, *.*', + } + } else { + headers = { + Accept: 'application/json, application/xml, text/play, text/html, *.*', + 'Content-Type': 'application/json; charset=utf-8', + } + data = JSON.stringify(data) + } + let opt = { + method: 'POST', + body: data, + headers, + credentials: 'include', + } + if (credentials) { + headers.Authorization = 'Token ' + token + } + // console.log(headers) + // headers['X-CSRFToken'] = csrftoken + return fetch(uri, opt).then(res => res.json()) +} + +// api queries +export const login = () => { + if (cachedAuth) return cachedAuth + const isLocal = (window.location.hostname === '0.0.0.0' || window.location.hostname === '127.0.0.1') + try { + const auth = JSON.parse(JSON.parse(localStorage.getItem('persist:root')).auth) + // console.log('auth', auth) + token = auth.token + username = auth.user.username + if (token) { + console.log('logged in', username) + } + cachedAuth = auth + if (!token && !isLocal) { + window.location.href = '/' + } + return auth + } catch (e) { + if (!isLocal) { + window.location.href = '/' + } + return {} + } +} diff --git a/faiss/requirements.txt b/faiss/requirements.txt deleted file mode 100644 index 1d60aabc..00000000 --- a/faiss/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -Pillow -h5py -tensorflow -Keras -Flask -opencv-python -imagehash -scikit-image -scikit-learn -imutils - diff --git a/faiss/run.sh b/faiss/run.sh deleted file mode 100644 index 8f9e77e2..00000000 --- a/faiss/run.sh +++ /dev/null @@ -1 +0,0 @@ -uwsgi --http 127.0.0.1:5000 --file wsgi.py --callable app --processes 1 diff --git a/faiss/static/css/app.css b/faiss/static/css/app.css deleted file mode 100644 index a3b24736..00000000 --- a/faiss/static/css/app.css +++ /dev/null @@ -1,289 +0,0 @@ -/* css boilerplate */ - -* { box-sizing: border-box; } -html,body { - margin: 0; padding: 0; - width: 100%; height: 100%; -} -body { - font-family: Helvetica, sans-serif; - font-weight: 300; - padding-top: 60px; -} - -/* header */ - -header { - position: fixed; - top: 0; - left: 0; - height: 60px; - width: 100%; - background: #11f; - color: white; - align-items: stretch; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - z-index: 3; -} -header > section { - justify-content: flex-start; - align-items: center; - display: flex; - flex: 1 0; - font-weight: bold; -} -header > section:last-of-type { - justify-content: flex-end; -} -header a { - color: hsla(0,0%,100%,.89); - text-decoration: none; - line-height: 18px; - font-size: 14px; - font-weight: 700; - padding: .35rem .4rem; - white-space: nowrap; -} -header .logged-in { - font-size: 12px; - font-weight: normal; - padding: 0 0.5rem; -} -header .logout { - padding: 0 6px; - border-left: 1px solid #99f; -} -header .logout a { - font-size: 12px; -} -.menuToggle { - width: 30px; - height: 30px; - margin: 5px; - cursor: pointer; - line-height: 1; -} - -/* form at the top */ - -#form { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin: 20px; - padding: 20px; - border: 1px solid #ddd; -} -input[type=text] { - border: 1px solid #888; - padding: 4px; - font-size: 15px; -} -input[type=file] { - max-width: 200px; - border-radius: 2px; -} -input[type=file]:invalid + button { visibility: hidden!important; } -input[type=file]:valid + button { visibility: visible!important; } -#form > div { - display: flex; - flex-direction: row; - align-items: center; -} -#form > div * { - margin: 0 3px; -} - -/* saving UI form */ - -label { - display: block; - white-space: nowrap; - padding-bottom: 10px; -} -label:last-child { - padding-bottom: 0; -} -label span { - display: inline-block; - min-width: 80px; -} -.saving_ui { - display: none; -} -.saving .saving_ui { - display: flex; - border: 1px solid #ddd; - margin: 20px; - padding: 20px; - flex-direction: row; - justify-content: space-between; -} - -/* query box, shows either searched image, directory name, etc */ - -.loading .results, -.prefetch .query, .prefetch .results, -.browsing .score, .browsing .browse, -.photo .browse, -.saving .score { - display: none; -} -.browsing .query div { display: inline; margin-left: 5px; font-weight: bold; } -.saving .query div { display: inline; margin-left: 5px; font-weight: bold; } -.load_message { - opacity: 0; -} -.loading .load_message { - display: block; - margin: 20px; - font-weight: bold; -} - -.query { - margin: 20px; -} -.query > div { - margin-top: 10px; - position: relative; - display: flex; - flex-direction: row; - align-items: flex-start; -} -.query img { - cursor: crosshair; - max-width: 400px; - display: block; -} -.query > div > .box { - position: absolute; - border: 1px solid #11f; - background: rgba(17,17,255,0.1); - pointer-events: none; -} -.query canvas { - margin-left: 20px; - max-width: 200px; -} - -/* search results */ - -.results { - display: flex; - flex-direction: row; - flex-wrap: wrap; -} -.results > div { - display: flex; - flex-direction: column; - justify-content: flex-end; - width: 210px; - margin: 15px; - padding: 5px; - border: 1px solid transparent; -} -.results > div.saved { - border-radius: 2px; - background: #fafaaa; -} -.results > div img { - cursor: pointer; - max-width: 210px; - margin-bottom: 10px; -} -.results > div > div { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; -} -.results a:visited .btn { - color: #99d; -} -.score { - font-size: 12px; - color: #444; -} - - -/* spinner */ - -.loader { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - top: 0; left: 0; - width: 100%; height: 100%; - background: rgba(255,255,255,0.9); -} -.loader > div { - background: white; - padding: 20px; - box-shadow: 0 1px 2px #bbb; - border-radius: 2px; -} -.spinner { - position: relative; - width: 32px; - height: 32px; - color: #11f; - margin: 0 auto; -} -.spinner:after { - position: absolute; - margin: auto; - width: 100%; - height: 100%; - top: 0; - left: 0; - right: 0; - bottom: 0; - content: " "; - display: inline-block; - border-radius: 50%; - border-style: solid; - border-width: 0.15em; - -webkit-background-clip: padding-box; - border-color: currentColor currentColor currentColor transparent; - box-sizing: border-box; - -webkit-animation: ld-cycle 0.7s infinite linear; - animation: ld-cycle 0.7s infinite linear; -} -@-webkit-keyframes ld-cycle { - 0%, 50%, 100% { - animation-timing-function: cubic-bezier(0.5, 0.5, 0.5, 0.5); - } - 0% { - -webkit-transform: rotate(0); - transform: rotate(0); - } - 50% { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes ld-cycle { - 0%, 50%, 100% { - animation-timing-function: cubic-bezier(0.5, 0.5, 0.5, 0.5); - } - 0% { - -webkit-transform: rotate(0); - transform: rotate(0); - } - 50% { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} diff --git a/faiss/static/favicon.ico b/faiss/static/favicon.ico deleted file mode 100644 index d97f2f59..00000000 Binary files a/faiss/static/favicon.ico and /dev/null differ diff --git a/faiss/static/img/play.png b/faiss/static/img/play.png deleted file mode 100644 index 40f76045..00000000 Binary files a/faiss/static/img/play.png and /dev/null differ diff --git a/faiss/static/index.html b/faiss/static/index.html deleted file mode 100644 index cf59c628..00000000 --- a/faiss/static/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - -VFrame Image Import - - - -
    - - - -
    - -
    - -
    -
    - - -
    -
    - - - -
    -
    - -
    -
    - - - - -
    -
    - -
    - -
    -
    - -
    -
    - - -
    - - - - - - - - diff --git a/faiss/static/js/app.js b/faiss/static/js/app.js deleted file mode 100644 index 77164c76..00000000 --- a/faiss/static/js/app.js +++ /dev/null @@ -1,491 +0,0 @@ -/* eslint no-use-before-define: 0, camelcase: 0, one-var-declaration-per-line: 0, one-var: 0, quotes: 0, prefer-destructuring: 0, no-alert: 0, no-console: 0, no-multi-assign: 0 */ - -function loadApp() { - const result_template = document.querySelector('#result-template').innerHTML - const results_el = document.querySelector('.results') - const query_div = document.body.querySelector('.query > div') - let bounds - let token, username - let x, y, mouse_x, mouse_y, dx, dy, box - let dragging = false - let cropping = false - let creating = false - let did_check = false - - function init() { - login() - bind() - route() - } - function bind() { - window.onpopstate = route - document.querySelector('[name=img]').addEventListener('change', upload) - on('click', '.results a', preventDefault) - on('click', '.search', search) - on('click', '.panic', panic) - on('click', '.upload_again', upload_again) - on('click', '.browse', browse) - on('click', '.results img', save) - on('click', '.view_saved', loadSaved) - on('click', '.create_new_group', createNewGroup) - on('click', '.reset', reset) - on('click', '.random', random) - on('click', '.check', check) - on('mousedown', '.query img', down) - window.addEventListener('mousemove', move) - window.addEventListener('mouseup', up) - window.addEventListener('keydown', keydown) - } - function route() { - const path = window.location.pathname.split('/') - // remove initial slash - path.shift() - // remove dummy route - if (path[0] === 'search') path.shift() - switch (path[0]) { - case 'fetch': - search({ target: { url: window.location.search.substr(1).split('=')[1] } }) - break - case 'view': - search(path.slice(1)) - break - case 'q': - if (path.length === 3) { - search({ target: { dir: path[1], fn: path[2] } }) - } else { - browse({ target: { dir: path[1], fn: null } }) - } - break - case 'saved': - loadSaved() - break - default: - break - } - } - function keydown(e) { - switch (e.keyCode) { - case 27: // escape - panic() - break - default: - break - } - } - - // load search results - function loadResults(data) { - console.log(data) - if (!data.query.url) return - // console.log(data) - document.body.className = 'searching' - const path = getPathFromImage(data.query.url) - pushState('searching', "/search/fetch/?url=" + path.url) - if (path.dir === 'uploaded' && path.fn.match('_filename')) { - loadMessage( - "< Back | " - + "Searching subregion, " - + "found " + data.results.length + " images" - ) - } else { - loadMessage( - "Found " + data.results.length + " images" - ) - } - loadQuery(data.query.url) - if (!data.results.length) { - results_el.innerHTML = "No results" - return - } - const saved = window.store.get('saved', []) - - results_el.innerHTML = data.results.map(res => { - const { distance, file, hash, frame, url } = res - const isSaved = saved.indexOf(url) !== -1 - const { type } = getPathFromImage(url) - let className = isSaved ? 'saved' : '' - className += ' ' + type - let t = result_template - .replace('{score}', Math.floor(clamp(1 - distance, 0, 1) * 100) + "%") - .replace('{browse}', '/search/q/' + hash) - .replace('{search}', '/search/view/' + [file, hash, frame].join('/')) - .replace('{metadata}', '/metadata/' + hash) - .replace('{className}', className) - .replace('{saved_msg}', isSaved ? 'Saved' : 'Save') - .replace('{img}', url) - return t - }).join('') - } - - function loadDirectory(data) { - console.log(data) - document.body.className = 'browsing' - pushState('searching', "/search/q/" + data.path) - loadMessage("Video: " + data.path + "") - loadQuery("") - if (!data.results.length) { - results_el.innerHTML = "No frames found" - return - } - const saved = window.store.get('saved', []) - results_el.innerHTML = data.results - .map(result => [parseInt(result.frame, 10), result]) - .sort((a, b) => a[0] - b[0]) - .map(pair => { - let { file, hash, frame, url } = pair[1] - const isSaved = saved.indexOf(url) !== -1 - let className = isSaved ? 'saved' : '' - let t = result_template - .replace('{img}', url) - .replace('{browse}', '/search/q/' + hash) - .replace('{search}', '/search/view/' + [file, hash, frame].join('/')) - .replace('{metadata}', '/metadata/' + hash) - .replace('{className}', className) - .replace('{saved_msg}', isSaved ? 'Saved' : 'Save') - return t - }).join('') - } - function loadSaved() { - document.body.className = 'saving' - pushState('View saved', "/search/saved") - const saved = window.store.get('saved', []) - cropping = false - loadMessage(saved.length + " saved image" + (saved.length === 1 ? "" : "s")) - loadQuery('') - const box_el = document.querySelector('.box') - if (box_el) box_el.parentNode.removeChild(box_el) - results_el.innerHTML = saved.map(href => { - const { url, dir } = getPathFromImage({ src: href }) - let className = 'saved' - let t = result_template - .replace('{img}', href) - .replace('{browse}', '/search/q/' + dir) - .replace('{search}', '/search/fetch/?url=' + url) - .replace('{metadata}', '/metadata/' + dir) - .replace('{className}', className) - .replace('{saved_msg}', 'Saved') - return t - }).join('') - } - function loadQuery(path) { - if (cropping) return - const qd = document.querySelector('.query div') - qd.innerHTML = '' - if (path.match(/(gif|jpe?g|png)$/)) { - const img = new Image() - img.setAttribute('crossorigin', 'anonymous') - img.src = path.replace('sm', 'md') - qd.appendChild(img) - } else { - qd.innerHTML = path || "" - } - } - function loadMessage(msg) { - document.querySelector('.query .msg').innerHTML = msg - } - - // panic button - function panic() { - loadMessage('Query cleared') - loadQuery('') - results_el.innerHTML = '' - } - - // adding stuff to localstorage - function save(e) { - const { url } = getPathFromImage(e.target) - const saved = window.store.get('saved', []) - let newList = saved || [] - if (saved.indexOf(url) !== -1) { - newList = saved.filter(f => f !== url) - e.target.parentNode.classList.remove('saved') - } else { - newList.push(url) - e.target.parentNode.classList.add('saved') - } - window.store.set('saved', newList) - } - function reset() { - const shouldReset = window.confirm("This will reset the saved images. Are you sure?") - if (!shouldReset) return - window.store.set('saved', []) - loadSaved() - document.querySelector('[name=title]').value = '' - window.alert("Reset saved images") - } - - // submit the new group - function createNewGroup() { - const title = document.querySelector('[name=title]').value.trim().replace(/[^-_a-zA-Z0-9 ]/g, "") - const saved = window.store.get('saved', []) - const graphic = document.querySelector('[name=graphic]').checked - if (!title.length) return alert("Please enter a title for this group") - if (!saved.length) return alert("Please pick some images to save") - if (!did_check) { - alert('Automatically checking for duplicates. Please doublecheck your selection.') - return check() - } - if (creating) return null - creating = true - return http_post("/api/images/import/new/", { - title, - graphic, - saved - }).then(res => { - console.log(res) - window.store.set('saved', []) - window.location.href = '/groups/show/' + res.image_group.id - }).catch(res => { - alert('Error creating group. The server response is logged to the console.') - console.log(res) - creating = false - }) - } - - // api queries - function login() { - const isLocal = (window.location.hostname === '0.0.0.0') - try { - // csrftoken = "test" // getCookie('csrftoken') - const auth = JSON.parse(window.store.get('persist:root').auth) - token = auth.token - username = auth.user.username - if (!token && !isLocal) { - window.location.href = '/' - } - } catch (e) { - if (!isLocal) { - window.location.href = '/' - } - } - document.querySelector('.logged-in .capitalize').innerHTML = username || 'user' - } - - function upload(e) { - cropping = false - const files = e.dataTransfer ? e.dataTransfer.files : e.target.files - let i, f - for (i = 0, f; i < files.length; i++) { - f = files[i] - if (f && f.type.match('image.*')) break - } - if (!f) return - do_upload(f) - } - - function do_upload(f) { - const fd = new FormData() - fd.append('query_img', f) - document.body.className = 'loading' - http_post('/search/api/upload', fd).then(loadResults) - } - - function upload_again() { - const { files } = document.querySelector('input[type=file]') - if (!files.length) { - window.alert('Please upload a file.') - return - } - upload({ - dataTransfer: { files } - }) - } - - function search(e) { - if (e.length) return search_by_vector(e) - const { url } = getPath(e.target) - cropping = false - document.body.className = 'loading' - loadQuery(url) - loadMessage('Loading results...') - http_get('/search/api/fetch/?url=' + url).then(loadResults) - } - - function search_by_vector(e) { - cropping = false - document.body.className = 'loading' - loadQuery('') - loadMessage('Loading results...') - http_get('/search/api/search/' + e.join('/')).then(loadResults) - } - - function browse(e) { - document.body.className = 'loading' - cropping = false - let dir; - if (e.target.dir) { - dir = e.target.dir - } - else { - const href = e.target.parentNode.href - dir = href.split('/')[5] - console.log(href, dir) - } - loadMessage('Listing video...') - http_get('/search/api/list/' + dir).then(loadDirectory) - } - - function check() { - http_post('/api/images/import/search/', { - saved: window.store.get('saved') || [], - }).then(res => { - console.log(res) - const { good, bad } = res - did_check = true - window.store.set('saved', good) - if (!bad.length) { - return alert("No duplicates found.") - } - bad.forEach(path => { - const el = document.querySelector('img[src="' + path + '"]') - if (el) el.parentNode.classList.remove('saved') - }) - return alert("Untagged " + bad.length + " duplicate" + (bad.length === 1 ? "" : "s") + ".") - }) - } - - function random() { - http_get('/search/api/random').then(loadResults) - } - - // drawing a box - function down(e) { - e.preventDefault() - dragging = true - bounds = query_div.querySelector('img').getBoundingClientRect() - mouse_x = e.pageX - mouse_y = e.pageY - x = mouse_x - bounds.left - y = mouse_y - bounds.top - dx = dy = 0 - box = document.querySelector('.box') || document.createElement('div') - box.className = 'box' - box.style.left = x + 'px' - box.style.top = y + 'px' - box.style.width = 0 + 'px' - box.style.height = 0 + 'px' - query_div.appendChild(box) - } - function move(e) { - if (!dragging) return - e.preventDefault() - dx = clamp(e.pageX - mouse_x, 0, bounds.width - x) - dy = clamp(e.pageY - mouse_y, 0, bounds.height - y) - box.style.width = dx + 'px' - box.style.height = dy + 'px' - } - function up(e) { - if (!dragging) return - dragging = false - e.preventDefault() - const img = query_div.querySelector('img') - const canvas = query_div.querySelector('canvas') || document.createElement('canvas') - const ctx = canvas.getContext('2d') - const ratio = img.naturalWidth / bounds.width - canvas.width = dx * ratio - canvas.height = dy * ratio - if (dx < 10 || dy < 10) { - if (canvas.parentNode) canvas.parentNode.removeChild(canvas) - const box_el = document.querySelector('.box') - if (box_el) box_el.parentNode.removeChild(box_el) - return - } - query_div.appendChild(canvas) - ctx.drawImage( - img, - x * ratio, - y * ratio, - dx * ratio, - dy * ratio, - 0, 0, canvas.width, canvas.height - ) - cropping = true - const blob = window.dataUriToBlob(canvas.toDataURL('image/jpeg', 0.9)) - do_upload(blob) - } - - // utility functions - function http_get(url) { - return fetch(url).then(res => res.json()) - } - function http_post(url, data) { - let headers - if (data instanceof FormData) { - headers = { - Accept: 'application/json, application/xml, text/play, text/html, *.*', - Authorization: 'Token ' + token, - } - } else { - headers = { - Accept: 'application/json, application/xml, text/play, text/html, *.*', - 'Content-Type': 'application/json; charset=utf-8', - Authorization: 'Token ' + token, - } - data = JSON.stringify(data) - } - - // headers['X-CSRFToken'] = csrftoken - return fetch(url, { - method: 'POST', - body: data, - credentials: 'include', - headers, - }).then(res => res.json()) - } - function on(evt, sel, handler) { - document.addEventListener(evt, function (event) { - let t = event.target - while (t && t !== this) { - if (t.matches(sel)) { - handler.call(t, event) - } - t = t.parentNode - } - }) - } - function getPathFromImage(el) { - const url = el.src ? el.src : el - const partz = url.split('/') - let type, dir, fn - if (partz.length === 3) { - type = 'photo' - dir = '' - fn = '' - } - if (partz.length === 9) { - type = 'photo' - dir = partz[6] - fn = '' - } else if (partz.length === 10) { - type = 'video' - dir = partz[6] - fn = partz[7] - } - return { type, dir, fn, url } - } - function getPath(el) { - if (el.url) { - return getPathFromImage(el.url) - } if (el.dir) { - return el - } - el = el.parentNode.parentNode.parentNode.querySelector('img') - return getPathFromImage(el) - } - function pushState(txt, path) { - if (window.location.pathname === path) return - console.log('pushstate', path) - window.history.pushState({}, txt, path) - } - function preventDefault(e) { - if (e && !e.target.classList.contains('metadata')) { - e.preventDefault() - } - } - function clamp(n, a, b) { return n < a ? a : n < b ? n : b } - - // initialize the app when the DOM is ready - document.addEventListener('DOMContentLoaded', init) -} - -loadApp() diff --git a/faiss/static/js/dataUriToBlob.js b/faiss/static/js/dataUriToBlob.js deleted file mode 100644 index 80189b8d..00000000 --- a/faiss/static/js/dataUriToBlob.js +++ /dev/null @@ -1,58 +0,0 @@ -var dataUriToUint8Array = function(uri){ - var data = uri.split(',')[1]; - var bytes = atob(data); - var buf = new ArrayBuffer(bytes.length); - var u8 = new Uint8Array(buf); - for (var i = 0; i < bytes.length; i++) { - u8[i] = bytes.charCodeAt(i); - } - return u8 -} - -window.dataUriToBlob = (function(){ -/** - * Blob constructor. - */ - -var Blob = window.Blob; - -/** - * ArrayBufferView support. - */ - -var hasArrayBufferView = new Blob([new Uint8Array(100)]).size == 100; - -/** - * Return a `Blob` for the given data `uri`. - * - * @param {String} uri - * @return {Blob} - * @api public - */ - -var dataUriToBlob = function(uri){ - var data = uri.split(',')[1]; - var bytes = atob(data); - var buf = new ArrayBuffer(bytes.length); - var arr = new Uint8Array(buf); - for (var i = 0; i < bytes.length; i++) { - arr[i] = bytes.charCodeAt(i); - } - - if (!hasArrayBufferView) arr = buf; - var blob = new Blob([arr], { type: mime(uri) }); - blob.slice = blob.slice || blob.webkitSlice; - return blob; -}; - -/** - * Return data uri mime type. - */ - -function mime(uri) { - return uri.split(';')[0].slice(5); -} - -return dataUriToBlob; - -})() diff --git a/faiss/static/js/metadata-app.js b/faiss/static/js/metadata-app.js deleted file mode 100644 index fa2265fa..00000000 --- a/faiss/static/js/metadata-app.js +++ /dev/null @@ -1,50 +0,0 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=212)}([function(e,t,n){var r=n(99),o=36e5,i=6e4,a=2,u=/[T ]/,s=/:/,c=/^(\d{2})$/,l=[/^([+-]\d{2})$/,/^([+-]\d{3})$/,/^([+-]\d{4})$/],f=/^(\d{4})/,d=[/^([+-]\d{4})/,/^([+-]\d{5})/,/^([+-]\d{6})/],p=/^-(\d{2})$/,h=/^-?(\d{3})$/,m=/^-?(\d{2})-?(\d{2})$/,v=/^-?W(\d{2})$/,y=/^-?W(\d{2})-?(\d{1})$/,g=/^(\d{2}([.,]\d*)?)$/,_=/^(\d{2}):?(\d{2}([.,]\d*)?)$/,b=/^(\d{2}):?(\d{2}):?(\d{2}([.,]\d*)?)$/,w=/([Z+-].*)$/,x=/^(Z)$/,E=/^([+-])(\d{2})$/,O=/^([+-])(\d{2}):?(\d{2})$/;function S(e,t,n){t=t||0,n=n||0;var r=new Date(0);r.setUTCFullYear(e,0,4);var o=7*t+n+1-(r.getUTCDay()||7);return r.setUTCDate(r.getUTCDate()+o),r}e.exports=function(e,t){if(r(e))return new Date(e.getTime());if("string"!=typeof e)return new Date(e);var n=(t||{}).additionalDigits;n=null==n?a:Number(n);var T=function(e){var t,n={},r=e.split(u);if(s.test(r[0])?(n.date=null,t=r[0]):(n.date=r[0],t=r[1]),t){var o=w.exec(t);o?(n.time=t.replace(o[1],""),n.timezone=o[1]):n.time=t}return n}(e),k=function(e,t){var n,r=l[t],o=d[t];if(n=f.exec(e)||o.exec(e)){var i=n[1];return{year:parseInt(i,10),restDateString:e.slice(i.length)}}if(n=c.exec(e)||r.exec(e)){var a=n[1];return{year:100*parseInt(a,10),restDateString:e.slice(a.length)}}return{year:null}}(T.date,n),R=k.year,j=function(e,t){if(null===t)return null;var n,r,o,i;if(0===e.length)return(r=new Date(0)).setUTCFullYear(t),r;if(n=p.exec(e))return r=new Date(0),o=parseInt(n[1],10)-1,r.setUTCFullYear(t,o),r;if(n=h.exec(e)){r=new Date(0);var a=parseInt(n[1],10);return r.setUTCFullYear(t,0,a),r}if(n=m.exec(e)){r=new Date(0),o=parseInt(n[1],10)-1;var u=parseInt(n[2],10);return r.setUTCFullYear(t,o,u),r}if(n=v.exec(e))return i=parseInt(n[1],10)-1,S(t,i);if(n=y.exec(e)){i=parseInt(n[1],10)-1;var s=parseInt(n[2],10)-1;return S(t,i,s)}return null}(k.restDateString,R);if(j){var P,C=j.getTime(),M=0;return T.time&&(M=function(e){var t,n,r;if(t=g.exec(e))return(n=parseFloat(t[1].replace(",",".")))%24*o;if(t=_.exec(e))return n=parseInt(t[1],10),r=parseFloat(t[2].replace(",",".")),n%24*o+r*i;if(t=b.exec(e)){n=parseInt(t[1],10),r=parseInt(t[2],10);var a=parseFloat(t[3].replace(",","."));return n%24*o+r*i+1e3*a}return null}(T.time)),T.timezone?P=function(e){var t,n;return(t=x.exec(e))?0:(t=E.exec(e))?(n=60*parseInt(t[2],10),"+"===t[1]?-n:n):(t=O.exec(e))?(n=60*parseInt(t[2],10)+parseInt(t[3],10),"+"===t[1]?-n:n):0}(T.timezone):(P=new Date(C+M).getTimezoneOffset(),P=new Date(C+M+P*i).getTimezoneOffset()),new Date(C+M+P*i)}return new Date(e)}},function(e,t,n){"use strict";e.exports=n(213)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(220),o=n(117),i=n(224);n.d(t,"Provider",function(){return r.b}),n.d(t,"createProvider",function(){return r.a}),n.d(t,"connectAdvanced",function(){return o.a}),n.d(t,"connect",function(){return i.a})},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";t.__esModule=!0;var r=function(e){return e&&e.__esModule?e:{default:e}}(n(340));t.default=r.default||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1];if(u)throw u;for(var r=!1,o={},i=0;i0&&void 0!==arguments[0]?arguments[0]:0;e/=arguments.length>1&&void 0!==arguments[1]?arguments[1]:25;var t=p(Math.round(e)%60);return(e=Math.floor(e/60))>60?Math.floor(e/60)+":"+p(e%60)+":"+t:e%60+":"+t},t.percent=function(e){return(100*e).toFixed(1)+"%"},t.px=function(e,t){return Math.round(e*t)+"px"},t.clamp=function(e,t,n){return e3&&void 0!==arguments[3]?arguments[3]:"th";return["https://sa-vframe.ams3.digitaloceanspaces.com/v1/media/keyframes",d(e)?null:"unverified",h(t),f(n,6),r,"index.jpg"].filter(function(e){return!!e}).join("/")},v=(t.metadataUri=function(e,t){return"/metadata/"+e+"/"+t+"/"},t.keyframeUri=function(e,t){return"/metadata/"+e+"/keyframe/"+f(t,6)+"/"},t.preloadImage=function(e){var t=e.verified,n=e.hash,r=e.frame,o=e.url;n&&r&&(o=m(t,n,r,"md"));var i=new Image,a=!1;i.onload=function(){a||(a=!0,i.onload=null)},i.crossOrigin="anonymous",i.src=o,i.complete&&i.onload()},null),y="",g="",_=(t.post=function(e,t,n){_();var o=void 0;t instanceof FormData?o={Accept:"application/json, application/xml, text/play, text/html, *.*"}:(o={Accept:"application/json, application/xml, text/play, text/html, *.*","Content-Type":"application/json; charset=utf-8"},t=(0,r.default)(t));var i={method:"POST",body:t,headers:o,credentials:"include"};return n&&(o.Authorization="Token "+y),fetch(e,i).then(function(e){return e.json()})},t.login=function(){if(v)return v;var e="0.0.0.0"===window.location.hostname||"127.0.0.1"===window.location.hostname;try{var t=JSON.parse(JSON.parse(localStorage.getItem("persist:root")).auth);return y=t.token,g=t.user.username,y&&console.log("logged in",g),v=t,y||e||(window.location.href="/"),t}catch(t){return e||(window.location.href="/"),{}}})},function(e,t,n){var r=n(13),o=n(10),i=n(34),a=n(26),u=n(25),s=function(e,t,n){var c,l,f,d=e&s.F,p=e&s.G,h=e&s.S,m=e&s.P,v=e&s.B,y=e&s.W,g=p?o:o[t]||(o[t]={}),_=g.prototype,b=p?r:h?r[t]:(r[t]||{}).prototype;for(c in p&&(n=t),n)(l=!d&&b&&void 0!==b[c])&&u(g,c)||(f=l?b[c]:n[c],g[c]=p&&"function"!=typeof b[c]?n[c]:v&&l?i(f,r):y&&b[c]==f?function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t.prototype=e.prototype,t}(f):m&&"function"==typeof f?i(Function.call,f):f,m&&((g.virtual||(g.virtual={}))[c]=f,e&s.R&&_&&!_[c]&&a(_,c,f)))};s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,e.exports=s},function(e,t,n){"use strict";e.exports=function(e,t,n,r,o,i,a,u){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,o,i,a,u],l=0;(s=new Error(t.replace(/%s/g,function(){return c[l++]}))).name="Invariant Violation"}throw s.framesToPop=1,s}}},function(e,t,n){var r=n(23);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.random=t.browse=t.search=t.searchByFrame=t.searchByVerifiedFrame=t.upload=t.updateOptions=t.panic=t.publicUrl=void 0;var r=s(n(4)),o=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}(n(39)),i=n(94),a=n(17),u=s(n(354));function s(e){return e&&e.__esModule?e:{default:e}}var c={upload:function(){return"https://syrianarchive.vframe.io/search/api/upload"},search:function(){return"https://syrianarchive.vframe.io/search/api/fetch"},searchByVerifiedFrame:function(e,t,n){return"https://syrianarchive.vframe.io/search/api/search/"+e+"/"+t+"/"+(0,a.pad)(n,6)},searchByFrame:function(e,t){return"https://syrianarchive.vframe.io/search/api/search/"+e+"/"+(0,a.pad)(t,6)},browse:function(e){return"https://syrianarchive.vframe.io/search/api/list/"+e},random:function(){return"https://syrianarchive.vframe.io/search/api/random"},check:function(){return"https://syrianarchive.vframe.io/api/images/import/search"}},l=t.publicUrl={browse:function(e){return"/search/browse/"+e},searchByVerifiedFrame:function(e,t,n){return"/search/keyframe/"+(0,a.verify)(e)+"/"+t+"/"+(0,a.pad)(n,6)},searchByFrame:function(e,t){return"/search/keyframe/"+e+"/"+(0,a.pad)(t,6)},review:function(){return"/search/review/"}},f=function(e,t){return{type:o.search.loading,tag:e,offset:t}},d=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return{type:o.search.loaded,tag:e,data:t,offset:n}},p=function(e,t){return{type:o.search.error,tag:e,err:t}};t.panic=function(){return function(e){i.history.push("/search/"),e({type:o.search.panic})}},t.updateOptions=function(e){return function(t){t({type:o.search.update_options,opt:e})}},t.upload=function(e,t){return function(n){var o=i.store.getState().search.options,u=new FormData;u.append("query_img",e),u.append("limit",o.perPage),t||n(f("query")),(0,a.post)(c.upload(),u).then(function(e){if(t){var o=e.query.timing;e.query=(0,r.default)({},t,{timing:o});var a={};if(e.query.crop){var u=e.query.crop,s=u.x,c=u.y,l=u.w,f=u.h;a.crop=[s,c,l,f].map(function(e){return parseInt(e,10)}).join(",")}t.url&&!t.hash&&(a.url=t.url)}else e.query.url&&!window.location.search.match(e.query.url)&&i.history.push("/search/?url="+e.query.url);n(d("query",e))}).catch(function(e){return n(p("query",e))})}},t.searchByVerifiedFrame=function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0;return function(o){var s=i.store.getState().search.options;o(f("query",r));var l=u.default.stringify({limit:s.perPage,offset:r});(0,a.preloadImage)({verified:e,hash:t,frame:n}),fetch(c.searchByVerifiedFrame(e,t,n)+"?"+l,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return o(d("query",e,r))}).catch(function(e){return o(p("query",e))})}},t.searchByFrame=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return function(r){var o=i.store.getState().search.options;r(f("query",n));var s=u.default.stringify({limit:o.perPage,offset:n});(0,a.preloadImage)({verified:!1,hash:e,frame:t}),fetch(c.searchByFrame(e,t)+"?"+s,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return r(d("query",e,n))}).catch(function(e){return r(p("query",e))})}},t.search=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return function(n){var r=i.store.getState().search.options;n(f("query",t));var o=u.default.stringify({url:e,limit:r.perPage,offset:t});0===e.indexOf("static")&&(0,a.preloadImage)({uri:e}),fetch(c.search(e)+"?"+o,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return n(d("query",e,t))}).catch(function(e){return n(p("query",e))})}},t.browse=function(e){return function(t){var n="browse";t(f(n)),fetch(c[n](e),{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return t(d(n,e))}).catch(function(e){return t(p(n,e))})}},t.random=function(){return function(e){var t=i.store.getState().search.options,n=u.default.stringify({limit:t.perPage});e(f("query")),fetch(c.random()+"?"+n,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(t){e(d("query",t)),i.history.push(l.searchByVerifiedFrame(t.query.verified,t.query.hash,t.query.frame))}).catch(function(t){return e(p("query",t))})}}},function(e,t,n){var r=n(20),o=n(125),i=n(77),a=Object.defineProperty;t.f=n(24)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){e.exports=!n(35)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){var r=n(22),o=n(43);e.exports=n(24)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(275);n.d(t,"createBrowserHistory",function(){return r.a});var o=n(278);n.d(t,"createHashHistory",function(){return o.a});var i=n(279);n.d(t,"createMemoryHistory",function(){return i.a});var a=n(62);n.d(t,"createLocation",function(){return a.a}),n.d(t,"locationsAreEqual",function(){return a.b});var u=n(47);n.d(t,"parsePath",function(){return u.d}),n.d(t,"createPath",function(){return u.b})},function(e,t,n){e.exports={default:n(331),__esModule:!0}},function(e,t,n){var r=n(0),o=n(30);e.exports=function(e){var t=r(e),n=t.getFullYear(),i=new Date(0);i.setFullYear(n+1,0,4),i.setHours(0,0,0,0);var a=o(i),u=new Date(0);u.setFullYear(n,0,4),u.setHours(0,0,0,0);var s=o(u);return t.getTime()>=a.getTime()?n+1:t.getTime()>=s.getTime()?n:n-1}},function(e,t,n){var r=n(66);e.exports=function(e){return r(e,{weekStartsOn:1})}},function(e,t,n){var r=n(0);e.exports=function(e){var t=r(e);return t.setHours(0,0,0,0),t}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){"use strict";var r=n(70),o=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=f;var i=n(54);i.inherits=n(32);var a=n(201),u=n(111);i.inherits(f,a);for(var s=o(u.prototype),c=0;c1)for(var n=1;n=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var r=n(129),o=n(82);e.exports=Object.keys||function(e){return r(e,o)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"f",function(){return o}),n.d(t,"c",function(){return i}),n.d(t,"e",function(){return a}),n.d(t,"g",function(){return u}),n.d(t,"d",function(){return s}),n.d(t,"b",function(){return c});var r=function(e){return"/"===e.charAt(0)?e:"/"+e},o=function(e){return"/"===e.charAt(0)?e.substr(1):e},i=function(e,t){return new RegExp("^"+t+"(\\/|\\?|#|$)","i").test(e)},a=function(e,t){return i(e,t)?e.substr(t.length):e},u=function(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e},s=function(e){var t=e||"/",n="",r="",o=t.indexOf("#");-1!==o&&(r=t.substr(o),t=t.substr(0,o));var i=t.indexOf("?");return-1!==i&&(n=t.substr(i),t=t.substr(0,i)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}},c=function(e){var t=e.pathname,n=e.search,r=e.hash,o=t||"/";return n&&"?"!==n&&(o+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(o+="#"===r.charAt(0)?r:"#"+r),o}},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var o=function(e){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(e))))+" */"}(r),i=r.sources.map(function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"});return[n].concat(i).concat([o]).join("\n")}return[n].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},o=0;o=0&&s.splice(t,1)}function h(e){var t=document.createElement("style");return void 0===e.attrs.type&&(e.attrs.type="text/css"),m(t,e.attrs),d(e,t),t}function m(e,t){Object.keys(t).forEach(function(n){e.setAttribute(n,t[n])})}function v(e,t){var n,r,o,i;if(t.transform&&e.css){if(!(i=t.transform(e.css)))return function(){};e.css=i}if(t.singleton){var s=u++;n=a||(a=h(t)),r=g.bind(null,n,s,!1),o=g.bind(null,n,s,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",m(t,e.attrs),d(e,t),t}(t),r=function(e,t,n){var r=n.css,o=n.sourceMap,i=void 0===t.convertToAbsoluteUrls&&o;(t.convertToAbsoluteUrls||i)&&(r=c(r));o&&(r+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var a=new Blob([r],{type:"text/css"}),u=e.href;e.href=URL.createObjectURL(a),u&&URL.revokeObjectURL(u)}.bind(null,n,t),o=function(){p(n),n.href&&URL.revokeObjectURL(n.href)}):(n=h(t),r=function(e,t){var n=t.css,r=t.media;r&&e.setAttribute("media",r);if(e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}.bind(null,n),o=function(){p(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else o()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=o()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=f(e,t);return l(n,t),function(e){for(var o=[],i=0;io?1:0}},function(e,t,n){(function(e){function n(e){return Object.prototype.toString.call(e)}t.isArray=function(e){return Array.isArray?Array.isArray(e):"[object Array]"===n(e)},t.isBoolean=function(e){return"boolean"==typeof e},t.isNull=function(e){return null===e},t.isNullOrUndefined=function(e){return null==e},t.isNumber=function(e){return"number"==typeof e},t.isString=function(e){return"string"==typeof e},t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=function(e){return void 0===e},t.isRegExp=function(e){return"[object RegExp]"===n(e)},t.isObject=function(e){return"object"==typeof e&&null!==e},t.isDate=function(e){return"[object Date]"===n(e)},t.isError=function(e){return"[object Error]"===n(e)||e instanceof Error},t.isFunction=function(e){return"function"==typeof e},t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=e.isBuffer}).call(t,n(204).Buffer)},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){var r=n(22).f,o=n(25),i=n(14)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){n(254);for(var r=n(13),o=n(26),i=n(36),a=n(14)("toStringTag"),u="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),s=0;s may have only one child element"),this.unlisten=r.listen(function(){e.setState({match:e.computeMatch(r.location.pathname)})})},t.prototype.componentWillReceiveProps=function(e){o()(this.props.history===e.history,"You cannot change ")},t.prototype.componentWillUnmount=function(){this.unlisten()},t.prototype.render=function(){var e=this.props.children;return e?s.a.Children.only(e):null},t}(s.a.Component);p.propTypes={history:l.a.object.isRequired,children:l.a.node},p.contextTypes={router:l.a.object},p.childContextTypes={router:l.a.object.isRequired},t.a=p},function(e,t,n){"use strict";var r=n(140),o=n.n(r),i={},a=0;t.a=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments[2];"string"==typeof t&&(t={path:t});var r=t,u=r.path,s=r.exact,c=void 0!==s&&s,l=r.strict,f=void 0!==l&&l,d=r.sensitive,p=void 0!==d&&d;if(null==u)return n;var h=function(e,t){var n=""+t.end+t.strict+t.sensitive,r=i[n]||(i[n]={});if(r[e])return r[e];var u=[],s={re:o()(e,u,t),keys:u};return a<1e4&&(r[e]=s,a++),s}(u,{end:c,strict:f,sensitive:p}),m=h.re,v=h.keys,y=m.exec(e);if(!y)return null;var g=y[0],_=y.slice(1),b=e===g;return c&&!b?null:{path:u,url:"/"===u&&""===g?"/":g,isExact:b,params:v.reduce(function(e,t,n){return e[t.name]=_[n],e},{})}}},function(e,t,n){"use strict";t.__esModule=!0;var r=function(e){return e&&e.__esModule?e:{default:e}}(n(126));t.default=function(e,t,n){return t in e?(0,r.default)(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},function(e,t,n){var r=n(0);e.exports=function(e,t){var n=t&&Number(t.weekStartsOn)||0,o=r(e),i=o.getDay(),a=(i0?r:n)(e)}},function(e,t,n){var r=n(20),o=n(251),i=n(82),a=n(74)("IE_PROTO"),u=function(){},s=function(){var e,t=n(76)("iframe"),r=i.length;for(t.style.display="none",n(131).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" - diff --git a/faiss/static/search.html b/faiss/static/search.html deleted file mode 100644 index 056d06c1..00000000 --- a/faiss/static/search.html +++ /dev/null @@ -1 +0,0 @@ -search.html \ No newline at end of file diff --git a/faiss/util.py b/faiss/util.py deleted file mode 100644 index 97afbc22..00000000 --- a/faiss/util.py +++ /dev/null @@ -1,29 +0,0 @@ -import time -import simplejson as json -import pickle -from os import path -from collections import namedtuple - -# Converts JSON el['key'] to Pythonic object-style el.key -def _json_object_hook(d): - return namedtuple('X', d.keys())(*d.values()) - -# Load a JSON recipe -def load_recipe(path): - with open(path) as fh: - return json.load(fh, object_hook=_json_object_hook) - -# Load a pickle file -def load_pickle(data_dir, pkl_fn): - load_start = time.time() - with open(path.join(str(data_dir), str(pkl_fn)), 'rb') as fh: - raw = fh.read() - data = pickle.loads(raw) - load_end = time.time() - load_time = load_end - load_start - print("Pickle load time: {:.1f}s".format(load_time)) - return data - -def read_json(fn): - with open(fn, 'r') as json_file: - return json.load(json_file) diff --git a/faiss/wsgi.py b/faiss/wsgi.py deleted file mode 100644 index 371862fb..00000000 --- a/faiss/wsgi.py +++ /dev/null @@ -1,5 +0,0 @@ -from server import app - -if __name__ == "__main__": - app.run() - diff --git a/megapixels/app/builder/README.md b/megapixels/app/builder/README.md new file mode 100644 index 00000000..1a6d3a1e --- /dev/null +++ b/megapixels/app/builder/README.md @@ -0,0 +1,21 @@ +Megapixels Static Site Generator +================================ + +The index, blog, and about other pages are built using this static site generator. + +## Metadata + +``` +status: published|draft|private +title: From 1 to 100 Pixels +desc: High resolution insights from low resolution imagery +slug: from-1-to-100-pixels +published: 2018-12-04 +updated: 2018-12-04 +authors: Adam Harvey, Berit Gilma, Matthew Stender +``` + +## S3 Assets + +Static assets: `v1/site/about/assets/picture.jpg` +Dataset assets: `v1/datasets/lfw/assets/picture.jpg` diff --git a/megapixels/app/builder/builder.py b/megapixels/app/builder/builder.py new file mode 100644 index 00000000..620fc710 --- /dev/null +++ b/megapixels/app/builder/builder.py @@ -0,0 +1,90 @@ +#!/usr/bin/python + +from dotenv import load_dotenv +load_dotenv() + +import os +import glob +from jinja2 import Environment, FileSystemLoader, select_autoescape + +import s3 +import parser +from paths import * + +env = Environment( + loader=FileSystemLoader(template_path), + autoescape=select_autoescape([]) +) + +def build_page(fn, research_posts): + metadata, sections = parser.read_metadata(fn) + + if metadata is None: + print("{} has no metadata".format(fn)) + return + + print(metadata['url']) + + dirname = os.path.dirname(fn) + output_path = public_path + metadata['url'] + output_fn = os.path.join(output_path, "index.html") + + skip_h1 = False + + if metadata['url'] == '/': + template = env.get_template("home.html") + elif 'research/' in fn: + skip_h1 = True + template = env.get_template("research.html") + else: + template = env.get_template("page.html") + + if 'datasets/' in fn: + s3_dir = s3_datasets_path + else: + s3_dir = s3_site_path + + s3_path = s3.make_s3_path(s3_dir, metadata['path']) + + if 'index.md' in fn: + s3.sync_directory(dirname, s3_dir, metadata) + + content = parser.parse_markdown(sections, s3_path, skip_h1=skip_h1) + + html = template.render( + metadata=metadata, + content=content, + research_posts=research_posts, + latest_research_post=research_posts[-1], + ) + + os.makedirs(output_path, exist_ok=True) + with open(output_fn, "w") as file: + file.write(html) + + print("______") + +def build_research_index(research_posts): + metadata, sections = parser.read_metadata('../site/content/research/index.md') + template = env.get_template("page.html") + s3_path = s3.make_s3_path(s3_site_path, metadata['path']) + content = parser.parse_markdown(sections, s3_path, skip_h1=False) + content += parser.parse_research_index(research_posts) + html = template.render( + metadata=metadata, + content=content, + research_posts=research_posts, + latest_research_post=research_posts[-1], + ) + output_fn = public_path + '/research/index.html' + with open(output_fn, "w") as file: + file.write(html) + +def build_site(): + research_posts = parser.read_research_post_index() + for fn in glob.iglob(os.path.join(content_path, "**/*.md"), recursive=True): + build_page(fn, research_posts) + build_research_index(research_posts) + +if __name__ == '__main__': + build_site() diff --git a/megapixels/app/builder/parser.py b/megapixels/app/builder/parser.py new file mode 100644 index 00000000..dd3643bf --- /dev/null +++ b/megapixels/app/builder/parser.py @@ -0,0 +1,172 @@ +import os +import re +import glob +import mistune + +import s3 +from paths import * + +renderer = mistune.Renderer(escape=False) +markdown = mistune.Markdown(renderer=renderer) + +def fix_images(lines, s3_path): + real_lines = [] + block = "\n\n".join(lines) + for line in block.split("\n"): + if "![" in line: + line = line.replace('![', '') + alt_text, tail = line.split('](', 1) + url, tail = tail.split(')', 1) + if ':' in alt_text: + tail, alt_text = alt_text.split(':', 1) + img_tag = "{}".format(s3_path + url, alt_text.replace("'", "")) + if len(alt_text): + line = "
    {}
    {}
    ".format(img_tag, alt_text) + else: + line = "
    {}
    ".format(img_tag, alt_text) + real_lines.append(line) + return "\n".join(real_lines) + +def format_section(lines, s3_path, type=''): + if len(lines): + lines = fix_images(lines, s3_path) + if type: + return "
    {}
    ".format(type, markdown(lines)) + else: + return "
    " + markdown(lines) + "
    " + return "" + +def format_metadata(section): + meta = [] + for line in section.split('\n'): + key, value = line[2:].split(': ', 1) + meta.append("
    {}
    {}
    ".format(key, value)) + return "
    {}
    ".format(''.join(meta)) + +def parse_markdown(sections, s3_path, skip_h1=False): + groups = [] + current_group = [] + for section in sections: + if skip_h1 and section.startswith('# '): + continue + elif section.startswith('+ '): + groups.append(format_section(current_group, s3_path)) + groups.append(format_metadata(section)) + current_group = [] + elif '![wide:' in section: + groups.append(format_section(current_group, s3_path)) + groups.append(format_section([section], s3_path, type='wide')) + current_group = [] + elif '![' in section: + groups.append(format_section(current_group, s3_path)) + groups.append(format_section([section], s3_path, type='images')) + current_group = [] + else: + current_group.append(section) + groups.append(format_section(current_group, s3_path)) + content = "".join(groups) + return content + +def parse_research_index(research_posts): + content = "
    " + for post in research_posts: + s3_path = s3.make_s3_path(s3_site_path, post['path']) + if 'image' in post: + post_image = s3_path + post['image'] + else: + post_image = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' + row = "
    Research post

    {}

    {}

    ".format( + post['path'], + post_image, + post['title'], + post['tagline']) + content += row + content += '
    ' + return content + +def read_metadata(fn): + with open(fn, "r") as file: + data = file.read() + data = data.replace("\n ", "\n") + if "\n" in data: + data = data.replace("\r", "") + else: + data = data.replace("\r", "\n") + sections = data.split("\n\n") + return parse_metadata(fn, sections) + +default_metadata = { + 'status': 'published', + 'title': 'Untitled Page', + 'desc': '', + 'slug': '', + 'published': '2018-12-31', + 'updated': '2018-12-31', + 'authors': 'Adam Harvey', + 'sync': 'true', + 'tagline': '', +} + +def parse_metadata_section(metadata, section): + for line in section.split("\n"): + if ': ' not in line: + continue + key, value = line.split(': ', 1) + metadata[key.lower()] = value + +def parse_metadata(fn, sections): + found_meta = False + metadata = {} + valid_sections = [] + for section in sections: + if not found_meta and ': ' in section: + found_meta = True + parse_metadata_section(metadata, section) + continue + if '-----' in section: + continue + if found_meta: + valid_sections.append(section) + + if 'title' not in metadata: + print('warning: {} has no title'.format(fn)) + for key in default_metadata: + if key not in metadata: + metadata[key] = default_metadata[key] + + basedir = os.path.dirname(fn.replace(content_path, '')) + basename = os.path.basename(fn) + if basedir == '/': + metadata['path'] = '/' + metadata['url'] = '/' + elif basename == 'index.md': + metadata['path'] = basedir + '/' + metadata['url'] = metadata['path'] + else: + metadata['path'] = basedir + '/' + metadata['url'] = metadata['path'] + basename.replace('.md', '') + '/' + + if metadata['status'] == 'published|draft|private': + metadata['status'] = 'published' + + metadata['sync'] = metadata['sync'] != 'false' + + metadata['author_html'] = '
    '.join(metadata['authors'].split(',')) + return metadata, valid_sections + +def read_research_post_index(): + posts = [] + for fn in sorted(glob.glob('../site/content/research/*/index.md')): + metadata, valid_sections = read_metadata(fn) + if metadata is None or metadata['status'] == 'private' or metadata['status'] == 'draft': + continue + posts.append(metadata) + if not len(posts): + posts.append({ + 'title': 'Placeholder', + 'slug': 'placeholder', + 'date': 'Placeholder', + 'url': '/', + }) + return posts + diff --git a/megapixels/app/builder/paths.py b/megapixels/app/builder/paths.py new file mode 100644 index 00000000..356f2f3d --- /dev/null +++ b/megapixels/app/builder/paths.py @@ -0,0 +1,6 @@ + +s3_site_path = "v1/site" +s3_datasets_path = "v1" # datasets is already in the filename +public_path = "../site/public" +content_path = "../site/content" +template_path = "../site/templates" diff --git a/megapixels/app/builder/s3.py b/megapixels/app/builder/s3.py new file mode 100644 index 00000000..41ecdf61 --- /dev/null +++ b/megapixels/app/builder/s3.py @@ -0,0 +1,61 @@ +import os +import glob +import boto3 +from paths import * + +session = boto3.session.Session() + +s3_client = session.client( + service_name='s3', + aws_access_key_id=os.getenv('S3_KEY'), + aws_secret_access_key=os.getenv('S3_SECRET'), + endpoint_url=os.getenv('S3_ENDPOINT'), + region_name=os.getenv('S3_REGION'), +) + +def sync_directory(base_fn, s3_path, metadata): + fns = {} + for fn in glob.glob(os.path.join(base_fn, 'assets/*')): + fns[os.path.basename(fn)] = True + + if not metadata['sync']: + return + + remote_path = s3_path + metadata['url'] + + directory = s3_client.list_objects(Bucket=os.getenv('S3_BUCKET'), Prefix=remote_path) + prefixes = [] + + if 'Contents' in directory: + for obj in directory['Contents']: + s3_fn = obj['Key'] + fn = os.path.basename(s3_fn) + local_fn = os.path.join(base_fn, 'assets', fn) + if fn in fns: + del fns[fn] + if obj['LastModified'].timestamp() < os.path.getmtime(os.path.join(local_fn)): + print("s3 update {}".format(s3_fn)) + s3_client.upload_file( + local_fn, + os.getenv('S3_BUCKET'), + s3_fn, + ExtraArgs={ 'ACL': 'public-read' }) + else: + print("s3 delete {}".format(s3_fn)) + response = s3_client.delete_object( + Bucket=os.getenv('S3_BUCKET'), + Key=s3_fn, + ) + + for fn in fns: + local_fn = os.path.join(base_fn, 'assets', fn) + s3_fn = os.path.join(remote_path, 'assets', fn) + print("s3 create {}".format(s3_fn)) + s3_client.upload_file( + local_fn, + os.getenv('S3_BUCKET'), + s3_fn, + ExtraArgs={ 'ACL': 'public-read' }) + +def make_s3_path(s3_dir, metadata_path): + return "{}/{}/{}{}".format(os.getenv('S3_ENDPOINT'), os.getenv('S3_BUCKET'), s3_dir, metadata_path) diff --git a/megapixels/app/server/create.py b/megapixels/app/server/create.py index 9efed669..c1f41dc4 100644 --- a/megapixels/app/server/create.py +++ b/megapixels/app/server/create.py @@ -1,4 +1,4 @@ -from flask import Flask, Blueprint, jsonify +from flask import Flask, Blueprint, jsonify, send_from_directory from flask_sqlalchemy import SQLAlchemy from app.models.sql_factory import connection_url, load_sql_datasets @@ -7,7 +7,7 @@ from app.server.api import api db = SQLAlchemy() def create_app(script_info=None): - app = Flask(__name__, static_url_path='') + app = Flask(__name__, static_folder='static', static_url_path='') app.config['SQLALCHEMY_DATABASE_URI'] = connection_url app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False @@ -15,6 +15,7 @@ def create_app(script_info=None): datasets = load_sql_datasets(replace=False, base_model=db.Model) app.register_blueprint(api, url_prefix='/api') + app.add_url_rule('/', 'serve_page', serve_page, methods=['GET']) @app.route('/', methods=['GET']) def index(): @@ -34,3 +35,8 @@ def create_app(script_info=None): return(jsonify(links)) return app + +def serve_page(file_relative_path_to_root): + if file_relative_path_to_root[-1] == '/': + file_relative_path_to_root += 'index.html' + return send_from_directory("static", file_relative_path_to_root) diff --git a/megapixels/app/settings/app_cfg.py b/megapixels/app/settings/app_cfg.py index 0507366f..1d3fbc4c 100644 --- a/megapixels/app/settings/app_cfg.py +++ b/megapixels/app/settings/app_cfg.py @@ -57,6 +57,13 @@ DIR_FAISS_RECIPES = join(DIR_FAISS, 'recipes') # Test images DIR_TEST_IMAGES = join(DIR_APP, 'test', 'images') +# ----------------------------------------------------------------------------- +# .env config for keys +# ----------------------------------------------------------------------------- + +DIR_DOTENV = join(DIR_APP, '.env') +load_dotenv(dotenv_path=DIR_DOTENV) + # ----------------------------------------------------------------------------- # Drawing, GUI settings # ----------------------------------------------------------------------------- @@ -69,6 +76,7 @@ FP_FONT = join(DIR_ASSETS, 'font') # ----------------------------------------------------------------------------- DIR_COMMANDS_CV = 'commands/cv' DIR_COMMANDS_ADMIN = 'commands/admin' +DIR_COMMANDS_BUILDER = 'commands/builder' DIR_COMMANDS_DATASETS = 'commands/datasets' DIR_COMMANDS_FAISS = 'commands/faiss' DIR_COMMANDS_MISC = 'commands/misc' @@ -118,10 +126,3 @@ LOGFILE_FORMAT = "%(log_color)s%(levelname)-8s%(reset)s %(cyan)s%(filename)s:%(l # ----------------------------------------------------------------------------- S3_MEDIA_ROOT = 's3://megapixels/v1/media/' S3_METADATA_ROOT = 's3://megapixels/v1/metadata/' - -# ----------------------------------------------------------------------------- -# .env config for keys -# ----------------------------------------------------------------------------- - -DIR_DOTENV = join(DIR_APP, '.env') -load_dotenv(dotenv_path=DIR_DOTENV) diff --git a/megapixels/commands/faiss/build_faiss.py b/megapixels/commands/faiss/build_faiss.py index 96d3f99e..ec94c924 100644 --- a/megapixels/commands/faiss/build_faiss.py +++ b/megapixels/commands/faiss/build_faiss.py @@ -12,8 +12,6 @@ import numpy as np from app.utils.file_utils import load_recipe, load_csv_safe from app.settings import app_cfg as cfg -engine = create_engine('sqlite:///:memory:') - class DefaultRecipe: def __init__(self): self.dim = 128 diff --git a/old/faiss/requirements.txt b/old/faiss/requirements.txt new file mode 100644 index 00000000..1d60aabc --- /dev/null +++ b/old/faiss/requirements.txt @@ -0,0 +1,11 @@ +Pillow +h5py +tensorflow +Keras +Flask +opencv-python +imagehash +scikit-image +scikit-learn +imutils + diff --git a/old/faiss/run.sh b/old/faiss/run.sh new file mode 100644 index 00000000..8f9e77e2 --- /dev/null +++ b/old/faiss/run.sh @@ -0,0 +1 @@ +uwsgi --http 127.0.0.1:5000 --file wsgi.py --callable app --processes 1 diff --git a/old/faiss/static/css/app.css b/old/faiss/static/css/app.css new file mode 100644 index 00000000..a3b24736 --- /dev/null +++ b/old/faiss/static/css/app.css @@ -0,0 +1,289 @@ +/* css boilerplate */ + +* { box-sizing: border-box; } +html,body { + margin: 0; padding: 0; + width: 100%; height: 100%; +} +body { + font-family: Helvetica, sans-serif; + font-weight: 300; + padding-top: 60px; +} + +/* header */ + +header { + position: fixed; + top: 0; + left: 0; + height: 60px; + width: 100%; + background: #11f; + color: white; + align-items: stretch; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + z-index: 3; +} +header > section { + justify-content: flex-start; + align-items: center; + display: flex; + flex: 1 0; + font-weight: bold; +} +header > section:last-of-type { + justify-content: flex-end; +} +header a { + color: hsla(0,0%,100%,.89); + text-decoration: none; + line-height: 18px; + font-size: 14px; + font-weight: 700; + padding: .35rem .4rem; + white-space: nowrap; +} +header .logged-in { + font-size: 12px; + font-weight: normal; + padding: 0 0.5rem; +} +header .logout { + padding: 0 6px; + border-left: 1px solid #99f; +} +header .logout a { + font-size: 12px; +} +.menuToggle { + width: 30px; + height: 30px; + margin: 5px; + cursor: pointer; + line-height: 1; +} + +/* form at the top */ + +#form { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin: 20px; + padding: 20px; + border: 1px solid #ddd; +} +input[type=text] { + border: 1px solid #888; + padding: 4px; + font-size: 15px; +} +input[type=file] { + max-width: 200px; + border-radius: 2px; +} +input[type=file]:invalid + button { visibility: hidden!important; } +input[type=file]:valid + button { visibility: visible!important; } +#form > div { + display: flex; + flex-direction: row; + align-items: center; +} +#form > div * { + margin: 0 3px; +} + +/* saving UI form */ + +label { + display: block; + white-space: nowrap; + padding-bottom: 10px; +} +label:last-child { + padding-bottom: 0; +} +label span { + display: inline-block; + min-width: 80px; +} +.saving_ui { + display: none; +} +.saving .saving_ui { + display: flex; + border: 1px solid #ddd; + margin: 20px; + padding: 20px; + flex-direction: row; + justify-content: space-between; +} + +/* query box, shows either searched image, directory name, etc */ + +.loading .results, +.prefetch .query, .prefetch .results, +.browsing .score, .browsing .browse, +.photo .browse, +.saving .score { + display: none; +} +.browsing .query div { display: inline; margin-left: 5px; font-weight: bold; } +.saving .query div { display: inline; margin-left: 5px; font-weight: bold; } +.load_message { + opacity: 0; +} +.loading .load_message { + display: block; + margin: 20px; + font-weight: bold; +} + +.query { + margin: 20px; +} +.query > div { + margin-top: 10px; + position: relative; + display: flex; + flex-direction: row; + align-items: flex-start; +} +.query img { + cursor: crosshair; + max-width: 400px; + display: block; +} +.query > div > .box { + position: absolute; + border: 1px solid #11f; + background: rgba(17,17,255,0.1); + pointer-events: none; +} +.query canvas { + margin-left: 20px; + max-width: 200px; +} + +/* search results */ + +.results { + display: flex; + flex-direction: row; + flex-wrap: wrap; +} +.results > div { + display: flex; + flex-direction: column; + justify-content: flex-end; + width: 210px; + margin: 15px; + padding: 5px; + border: 1px solid transparent; +} +.results > div.saved { + border-radius: 2px; + background: #fafaaa; +} +.results > div img { + cursor: pointer; + max-width: 210px; + margin-bottom: 10px; +} +.results > div > div { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} +.results a:visited .btn { + color: #99d; +} +.score { + font-size: 12px; + color: #444; +} + + +/* spinner */ + +.loader { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + top: 0; left: 0; + width: 100%; height: 100%; + background: rgba(255,255,255,0.9); +} +.loader > div { + background: white; + padding: 20px; + box-shadow: 0 1px 2px #bbb; + border-radius: 2px; +} +.spinner { + position: relative; + width: 32px; + height: 32px; + color: #11f; + margin: 0 auto; +} +.spinner:after { + position: absolute; + margin: auto; + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + content: " "; + display: inline-block; + border-radius: 50%; + border-style: solid; + border-width: 0.15em; + -webkit-background-clip: padding-box; + border-color: currentColor currentColor currentColor transparent; + box-sizing: border-box; + -webkit-animation: ld-cycle 0.7s infinite linear; + animation: ld-cycle 0.7s infinite linear; +} +@-webkit-keyframes ld-cycle { + 0%, 50%, 100% { + animation-timing-function: cubic-bezier(0.5, 0.5, 0.5, 0.5); + } + 0% { + -webkit-transform: rotate(0); + transform: rotate(0); + } + 50% { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes ld-cycle { + 0%, 50%, 100% { + animation-timing-function: cubic-bezier(0.5, 0.5, 0.5, 0.5); + } + 0% { + -webkit-transform: rotate(0); + transform: rotate(0); + } + 50% { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} diff --git a/old/faiss/static/favicon.ico b/old/faiss/static/favicon.ico new file mode 100644 index 00000000..d97f2f59 Binary files /dev/null and b/old/faiss/static/favicon.ico differ diff --git a/old/faiss/static/img/play.png b/old/faiss/static/img/play.png new file mode 100644 index 00000000..40f76045 Binary files /dev/null and b/old/faiss/static/img/play.png differ diff --git a/old/faiss/static/index.html b/old/faiss/static/index.html new file mode 100644 index 00000000..cf59c628 --- /dev/null +++ b/old/faiss/static/index.html @@ -0,0 +1,83 @@ + + + + + + + +VFrame Image Import + + + +
    + + + +
    + +
    + +
    +
    + + +
    +
    + + + +
    +
    + +
    +
    + + + + +
    +
    + +
    + +
    +
    + +
    +
    + + +
    + + + + + + + + diff --git a/old/faiss/static/js/app.js b/old/faiss/static/js/app.js new file mode 100644 index 00000000..77164c76 --- /dev/null +++ b/old/faiss/static/js/app.js @@ -0,0 +1,491 @@ +/* eslint no-use-before-define: 0, camelcase: 0, one-var-declaration-per-line: 0, one-var: 0, quotes: 0, prefer-destructuring: 0, no-alert: 0, no-console: 0, no-multi-assign: 0 */ + +function loadApp() { + const result_template = document.querySelector('#result-template').innerHTML + const results_el = document.querySelector('.results') + const query_div = document.body.querySelector('.query > div') + let bounds + let token, username + let x, y, mouse_x, mouse_y, dx, dy, box + let dragging = false + let cropping = false + let creating = false + let did_check = false + + function init() { + login() + bind() + route() + } + function bind() { + window.onpopstate = route + document.querySelector('[name=img]').addEventListener('change', upload) + on('click', '.results a', preventDefault) + on('click', '.search', search) + on('click', '.panic', panic) + on('click', '.upload_again', upload_again) + on('click', '.browse', browse) + on('click', '.results img', save) + on('click', '.view_saved', loadSaved) + on('click', '.create_new_group', createNewGroup) + on('click', '.reset', reset) + on('click', '.random', random) + on('click', '.check', check) + on('mousedown', '.query img', down) + window.addEventListener('mousemove', move) + window.addEventListener('mouseup', up) + window.addEventListener('keydown', keydown) + } + function route() { + const path = window.location.pathname.split('/') + // remove initial slash + path.shift() + // remove dummy route + if (path[0] === 'search') path.shift() + switch (path[0]) { + case 'fetch': + search({ target: { url: window.location.search.substr(1).split('=')[1] } }) + break + case 'view': + search(path.slice(1)) + break + case 'q': + if (path.length === 3) { + search({ target: { dir: path[1], fn: path[2] } }) + } else { + browse({ target: { dir: path[1], fn: null } }) + } + break + case 'saved': + loadSaved() + break + default: + break + } + } + function keydown(e) { + switch (e.keyCode) { + case 27: // escape + panic() + break + default: + break + } + } + + // load search results + function loadResults(data) { + console.log(data) + if (!data.query.url) return + // console.log(data) + document.body.className = 'searching' + const path = getPathFromImage(data.query.url) + pushState('searching', "/search/fetch/?url=" + path.url) + if (path.dir === 'uploaded' && path.fn.match('_filename')) { + loadMessage( + "< Back | " + + "Searching subregion, " + + "found " + data.results.length + " images" + ) + } else { + loadMessage( + "Found " + data.results.length + " images" + ) + } + loadQuery(data.query.url) + if (!data.results.length) { + results_el.innerHTML = "No results" + return + } + const saved = window.store.get('saved', []) + + results_el.innerHTML = data.results.map(res => { + const { distance, file, hash, frame, url } = res + const isSaved = saved.indexOf(url) !== -1 + const { type } = getPathFromImage(url) + let className = isSaved ? 'saved' : '' + className += ' ' + type + let t = result_template + .replace('{score}', Math.floor(clamp(1 - distance, 0, 1) * 100) + "%") + .replace('{browse}', '/search/q/' + hash) + .replace('{search}', '/search/view/' + [file, hash, frame].join('/')) + .replace('{metadata}', '/metadata/' + hash) + .replace('{className}', className) + .replace('{saved_msg}', isSaved ? 'Saved' : 'Save') + .replace('{img}', url) + return t + }).join('') + } + + function loadDirectory(data) { + console.log(data) + document.body.className = 'browsing' + pushState('searching', "/search/q/" + data.path) + loadMessage("Video: " + data.path + "") + loadQuery("") + if (!data.results.length) { + results_el.innerHTML = "No frames found" + return + } + const saved = window.store.get('saved', []) + results_el.innerHTML = data.results + .map(result => [parseInt(result.frame, 10), result]) + .sort((a, b) => a[0] - b[0]) + .map(pair => { + let { file, hash, frame, url } = pair[1] + const isSaved = saved.indexOf(url) !== -1 + let className = isSaved ? 'saved' : '' + let t = result_template + .replace('{img}', url) + .replace('{browse}', '/search/q/' + hash) + .replace('{search}', '/search/view/' + [file, hash, frame].join('/')) + .replace('{metadata}', '/metadata/' + hash) + .replace('{className}', className) + .replace('{saved_msg}', isSaved ? 'Saved' : 'Save') + return t + }).join('') + } + function loadSaved() { + document.body.className = 'saving' + pushState('View saved', "/search/saved") + const saved = window.store.get('saved', []) + cropping = false + loadMessage(saved.length + " saved image" + (saved.length === 1 ? "" : "s")) + loadQuery('') + const box_el = document.querySelector('.box') + if (box_el) box_el.parentNode.removeChild(box_el) + results_el.innerHTML = saved.map(href => { + const { url, dir } = getPathFromImage({ src: href }) + let className = 'saved' + let t = result_template + .replace('{img}', href) + .replace('{browse}', '/search/q/' + dir) + .replace('{search}', '/search/fetch/?url=' + url) + .replace('{metadata}', '/metadata/' + dir) + .replace('{className}', className) + .replace('{saved_msg}', 'Saved') + return t + }).join('') + } + function loadQuery(path) { + if (cropping) return + const qd = document.querySelector('.query div') + qd.innerHTML = '' + if (path.match(/(gif|jpe?g|png)$/)) { + const img = new Image() + img.setAttribute('crossorigin', 'anonymous') + img.src = path.replace('sm', 'md') + qd.appendChild(img) + } else { + qd.innerHTML = path || "" + } + } + function loadMessage(msg) { + document.querySelector('.query .msg').innerHTML = msg + } + + // panic button + function panic() { + loadMessage('Query cleared') + loadQuery('') + results_el.innerHTML = '' + } + + // adding stuff to localstorage + function save(e) { + const { url } = getPathFromImage(e.target) + const saved = window.store.get('saved', []) + let newList = saved || [] + if (saved.indexOf(url) !== -1) { + newList = saved.filter(f => f !== url) + e.target.parentNode.classList.remove('saved') + } else { + newList.push(url) + e.target.parentNode.classList.add('saved') + } + window.store.set('saved', newList) + } + function reset() { + const shouldReset = window.confirm("This will reset the saved images. Are you sure?") + if (!shouldReset) return + window.store.set('saved', []) + loadSaved() + document.querySelector('[name=title]').value = '' + window.alert("Reset saved images") + } + + // submit the new group + function createNewGroup() { + const title = document.querySelector('[name=title]').value.trim().replace(/[^-_a-zA-Z0-9 ]/g, "") + const saved = window.store.get('saved', []) + const graphic = document.querySelector('[name=graphic]').checked + if (!title.length) return alert("Please enter a title for this group") + if (!saved.length) return alert("Please pick some images to save") + if (!did_check) { + alert('Automatically checking for duplicates. Please doublecheck your selection.') + return check() + } + if (creating) return null + creating = true + return http_post("/api/images/import/new/", { + title, + graphic, + saved + }).then(res => { + console.log(res) + window.store.set('saved', []) + window.location.href = '/groups/show/' + res.image_group.id + }).catch(res => { + alert('Error creating group. The server response is logged to the console.') + console.log(res) + creating = false + }) + } + + // api queries + function login() { + const isLocal = (window.location.hostname === '0.0.0.0') + try { + // csrftoken = "test" // getCookie('csrftoken') + const auth = JSON.parse(window.store.get('persist:root').auth) + token = auth.token + username = auth.user.username + if (!token && !isLocal) { + window.location.href = '/' + } + } catch (e) { + if (!isLocal) { + window.location.href = '/' + } + } + document.querySelector('.logged-in .capitalize').innerHTML = username || 'user' + } + + function upload(e) { + cropping = false + const files = e.dataTransfer ? e.dataTransfer.files : e.target.files + let i, f + for (i = 0, f; i < files.length; i++) { + f = files[i] + if (f && f.type.match('image.*')) break + } + if (!f) return + do_upload(f) + } + + function do_upload(f) { + const fd = new FormData() + fd.append('query_img', f) + document.body.className = 'loading' + http_post('/search/api/upload', fd).then(loadResults) + } + + function upload_again() { + const { files } = document.querySelector('input[type=file]') + if (!files.length) { + window.alert('Please upload a file.') + return + } + upload({ + dataTransfer: { files } + }) + } + + function search(e) { + if (e.length) return search_by_vector(e) + const { url } = getPath(e.target) + cropping = false + document.body.className = 'loading' + loadQuery(url) + loadMessage('Loading results...') + http_get('/search/api/fetch/?url=' + url).then(loadResults) + } + + function search_by_vector(e) { + cropping = false + document.body.className = 'loading' + loadQuery('') + loadMessage('Loading results...') + http_get('/search/api/search/' + e.join('/')).then(loadResults) + } + + function browse(e) { + document.body.className = 'loading' + cropping = false + let dir; + if (e.target.dir) { + dir = e.target.dir + } + else { + const href = e.target.parentNode.href + dir = href.split('/')[5] + console.log(href, dir) + } + loadMessage('Listing video...') + http_get('/search/api/list/' + dir).then(loadDirectory) + } + + function check() { + http_post('/api/images/import/search/', { + saved: window.store.get('saved') || [], + }).then(res => { + console.log(res) + const { good, bad } = res + did_check = true + window.store.set('saved', good) + if (!bad.length) { + return alert("No duplicates found.") + } + bad.forEach(path => { + const el = document.querySelector('img[src="' + path + '"]') + if (el) el.parentNode.classList.remove('saved') + }) + return alert("Untagged " + bad.length + " duplicate" + (bad.length === 1 ? "" : "s") + ".") + }) + } + + function random() { + http_get('/search/api/random').then(loadResults) + } + + // drawing a box + function down(e) { + e.preventDefault() + dragging = true + bounds = query_div.querySelector('img').getBoundingClientRect() + mouse_x = e.pageX + mouse_y = e.pageY + x = mouse_x - bounds.left + y = mouse_y - bounds.top + dx = dy = 0 + box = document.querySelector('.box') || document.createElement('div') + box.className = 'box' + box.style.left = x + 'px' + box.style.top = y + 'px' + box.style.width = 0 + 'px' + box.style.height = 0 + 'px' + query_div.appendChild(box) + } + function move(e) { + if (!dragging) return + e.preventDefault() + dx = clamp(e.pageX - mouse_x, 0, bounds.width - x) + dy = clamp(e.pageY - mouse_y, 0, bounds.height - y) + box.style.width = dx + 'px' + box.style.height = dy + 'px' + } + function up(e) { + if (!dragging) return + dragging = false + e.preventDefault() + const img = query_div.querySelector('img') + const canvas = query_div.querySelector('canvas') || document.createElement('canvas') + const ctx = canvas.getContext('2d') + const ratio = img.naturalWidth / bounds.width + canvas.width = dx * ratio + canvas.height = dy * ratio + if (dx < 10 || dy < 10) { + if (canvas.parentNode) canvas.parentNode.removeChild(canvas) + const box_el = document.querySelector('.box') + if (box_el) box_el.parentNode.removeChild(box_el) + return + } + query_div.appendChild(canvas) + ctx.drawImage( + img, + x * ratio, + y * ratio, + dx * ratio, + dy * ratio, + 0, 0, canvas.width, canvas.height + ) + cropping = true + const blob = window.dataUriToBlob(canvas.toDataURL('image/jpeg', 0.9)) + do_upload(blob) + } + + // utility functions + function http_get(url) { + return fetch(url).then(res => res.json()) + } + function http_post(url, data) { + let headers + if (data instanceof FormData) { + headers = { + Accept: 'application/json, application/xml, text/play, text/html, *.*', + Authorization: 'Token ' + token, + } + } else { + headers = { + Accept: 'application/json, application/xml, text/play, text/html, *.*', + 'Content-Type': 'application/json; charset=utf-8', + Authorization: 'Token ' + token, + } + data = JSON.stringify(data) + } + + // headers['X-CSRFToken'] = csrftoken + return fetch(url, { + method: 'POST', + body: data, + credentials: 'include', + headers, + }).then(res => res.json()) + } + function on(evt, sel, handler) { + document.addEventListener(evt, function (event) { + let t = event.target + while (t && t !== this) { + if (t.matches(sel)) { + handler.call(t, event) + } + t = t.parentNode + } + }) + } + function getPathFromImage(el) { + const url = el.src ? el.src : el + const partz = url.split('/') + let type, dir, fn + if (partz.length === 3) { + type = 'photo' + dir = '' + fn = '' + } + if (partz.length === 9) { + type = 'photo' + dir = partz[6] + fn = '' + } else if (partz.length === 10) { + type = 'video' + dir = partz[6] + fn = partz[7] + } + return { type, dir, fn, url } + } + function getPath(el) { + if (el.url) { + return getPathFromImage(el.url) + } if (el.dir) { + return el + } + el = el.parentNode.parentNode.parentNode.querySelector('img') + return getPathFromImage(el) + } + function pushState(txt, path) { + if (window.location.pathname === path) return + console.log('pushstate', path) + window.history.pushState({}, txt, path) + } + function preventDefault(e) { + if (e && !e.target.classList.contains('metadata')) { + e.preventDefault() + } + } + function clamp(n, a, b) { return n < a ? a : n < b ? n : b } + + // initialize the app when the DOM is ready + document.addEventListener('DOMContentLoaded', init) +} + +loadApp() diff --git a/old/faiss/static/js/dataUriToBlob.js b/old/faiss/static/js/dataUriToBlob.js new file mode 100644 index 00000000..80189b8d --- /dev/null +++ b/old/faiss/static/js/dataUriToBlob.js @@ -0,0 +1,58 @@ +var dataUriToUint8Array = function(uri){ + var data = uri.split(',')[1]; + var bytes = atob(data); + var buf = new ArrayBuffer(bytes.length); + var u8 = new Uint8Array(buf); + for (var i = 0; i < bytes.length; i++) { + u8[i] = bytes.charCodeAt(i); + } + return u8 +} + +window.dataUriToBlob = (function(){ +/** + * Blob constructor. + */ + +var Blob = window.Blob; + +/** + * ArrayBufferView support. + */ + +var hasArrayBufferView = new Blob([new Uint8Array(100)]).size == 100; + +/** + * Return a `Blob` for the given data `uri`. + * + * @param {String} uri + * @return {Blob} + * @api public + */ + +var dataUriToBlob = function(uri){ + var data = uri.split(',')[1]; + var bytes = atob(data); + var buf = new ArrayBuffer(bytes.length); + var arr = new Uint8Array(buf); + for (var i = 0; i < bytes.length; i++) { + arr[i] = bytes.charCodeAt(i); + } + + if (!hasArrayBufferView) arr = buf; + var blob = new Blob([arr], { type: mime(uri) }); + blob.slice = blob.slice || blob.webkitSlice; + return blob; +}; + +/** + * Return data uri mime type. + */ + +function mime(uri) { + return uri.split(';')[0].slice(5); +} + +return dataUriToBlob; + +})() diff --git a/old/faiss/static/js/metadata-app.js b/old/faiss/static/js/metadata-app.js new file mode 100644 index 00000000..fa2265fa --- /dev/null +++ b/old/faiss/static/js/metadata-app.js @@ -0,0 +1,50 @@ +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=212)}([function(e,t,n){var r=n(99),o=36e5,i=6e4,a=2,u=/[T ]/,s=/:/,c=/^(\d{2})$/,l=[/^([+-]\d{2})$/,/^([+-]\d{3})$/,/^([+-]\d{4})$/],f=/^(\d{4})/,d=[/^([+-]\d{4})/,/^([+-]\d{5})/,/^([+-]\d{6})/],p=/^-(\d{2})$/,h=/^-?(\d{3})$/,m=/^-?(\d{2})-?(\d{2})$/,v=/^-?W(\d{2})$/,y=/^-?W(\d{2})-?(\d{1})$/,g=/^(\d{2}([.,]\d*)?)$/,_=/^(\d{2}):?(\d{2}([.,]\d*)?)$/,b=/^(\d{2}):?(\d{2}):?(\d{2}([.,]\d*)?)$/,w=/([Z+-].*)$/,x=/^(Z)$/,E=/^([+-])(\d{2})$/,O=/^([+-])(\d{2}):?(\d{2})$/;function S(e,t,n){t=t||0,n=n||0;var r=new Date(0);r.setUTCFullYear(e,0,4);var o=7*t+n+1-(r.getUTCDay()||7);return r.setUTCDate(r.getUTCDate()+o),r}e.exports=function(e,t){if(r(e))return new Date(e.getTime());if("string"!=typeof e)return new Date(e);var n=(t||{}).additionalDigits;n=null==n?a:Number(n);var T=function(e){var t,n={},r=e.split(u);if(s.test(r[0])?(n.date=null,t=r[0]):(n.date=r[0],t=r[1]),t){var o=w.exec(t);o?(n.time=t.replace(o[1],""),n.timezone=o[1]):n.time=t}return n}(e),k=function(e,t){var n,r=l[t],o=d[t];if(n=f.exec(e)||o.exec(e)){var i=n[1];return{year:parseInt(i,10),restDateString:e.slice(i.length)}}if(n=c.exec(e)||r.exec(e)){var a=n[1];return{year:100*parseInt(a,10),restDateString:e.slice(a.length)}}return{year:null}}(T.date,n),R=k.year,j=function(e,t){if(null===t)return null;var n,r,o,i;if(0===e.length)return(r=new Date(0)).setUTCFullYear(t),r;if(n=p.exec(e))return r=new Date(0),o=parseInt(n[1],10)-1,r.setUTCFullYear(t,o),r;if(n=h.exec(e)){r=new Date(0);var a=parseInt(n[1],10);return r.setUTCFullYear(t,0,a),r}if(n=m.exec(e)){r=new Date(0),o=parseInt(n[1],10)-1;var u=parseInt(n[2],10);return r.setUTCFullYear(t,o,u),r}if(n=v.exec(e))return i=parseInt(n[1],10)-1,S(t,i);if(n=y.exec(e)){i=parseInt(n[1],10)-1;var s=parseInt(n[2],10)-1;return S(t,i,s)}return null}(k.restDateString,R);if(j){var P,C=j.getTime(),M=0;return T.time&&(M=function(e){var t,n,r;if(t=g.exec(e))return(n=parseFloat(t[1].replace(",",".")))%24*o;if(t=_.exec(e))return n=parseInt(t[1],10),r=parseFloat(t[2].replace(",",".")),n%24*o+r*i;if(t=b.exec(e)){n=parseInt(t[1],10),r=parseInt(t[2],10);var a=parseFloat(t[3].replace(",","."));return n%24*o+r*i+1e3*a}return null}(T.time)),T.timezone?P=function(e){var t,n;return(t=x.exec(e))?0:(t=E.exec(e))?(n=60*parseInt(t[2],10),"+"===t[1]?-n:n):(t=O.exec(e))?(n=60*parseInt(t[2],10)+parseInt(t[3],10),"+"===t[1]?-n:n):0}(T.timezone):(P=new Date(C+M).getTimezoneOffset(),P=new Date(C+M+P*i).getTimezoneOffset()),new Date(C+M+P*i)}return new Date(e)}},function(e,t,n){"use strict";e.exports=n(213)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(220),o=n(117),i=n(224);n.d(t,"Provider",function(){return r.b}),n.d(t,"createProvider",function(){return r.a}),n.d(t,"connectAdvanced",function(){return o.a}),n.d(t,"connect",function(){return i.a})},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";t.__esModule=!0;var r=function(e){return e&&e.__esModule?e:{default:e}}(n(340));t.default=r.default||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1];if(u)throw u;for(var r=!1,o={},i=0;i0&&void 0!==arguments[0]?arguments[0]:0;e/=arguments.length>1&&void 0!==arguments[1]?arguments[1]:25;var t=p(Math.round(e)%60);return(e=Math.floor(e/60))>60?Math.floor(e/60)+":"+p(e%60)+":"+t:e%60+":"+t},t.percent=function(e){return(100*e).toFixed(1)+"%"},t.px=function(e,t){return Math.round(e*t)+"px"},t.clamp=function(e,t,n){return e3&&void 0!==arguments[3]?arguments[3]:"th";return["https://sa-vframe.ams3.digitaloceanspaces.com/v1/media/keyframes",d(e)?null:"unverified",h(t),f(n,6),r,"index.jpg"].filter(function(e){return!!e}).join("/")},v=(t.metadataUri=function(e,t){return"/metadata/"+e+"/"+t+"/"},t.keyframeUri=function(e,t){return"/metadata/"+e+"/keyframe/"+f(t,6)+"/"},t.preloadImage=function(e){var t=e.verified,n=e.hash,r=e.frame,o=e.url;n&&r&&(o=m(t,n,r,"md"));var i=new Image,a=!1;i.onload=function(){a||(a=!0,i.onload=null)},i.crossOrigin="anonymous",i.src=o,i.complete&&i.onload()},null),y="",g="",_=(t.post=function(e,t,n){_();var o=void 0;t instanceof FormData?o={Accept:"application/json, application/xml, text/play, text/html, *.*"}:(o={Accept:"application/json, application/xml, text/play, text/html, *.*","Content-Type":"application/json; charset=utf-8"},t=(0,r.default)(t));var i={method:"POST",body:t,headers:o,credentials:"include"};return n&&(o.Authorization="Token "+y),fetch(e,i).then(function(e){return e.json()})},t.login=function(){if(v)return v;var e="0.0.0.0"===window.location.hostname||"127.0.0.1"===window.location.hostname;try{var t=JSON.parse(JSON.parse(localStorage.getItem("persist:root")).auth);return y=t.token,g=t.user.username,y&&console.log("logged in",g),v=t,y||e||(window.location.href="/"),t}catch(t){return e||(window.location.href="/"),{}}})},function(e,t,n){var r=n(13),o=n(10),i=n(34),a=n(26),u=n(25),s=function(e,t,n){var c,l,f,d=e&s.F,p=e&s.G,h=e&s.S,m=e&s.P,v=e&s.B,y=e&s.W,g=p?o:o[t]||(o[t]={}),_=g.prototype,b=p?r:h?r[t]:(r[t]||{}).prototype;for(c in p&&(n=t),n)(l=!d&&b&&void 0!==b[c])&&u(g,c)||(f=l?b[c]:n[c],g[c]=p&&"function"!=typeof b[c]?n[c]:v&&l?i(f,r):y&&b[c]==f?function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t.prototype=e.prototype,t}(f):m&&"function"==typeof f?i(Function.call,f):f,m&&((g.virtual||(g.virtual={}))[c]=f,e&s.R&&_&&!_[c]&&a(_,c,f)))};s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,e.exports=s},function(e,t,n){"use strict";e.exports=function(e,t,n,r,o,i,a,u){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,o,i,a,u],l=0;(s=new Error(t.replace(/%s/g,function(){return c[l++]}))).name="Invariant Violation"}throw s.framesToPop=1,s}}},function(e,t,n){var r=n(23);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.random=t.browse=t.search=t.searchByFrame=t.searchByVerifiedFrame=t.upload=t.updateOptions=t.panic=t.publicUrl=void 0;var r=s(n(4)),o=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}(n(39)),i=n(94),a=n(17),u=s(n(354));function s(e){return e&&e.__esModule?e:{default:e}}var c={upload:function(){return"https://syrianarchive.vframe.io/search/api/upload"},search:function(){return"https://syrianarchive.vframe.io/search/api/fetch"},searchByVerifiedFrame:function(e,t,n){return"https://syrianarchive.vframe.io/search/api/search/"+e+"/"+t+"/"+(0,a.pad)(n,6)},searchByFrame:function(e,t){return"https://syrianarchive.vframe.io/search/api/search/"+e+"/"+(0,a.pad)(t,6)},browse:function(e){return"https://syrianarchive.vframe.io/search/api/list/"+e},random:function(){return"https://syrianarchive.vframe.io/search/api/random"},check:function(){return"https://syrianarchive.vframe.io/api/images/import/search"}},l=t.publicUrl={browse:function(e){return"/search/browse/"+e},searchByVerifiedFrame:function(e,t,n){return"/search/keyframe/"+(0,a.verify)(e)+"/"+t+"/"+(0,a.pad)(n,6)},searchByFrame:function(e,t){return"/search/keyframe/"+e+"/"+(0,a.pad)(t,6)},review:function(){return"/search/review/"}},f=function(e,t){return{type:o.search.loading,tag:e,offset:t}},d=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return{type:o.search.loaded,tag:e,data:t,offset:n}},p=function(e,t){return{type:o.search.error,tag:e,err:t}};t.panic=function(){return function(e){i.history.push("/search/"),e({type:o.search.panic})}},t.updateOptions=function(e){return function(t){t({type:o.search.update_options,opt:e})}},t.upload=function(e,t){return function(n){var o=i.store.getState().search.options,u=new FormData;u.append("query_img",e),u.append("limit",o.perPage),t||n(f("query")),(0,a.post)(c.upload(),u).then(function(e){if(t){var o=e.query.timing;e.query=(0,r.default)({},t,{timing:o});var a={};if(e.query.crop){var u=e.query.crop,s=u.x,c=u.y,l=u.w,f=u.h;a.crop=[s,c,l,f].map(function(e){return parseInt(e,10)}).join(",")}t.url&&!t.hash&&(a.url=t.url)}else e.query.url&&!window.location.search.match(e.query.url)&&i.history.push("/search/?url="+e.query.url);n(d("query",e))}).catch(function(e){return n(p("query",e))})}},t.searchByVerifiedFrame=function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0;return function(o){var s=i.store.getState().search.options;o(f("query",r));var l=u.default.stringify({limit:s.perPage,offset:r});(0,a.preloadImage)({verified:e,hash:t,frame:n}),fetch(c.searchByVerifiedFrame(e,t,n)+"?"+l,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return o(d("query",e,r))}).catch(function(e){return o(p("query",e))})}},t.searchByFrame=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return function(r){var o=i.store.getState().search.options;r(f("query",n));var s=u.default.stringify({limit:o.perPage,offset:n});(0,a.preloadImage)({verified:!1,hash:e,frame:t}),fetch(c.searchByFrame(e,t)+"?"+s,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return r(d("query",e,n))}).catch(function(e){return r(p("query",e))})}},t.search=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return function(n){var r=i.store.getState().search.options;n(f("query",t));var o=u.default.stringify({url:e,limit:r.perPage,offset:t});0===e.indexOf("static")&&(0,a.preloadImage)({uri:e}),fetch(c.search(e)+"?"+o,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return n(d("query",e,t))}).catch(function(e){return n(p("query",e))})}},t.browse=function(e){return function(t){var n="browse";t(f(n)),fetch(c[n](e),{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(e){return t(d(n,e))}).catch(function(e){return t(p(n,e))})}},t.random=function(){return function(e){var t=i.store.getState().search.options,n=u.default.stringify({limit:t.perPage});e(f("query")),fetch(c.random()+"?"+n,{method:"GET",mode:"cors"}).then(function(e){return e.json()}).then(function(t){e(d("query",t)),i.history.push(l.searchByVerifiedFrame(t.query.verified,t.query.hash,t.query.frame))}).catch(function(t){return e(p("query",t))})}}},function(e,t,n){var r=n(20),o=n(125),i=n(77),a=Object.defineProperty;t.f=n(24)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){e.exports=!n(35)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){var r=n(22),o=n(43);e.exports=n(24)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(275);n.d(t,"createBrowserHistory",function(){return r.a});var o=n(278);n.d(t,"createHashHistory",function(){return o.a});var i=n(279);n.d(t,"createMemoryHistory",function(){return i.a});var a=n(62);n.d(t,"createLocation",function(){return a.a}),n.d(t,"locationsAreEqual",function(){return a.b});var u=n(47);n.d(t,"parsePath",function(){return u.d}),n.d(t,"createPath",function(){return u.b})},function(e,t,n){e.exports={default:n(331),__esModule:!0}},function(e,t,n){var r=n(0),o=n(30);e.exports=function(e){var t=r(e),n=t.getFullYear(),i=new Date(0);i.setFullYear(n+1,0,4),i.setHours(0,0,0,0);var a=o(i),u=new Date(0);u.setFullYear(n,0,4),u.setHours(0,0,0,0);var s=o(u);return t.getTime()>=a.getTime()?n+1:t.getTime()>=s.getTime()?n:n-1}},function(e,t,n){var r=n(66);e.exports=function(e){return r(e,{weekStartsOn:1})}},function(e,t,n){var r=n(0);e.exports=function(e){var t=r(e);return t.setHours(0,0,0,0),t}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){"use strict";var r=n(70),o=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=f;var i=n(54);i.inherits=n(32);var a=n(201),u=n(111);i.inherits(f,a);for(var s=o(u.prototype),c=0;c1)for(var n=1;n=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var r=n(129),o=n(82);e.exports=Object.keys||function(e){return r(e,o)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"f",function(){return o}),n.d(t,"c",function(){return i}),n.d(t,"e",function(){return a}),n.d(t,"g",function(){return u}),n.d(t,"d",function(){return s}),n.d(t,"b",function(){return c});var r=function(e){return"/"===e.charAt(0)?e:"/"+e},o=function(e){return"/"===e.charAt(0)?e.substr(1):e},i=function(e,t){return new RegExp("^"+t+"(\\/|\\?|#|$)","i").test(e)},a=function(e,t){return i(e,t)?e.substr(t.length):e},u=function(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e},s=function(e){var t=e||"/",n="",r="",o=t.indexOf("#");-1!==o&&(r=t.substr(o),t=t.substr(0,o));var i=t.indexOf("?");return-1!==i&&(n=t.substr(i),t=t.substr(0,i)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}},c=function(e){var t=e.pathname,n=e.search,r=e.hash,o=t||"/";return n&&"?"!==n&&(o+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(o+="#"===r.charAt(0)?r:"#"+r),o}},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var o=function(e){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(e))))+" */"}(r),i=r.sources.map(function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"});return[n].concat(i).concat([o]).join("\n")}return[n].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},o=0;o=0&&s.splice(t,1)}function h(e){var t=document.createElement("style");return void 0===e.attrs.type&&(e.attrs.type="text/css"),m(t,e.attrs),d(e,t),t}function m(e,t){Object.keys(t).forEach(function(n){e.setAttribute(n,t[n])})}function v(e,t){var n,r,o,i;if(t.transform&&e.css){if(!(i=t.transform(e.css)))return function(){};e.css=i}if(t.singleton){var s=u++;n=a||(a=h(t)),r=g.bind(null,n,s,!1),o=g.bind(null,n,s,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",m(t,e.attrs),d(e,t),t}(t),r=function(e,t,n){var r=n.css,o=n.sourceMap,i=void 0===t.convertToAbsoluteUrls&&o;(t.convertToAbsoluteUrls||i)&&(r=c(r));o&&(r+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var a=new Blob([r],{type:"text/css"}),u=e.href;e.href=URL.createObjectURL(a),u&&URL.revokeObjectURL(u)}.bind(null,n,t),o=function(){p(n),n.href&&URL.revokeObjectURL(n.href)}):(n=h(t),r=function(e,t){var n=t.css,r=t.media;r&&e.setAttribute("media",r);if(e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}.bind(null,n),o=function(){p(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else o()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=o()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=f(e,t);return l(n,t),function(e){for(var o=[],i=0;io?1:0}},function(e,t,n){(function(e){function n(e){return Object.prototype.toString.call(e)}t.isArray=function(e){return Array.isArray?Array.isArray(e):"[object Array]"===n(e)},t.isBoolean=function(e){return"boolean"==typeof e},t.isNull=function(e){return null===e},t.isNullOrUndefined=function(e){return null==e},t.isNumber=function(e){return"number"==typeof e},t.isString=function(e){return"string"==typeof e},t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=function(e){return void 0===e},t.isRegExp=function(e){return"[object RegExp]"===n(e)},t.isObject=function(e){return"object"==typeof e&&null!==e},t.isDate=function(e){return"[object Date]"===n(e)},t.isError=function(e){return"[object Error]"===n(e)||e instanceof Error},t.isFunction=function(e){return"function"==typeof e},t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=e.isBuffer}).call(t,n(204).Buffer)},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){var r=n(22).f,o=n(25),i=n(14)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){n(254);for(var r=n(13),o=n(26),i=n(36),a=n(14)("toStringTag"),u="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),s=0;s may have only one child element"),this.unlisten=r.listen(function(){e.setState({match:e.computeMatch(r.location.pathname)})})},t.prototype.componentWillReceiveProps=function(e){o()(this.props.history===e.history,"You cannot change ")},t.prototype.componentWillUnmount=function(){this.unlisten()},t.prototype.render=function(){var e=this.props.children;return e?s.a.Children.only(e):null},t}(s.a.Component);p.propTypes={history:l.a.object.isRequired,children:l.a.node},p.contextTypes={router:l.a.object},p.childContextTypes={router:l.a.object.isRequired},t.a=p},function(e,t,n){"use strict";var r=n(140),o=n.n(r),i={},a=0;t.a=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments[2];"string"==typeof t&&(t={path:t});var r=t,u=r.path,s=r.exact,c=void 0!==s&&s,l=r.strict,f=void 0!==l&&l,d=r.sensitive,p=void 0!==d&&d;if(null==u)return n;var h=function(e,t){var n=""+t.end+t.strict+t.sensitive,r=i[n]||(i[n]={});if(r[e])return r[e];var u=[],s={re:o()(e,u,t),keys:u};return a<1e4&&(r[e]=s,a++),s}(u,{end:c,strict:f,sensitive:p}),m=h.re,v=h.keys,y=m.exec(e);if(!y)return null;var g=y[0],_=y.slice(1),b=e===g;return c&&!b?null:{path:u,url:"/"===u&&""===g?"/":g,isExact:b,params:v.reduce(function(e,t,n){return e[t.name]=_[n],e},{})}}},function(e,t,n){"use strict";t.__esModule=!0;var r=function(e){return e&&e.__esModule?e:{default:e}}(n(126));t.default=function(e,t,n){return t in e?(0,r.default)(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},function(e,t,n){var r=n(0);e.exports=function(e,t){var n=t&&Number(t.weekStartsOn)||0,o=r(e),i=o.getDay(),a=(i0?r:n)(e)}},function(e,t,n){var r=n(20),o=n(251),i=n(82),a=n(74)("IE_PROTO"),u=function(){},s=function(){var e,t=n(76)("iframe"),r=i.length;for(t.style.display="none",n(131).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" + diff --git a/old/faiss/static/search.html b/old/faiss/static/search.html new file mode 100644 index 00000000..056d06c1 --- /dev/null +++ b/old/faiss/static/search.html @@ -0,0 +1 @@ +search.html \ No newline at end of file diff --git a/old/faiss/util.py b/old/faiss/util.py new file mode 100644 index 00000000..97afbc22 --- /dev/null +++ b/old/faiss/util.py @@ -0,0 +1,29 @@ +import time +import simplejson as json +import pickle +from os import path +from collections import namedtuple + +# Converts JSON el['key'] to Pythonic object-style el.key +def _json_object_hook(d): + return namedtuple('X', d.keys())(*d.values()) + +# Load a JSON recipe +def load_recipe(path): + with open(path) as fh: + return json.load(fh, object_hook=_json_object_hook) + +# Load a pickle file +def load_pickle(data_dir, pkl_fn): + load_start = time.time() + with open(path.join(str(data_dir), str(pkl_fn)), 'rb') as fh: + raw = fh.read() + data = pickle.loads(raw) + load_end = time.time() + load_time = load_end - load_start + print("Pickle load time: {:.1f}s".format(load_time)) + return data + +def read_json(fn): + with open(fn, 'r') as json_file: + return json.load(json_file) diff --git a/old/faiss/wsgi.py b/old/faiss/wsgi.py new file mode 100644 index 00000000..371862fb --- /dev/null +++ b/old/faiss/wsgi.py @@ -0,0 +1,5 @@ +from server import app + +if __name__ == "__main__": + app.run() + diff --git a/old/server/app/README.md b/old/server/app/README.md new file mode 100644 index 00000000..8bc70132 --- /dev/null +++ b/old/server/app/README.md @@ -0,0 +1,17 @@ +# Startup + +Run supervisor + +`/usr/bin/supervisord` + +Tail log file + +`tail -f /var/log/uwsgi/app/app.log` + +`/opt/redis/redis-stable/src/redis-server &` + +`celery worker -A celery_worker.celery --loglevel=info &` + +If using on Production Server + +`/usr/bin/nohup /usr/bin/supervisord &` \ No newline at end of file diff --git a/old/server/app/__init__.py b/old/server/app/__init__.py new file mode 100644 index 00000000..bce3f9ee --- /dev/null +++ b/old/server/app/__init__.py @@ -0,0 +1,39 @@ +import logging +from logging.handlers import RotatingFileHandler + +from flask import Flask +from flask_bootstrap import Bootstrap + +from flask import Flask + +from config import config, Config + +bootstrap = Bootstrap() +#celery = Celery(__name__, broker=Config.CELERY_BROKER_URL) +from .basemodels import celery + +def create_app(config_name): + app = Flask(__name__) + app.config.from_object(config[config_name]) + config[config_name].init_app(app) + + bootstrap.init_app(app) + celery.conf.update(app.config) + + from .main import main as main_blueprint + app.register_blueprint(main_blueprint) + + #handler = RotatingFileHandler('debug.log', maxBytes=10000, backupCount=1) + #handler.setLevel(logging.INFO) + #app.logger.addHandler(handler) + + format = "%(asctime)s - [%(levelname)s] %(message)s" + logging.basicConfig(filename='debug.log', + filemode='a', + format=format, + level=logging.DEBUG) + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) + logging.getLogger(__name__).addHandler(console) + + return app diff --git a/old/server/app/basemodels.py b/old/server/app/basemodels.py new file mode 100644 index 00000000..475ab0c2 --- /dev/null +++ b/old/server/app/basemodels.py @@ -0,0 +1,5 @@ +from config import config, Config +from celery import Celery + +#bootstrap = Bootstrap() +celery = Celery(__name__, broker=Config.CELERY_BROKER_URL) diff --git a/old/server/app/favicon.ico b/old/server/app/favicon.ico new file mode 100644 index 00000000..4d001b21 Binary files /dev/null and b/old/server/app/favicon.ico differ diff --git a/old/server/app/index.html b/old/server/app/index.html new file mode 100644 index 00000000..3c1b0dfd --- /dev/null +++ b/old/server/app/index.html @@ -0,0 +1,161 @@ + + + + + + + DullDream (v2 x ZkM) + + + + +
    +

    DullDream

    +

    Neural network photo effect

    +
    + +
    +
    +
    + + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    + Change Image + +
    + +
    + Upload +
    +
    + +
    + +
    +
    + About + Privacy +

    + All images uploaded can be used for exhibition and review purposes. +

    +

    + Currently this work is on view at ZKM. View recent DullDreams here. +

    +
    +
    +
    + + + + + + +
    +
    +
    + +
    + Made with DullDream.xyz for ZKM OpenCodes 2017 +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + Home + About + Privacy +
    + +
    + +
    + + + + + + + + + + + + + + diff --git a/old/server/app/main/__init__.py b/old/server/app/main/__init__.py new file mode 100644 index 00000000..a21e2754 --- /dev/null +++ b/old/server/app/main/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +main = Blueprint('main', __name__) + +from . import views, errors, tasks, utils \ No newline at end of file diff --git a/old/server/app/main/errors.py b/old/server/app/main/errors.py new file mode 100644 index 00000000..60b5f227 --- /dev/null +++ b/old/server/app/main/errors.py @@ -0,0 +1,32 @@ +from flask import render_template, request, jsonify +from . import main + + +@main.app_errorhandler(403) +def forbidden(e): + if request.accept_mimetypes.accept_json and \ + not request.accept_mimetypes.accept_html: + response = jsonify({'error': 'forbidden'}) + response.status_code = 403 + return response + return render_template('403.html'), 403 + + +@main.app_errorhandler(404) +def page_not_found(e): + if request.accept_mimetypes.accept_json and \ + not request.accept_mimetypes.accept_html: + response = jsonify({'error': 'not found'}) + response.status_code = 404 + return response + return render_template('404.html'), 404 + + +@main.app_errorhandler(500) +def internal_server_error(e): + if request.accept_mimetypes.accept_json and \ + not request.accept_mimetypes.accept_html: + response = jsonify({'error': 'internal server error'}) + response.status_code = 500 + return response + return render_template('500.html'), 500 diff --git a/old/server/app/main/forms.py b/old/server/app/main/forms.py new file mode 100644 index 00000000..bc1399ad --- /dev/null +++ b/old/server/app/main/forms.py @@ -0,0 +1,60 @@ +from flask.ext.wtf import Form +from wtforms import StringField, TextAreaField, BooleanField, SelectField,\ + SubmitField +from wtforms.validators import Required, Length, Email, Regexp +from wtforms import ValidationError +from flask.ext.pagedown.fields import PageDownField +from ..models import Role, User + + +class NameForm(Form): + name = StringField('What is your name?', validators=[Required()]) + submit = SubmitField('Submit') + + +class EditProfileForm(Form): + name = StringField('Real name', validators=[Length(0, 64)]) + location = StringField('Location', validators=[Length(0, 64)]) + about_me = TextAreaField('About me') + submit = SubmitField('Submit') + + +class EditProfileAdminForm(Form): + email = StringField('Email', validators=[Required(), Length(1, 64), + Email()]) + username = StringField('Username', validators=[ + Required(), Length(1, 64), Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0, + 'Usernames must have only letters, ' + 'numbers, dots or underscores')]) + confirmed = BooleanField('Confirmed') + role = SelectField('Role', coerce=int) + name = StringField('Real name', validators=[Length(0, 64)]) + location = StringField('Location', validators=[Length(0, 64)]) + about_me = TextAreaField('About me') + submit = SubmitField('Submit') + + def __init__(self, user, *args, **kwargs): + super(EditProfileAdminForm, self).__init__(*args, **kwargs) + self.role.choices = [(role.id, role.name) + for role in Role.query.order_by(Role.name).all()] + self.user = user + + def validate_email(self, field): + if field.data != self.user.email and \ + User.query.filter_by(email=field.data).first(): + raise ValidationError('Email already registered.') + + def validate_username(self, field): + if field.data != self.user.username and \ + User.query.filter_by(username=field.data).first(): + raise ValidationError('Username already in use.') + + +class PostForm(Form): + body = PageDownField("What's on your mind?", validators=[Required()]) + submit = SubmitField('Submit') + + +class CommentForm(Form): + body = StringField('Enter your comment', validators=[Required()]) + submit = SubmitField('Submit') diff --git a/old/server/app/main/img_proc_config.py b/old/server/app/main/img_proc_config.py new file mode 100644 index 00000000..db124978 --- /dev/null +++ b/old/server/app/main/img_proc_config.py @@ -0,0 +1,20 @@ +# paths for image processors +import os +from os.path import join + +class ImgProcConfig: + + def __init__(self): + dir_models = '/data_store/apps/dulldream/dnn_models' + + # mask rcnn + self.mask_rcnn_class_config = '/dulldream/src/config/coco_meta.json' + self.mask_rcnn_model = join(dir_models,'tf/mask_rcnn_coco.h5') + + # p2p + self.p2p_ckpts_dir = join(dir_models,'p2p/coco2014_person') + self.p2p_epoch = 'latest' + + # p2p objects only + self.p2p_bg_ckpts_dir = join(dir_models,'p2p/coco2014_objects') + self.p2p_bg_epoch = 'latest' diff --git a/old/server/app/main/paths.py b/old/server/app/main/paths.py new file mode 100644 index 00000000..69c21627 --- /dev/null +++ b/old/server/app/main/paths.py @@ -0,0 +1,19 @@ +from flask import current_app as app + +def get_paths(agree): + if agree: + return ( + app.config['UPLOADS'], + app.config['RENDERS'], + app.config['JSON_DIR'], + app.config['UPLOADS_URI'], + app.config['RENDERS_URI'], + ) + else: + return ( + app.config['UPLOADS_PRIVATE'], + app.config['RENDERS_PRIVATE'], + app.config['JSON_PRIVATE_DIR'], + app.config['UPLOADS_PRIVATE_URI'], + app.config['RENDERS_PRIVATE_URI'], + ) diff --git a/old/server/app/main/tasks.py b/old/server/app/main/tasks.py new file mode 100644 index 00000000..970e6988 --- /dev/null +++ b/old/server/app/main/tasks.py @@ -0,0 +1,374 @@ +import os +import sys +import time +import datetime +import json +from PIL import Image, ImageFilter +import cv2 as cv +import numpy as np +from . import main, utils +from .. import basemodels +from flask import current_app as app +from .paths import get_paths +celery = basemodels.celery +from celery.utils.log import get_task_logger +celery_logger = get_task_logger(__name__) +import imutils + + +# init image processors +sys.path.append('/dulldream/src/') +from .img_proc_config import ImgProcConfig +from image_processors.mask_rcnn import MaskRCNN +from image_processors.pix2pix import Pix2Pix +from utils import imx +from utils import fiox + + +# initialize image processor +img_proc_config = ImgProcConfig() +p2p = Pix2Pix(img_proc_config.p2p_ckpts_dir,epoch=img_proc_config.p2p_epoch) +p2p_objects = Pix2Pix(img_proc_config.p2p_bg_ckpts_dir,epoch=img_proc_config.p2p_epoch) + +mask_rcnn = MaskRCNN(img_proc_config.mask_rcnn_class_config, + model_path=img_proc_config.mask_rcnn_model) + + +@celery.task(bind=True) +def task_dull(self, uuid_name, agree, mask_rcnn_result): + """Process image and update during""" + celery_logger.debug('process_image_task, uuid: {}'.format(uuid_name)) + + upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(agree) + + files = [] + im = Image.open(os.path.join(upload_dir, uuid_name + '.jpg')).convert('RGB') + #im_np = cv.cvtColor(imx.ensure_np(im),cv.COLOR_RGB2BGR) + im_np = imx.ensure_np(im) + im_np = im_np[:,:,::-1] + im = im.resize((256,256)) + im_np_256 = imutils.resize(im_np,width=256) + + # Add original + fpath = os.path.join(render_dir, uuid_name + '_orig.jpg') + im.save(fpath, 'JPEG', quality=95) + files.append({ + 'title': 'Original', + 'fn': render_uri + uuid_name + '_orig.jpg' + }) + + if mask_rcnn_result['valid']: + # ----------------------------------------------- + # Segment image (processed in views) + # seems to be an error with async celery processor? + # ----------------------------------------------- + + # parse mrcnn data + im_mask = cv.imread(mask_rcnn_result['fp_im_mask']) + seg_mask = cv.imread(mask_rcnn_result['fp_seg_mask']) + #score = mask_rcnn_result['score'] + #name = mask_rcnn_result['name'] + #color = mask_rcnn_result['color'] + files.append({ + 'title': 'Semantic Segmentation', + 'fn': render_uri + uuid_name + '_seg_mask.jpg' + }) + files.append({ + 'title': 'Semantic Segmentation Isolate', + 'fn': render_uri + uuid_name + '_im_mask.jpg' + }) + + + # ----------------------------------------------- + # run rag generator + # ----------------------------------------------- + + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': 0.50, + 'message': 'Applying Region Adjacency Graph', + 'uuid': uuid_name + }) + + # save the regions adjancency graph + im_rag = imx.create_rag_mean(im_mask,compactness=30,n_segments=128) + fpath = os.path.join(render_dir, uuid_name + '_rgraph.jpg') + imx.save_np_as_pil(fpath,im_rag,quality=95) + files.append({ + 'title': 'Region Adjacency Graph', + 'fn': render_uri + uuid_name + '_rgraph.jpg' + }) + + + # ----------------------------------------------- + # generate p2p fake + # ----------------------------------------------- + + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': 0.75, + 'message': 'Running generative adversarial network...', + 'uuid': uuid_name + }) + + + # convert segmentation to mask + seg_mask_gray = cv.cvtColor(seg_mask,cv.COLOR_BGR2GRAY) + seg_mask_gray[seg_mask_gray > 1] = 255 + + # find best P2P fit + ims_p2p = [] + match_amts = [] + iters = 15 + for i in range(0,iters): + im_p2p = p2p.create_p2p(im_rag) + ims_p2p.append(im_p2p) + im_p2p_mask = cv.cvtColor(im_p2p,cv.COLOR_RGB2GRAY) + im_p2p_mask[im_p2p_mask > 1] = 255 + # find where masks intersect + matches = np.bitwise_and(im_p2p_mask,seg_mask_gray) + amt = len(np.where(matches == 255)[0]) + match_amts.append(amt) + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': 0.75, + 'message': 'Generating ({}/{})'.format(i,iters), + 'uuid': uuid_name + }) + + best_idx = np.argmax(match_amts) + im_p2p = ims_p2p[best_idx] + + fpath = os.path.join(render_dir, uuid_name + '_gan.jpg') + imx.save_np_as_pil(fpath,im_p2p,quality=95) + files.append({ + 'title': 'Generative Adversarial Network', + 'fn': render_uri + uuid_name + '_gan.jpg' + }) + + + # ----------------------------------------------- + # generate p2p fake + # ----------------------------------------------- + + # announce to user + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': 0.90, + 'message': 'Compositing images...', + 'uuid': uuid_name + }) + + + # apply masked cloning + im_p2p_gray = cv.cvtColor(im_p2p,cv.COLOR_BGR2GRAY) + im_clone_mask = np.zeros_like(im_p2p_gray,dtype=np.uint8) + im_clone_mask[im_p2p_gray > 1] = 255 + + + # apply smoothed copy+paste clone + im_blur_mask = np.zeros(im_np_256.shape[:2],dtype=np.float64) + im_blur_mask[im_p2p_gray > 1] = 1.0 + im_blur_mask = np.array([im_blur_mask,im_blur_mask,im_blur_mask]).transpose((1,2,0)) + + # erode mask to remove black border + kernel = np.ones((3,3),np.uint8) + im_blur_mask = cv.erode(im_blur_mask,kernel,iterations = 3) + + # feather mask + feather_amt = (3,3) + im_blur_mask = (cv.GaussianBlur(im_blur_mask,feather_amt, 0) > 0) * 1.0 #? + im_blur_mask = cv.GaussianBlur(im_blur_mask,feather_amt, 0) + im_blur_mask = np.clip(im_blur_mask,0.0,1.0) + + # mask p2p fg --> photo bg + im_dull = im_np_256.astype(np.float64) * (1.0 - im_blur_mask) + im_p2p.astype(np.float64) * im_blur_mask + im_dull = im_dull.astype(np.uint8) + + + else: + print('No person. Apply background P2P') + celery_logger.debug('No person. Apply background P2P, uuid: {}'.format(uuid_name)) + im_bg_blur = cv.GaussianBlur(im_np_256,(31,31),0) + im_bg_rag = imx.create_rag_mean(im_bg_blur,compactness=30,n_segments=64) + + # apply gan + im_dull = p2p_objects.create_p2p(im_bg_rag) + + # resize back to full 512px + im_dull_512 = imutils.resize(im_dull,width=512) + + # save dulldream image + fpath = os.path.join(render_dir, uuid_name + '_dull.jpg') + imx.save_np_as_pil(fpath,im_dull_512,quality=95) + files.append({ + 'title': 'Your DullDream', + 'fn': render_uri + uuid_name + '_dull.jpg' + }) + + + # ----------------------------------------------- + # Write data to disk + # ----------------------------------------------- + + data = { + 'uuid': uuid_name, + 'date': str(datetime.datetime.now()), + 'files': files + } + + json_path = os.path.join(json_dir, uuid_name + '.json') + with open(json_path, 'w') as json_file: + json.dump(data, json_file) + + return { + 'percent': 100, + 'state': 'complete', + 'uuid': uuid_name + } + + + + +@celery.task(bind=True) +def blur_task(self, uuid_name, agree, extra): + """Process image and update during""" + celery_logger.debug('process_image_task, uuid: {}'.format(uuid_name)) + + upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(agree) + + files = [] + + im = Image.open(os.path.join(upload_dir, uuid_name + '.jpg')).convert('RGB') + im = im.resize((256,256)) + files.append({ + 'title': 'Original image', + 'fn': upload_uri + uuid_name + '.jpg' + }) + + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': 0.25, + 'message': 'Applying blur', + 'uuid': uuid_name + }) + + im_np = utils.ensure_np(im) + im_blur = cv.blur(im_np, (5,5), 1.0) + im_blur_pil = utils.ensure_pil(im_blur) + + fn = uuid_name + '_blur.jpg' + fpath = os.path.join(render_dir, fn) + im_blur_pil.save(fpath, 'JPEG', quality=95) + + files.append({ + 'title': 'Blurred image', + 'fn': render_uri + uuid_name + '_blur.jpg' + }) + + time.sleep(3) + + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': 0.50, + 'message': 'Sleeping for some reason', + 'uuid': uuid_name + }) + time.sleep(2) + + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': 0.75, + 'message': 'Sleeping some more', + 'uuid': uuid_name + }) + time.sleep(2) + + data = { + 'uuid': uuid_name, + 'date': str(datetime.datetime.now()), + 'files': files + } + + json_path = os.path.join(json_dir, uuid_name + '.json') + with open(json_path, 'w') as json_file: + json.dump(data, json_file) + + celery_logger.debug('ok') + + return { + 'percent': 100, + 'state': 'complete', + 'uuid': uuid_name, + } + +@celery.task(bind=True) +def sleep_task(self, uuid_name): + celery_logger.debug('sleep_task'.format(uuid_name)) + msgs = [ + {'msg':'Uploaded OK','time':.1}, + {'msg':'Segmenting Image...','time':2}, + {'msg':'Found: Person, Horse','time':1}, + {'msg':'Creating Pix2Pix','time':2} + ] + for i,m in enumerate(msgs): + percent = int(float(i)/float(len(msgs))*100.0) + self.update_state( + state = 'PROCESSING', + meta = { + 'percent': percent, + 'message': m['msg'], + 'uuid': uuid_name + }) + celery_logger.debug(m['msg']) + time.sleep(m['time']) + + return { + 'percent': 100, + 'state': 'complete', + 'uuid': uuid_name + } + +def make_task_json(): + dropdown = {} + for k,v in task_lookup.items(): + if 'active' not in v or v['active'] is not False: + is_default = 'default' in v and v['default'] is True + task = { + 'name': k, + 'title': v['title'], + 'selected': is_default, + } + dropdown[k] = task + return json.dumps(dropdown) + +# Add all valid tasks to this lookup. +# Set 'active': False to disable a task +# Set 'default': True to define the default task + +task_lookup = { + 'sleep': { + 'title': 'Sleep Test', + 'task': sleep_task, + 'active': False + }, + 'blur': { + 'title': 'Blur', + 'task': blur_task, + 'active': False + }, + 'task_dull': { + 'title': 'DullDream V2', + 'task': task_dull, + 'active': True, + 'default': True + } +} + diff --git a/old/server/app/main/utils.py b/old/server/app/main/utils.py new file mode 100644 index 00000000..510e5c23 --- /dev/null +++ b/old/server/app/main/utils.py @@ -0,0 +1,37 @@ +from flask import current_app as app +from PIL import Image +import numpy as np +import cv2 as cv +import os +from os.path import join + +def ensure_pil(im): + try: + im.verify() + return im + except: + return Image.fromarray(im.astype('uint8'), 'RGB') + +def ensure_np(im): + if type(im) == np.ndarray: + return im + return np.asarray(im, np.uint8) + +def get_recent_uploads(limit=10): + d_uploads = app.config['UPLOADS'] + d_renders = app.config['RENDERS'] + + # list all files in uploads dir + filenames = [s for s in os.listdir(d_uploads) + if os.path.isfile(os.path.join(d_uploads, s))] + # sort upload files by date + filenames.sort(key=lambda s: os.path.getmtime(os.path.join(d_uploads, s)),reverse=True) + basenames = [os.path.splitext(os.path.basename(f))[0] for f in filenames] + basenames = basenames[:limit] + filenames = [f for f in basenames if os.path.isfile(join(d_renders,'{}_dull.jpg'.format(f)))] + + # create list for uploads and renders + uploads = [join('/img/uploads',f) for f in filenames] + renders = [join('/img/renders','{}_dull'.format(f)) for f in filenames] + urls = [join('/d',f) for f in basenames] + return uploads, renders, urls diff --git a/old/server/app/main/views.py b/old/server/app/main/views.py new file mode 100644 index 00000000..11a8ca53 --- /dev/null +++ b/old/server/app/main/views.py @@ -0,0 +1,300 @@ +import os +import uuid +import json +from flask import render_template, redirect, url_for, send_from_directory +from flask import request, make_response, jsonify +from . import main, utils +from .tasks import task_lookup, make_task_json +from PIL import Image, ImageOps +import cv2 as cv + +from .paths import get_paths + +from flask import current_app as app +from werkzeug.utils import secure_filename +import imutils + +# ------------------------------------------------------------ +# Temp: run mask rcnn outside celery +# ------------------------------------------------------------ + +# init image processors +import sys +from .img_proc_config import ImgProcConfig +sys.path.append('/dulldream/src/') +from image_processors.mask_rcnn import MaskRCNN +from utils import imx +from utils import fiox + +img_proc_congif = ImgProcConfig() +mask_rcnn = MaskRCNN(img_proc_congif.mask_rcnn_class_config, + model_path=img_proc_congif.mask_rcnn_model) + +# ------------------------------------------------------------ +# Tasks +# ------------------------------------------------------------ + +@main.route('/status//') +def task_status(task_name, task_id): + """Return celery image processing status""" + if task_name in task_lookup: + task = task_lookup[task_name]['task'].AsyncResult(task_id) + else: + return jsonify({ + 'state': 'error', + 'percent': 100, + 'message': 'Unknown task' + }) + + app.logger.info('task state: {}'.format(task.state)) + if task.state == 'PENDING': + response = { + 'state': task.state, + 'percent': 0, + 'message': 'Pending...' + } + elif task.state != 'FAILURE': + response = { + 'state': task.state, + 'percent': task.info.get('percent', 0), + 'uuid': task.info.get('uuid', 0), + 'message': task.info.get('message', '') + } + if 'result' in task.info: + response['result'] = task.info['result'] + else: + # something went wrong in the background job + response = { + 'state': task.state, + 'percent': 100, + 'message': str(task.info), # this is the exception raised + } + return jsonify(response) + +# ------------------------------------------------------------ +# POST Routes +# ------------------------------------------------------------ + +@main.route('/upload/sleep', methods=['GET', 'POST']) +def sleep_test(): + async_task = task_lookup['sleep']['task'].apply_async(args=['sleep_test']) + task_url = url_for('main.task_status', task_name='sleep', task_id=async_task.id) + return jsonify({ + 'result': True, + 'task_url': task_url, + }) + +@main.route('/upload', methods=['POST']) +def upload(): + + style = request.form['style'] + print('style',style) + if style in task_lookup: + task = task_lookup[style]['task'] + print('task',task) + else: + return jsonify({ + 'result': False, + 'error': 'Unknown task', + }) + + file = request.files['user_image'] + agree = bool(request.form['agree']) + ext = request.form['ext'] + if ext is None: + ext = request.files['ext'] + + uuid_name = str(uuid.uuid4()) + + app.logger.info('[+] style: {}'.format(style)) + app.logger.info('[+] ext: {}'.format(ext)) + app.logger.info('[+] uuid_name: {}'.format(uuid_name)) + app.logger.info('[+] agreed: {}'.format(agree)) + + # convert PNG to JPG + print('[+] Resizing image') + + # LOL MaskRCNN needs to be run outside of the Celery Task + im = Image.open(file.stream).convert('RGB') + im = ImageOps.fit(im,(512,512)) + if agree: + upload_folder = app.config['UPLOADS'] + else: + upload_folder = app.config['UPLOADS_PRIVATE'] + + fpath = os.path.join(upload_folder, uuid_name + '.jpg') + + # Save image to disk + print('[+] Save image to {}'.format(fpath)) + im.save(fpath, 'JPEG', quality=100) + im_pil_256 = im.resize((256,256)) + + print('[+] ensure_np...') + im_np = imx.ensure_np(im_pil_256) + #print('[+] resize np...') + #im_np = imutils.resize(im_np,width=256) + + upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(agree) + + print('[+] Run mrcnn...') + try: + result = mask_rcnn.create_segmentations(im_np,concat=True) + except: + print('[-] Error. Could not run mask_rcnn') + result = [] + + if len(result) > 0: + result = result[0] + + # save data, then pass to celery task + print('[+] Save masks') + seg_mask = result['seg_mask'] + fpath_seg_mask = os.path.join(render_dir, uuid_name + '_seg_mask.jpg') + #cv.imwrite(fpath_seg_mask,cv.cvtColor(seg_mask,cv.COLOR_BGR2RGB)) + #seg_mask = seg_mask[:,:,::-1] + seg_mask_pil = imx.ensure_pil(seg_mask) + seg_mask_pil.save(fpath_seg_mask, 'JPEG', quality=100) + + im_mask = result['im_mask'] + fpath_im_mask = os.path.join(render_dir, uuid_name + '_im_mask.jpg') + #im_mask = im_mask[:,:,::-1] + im_mask_pil = imx.ensure_pil(im_mask) + im_mask_pil.save(fpath_im_mask, 'JPEG',quality=100) + #cv.imwrite(fpath_im_mask,cv.cvtColor(im_mask,cv.COLOR_BGR2RGB)) + + celery_result = { + 'score':str(result['score']), + 'name':str(result['name']), + 'class_index':str(result['class_index']), + 'color':str(result['color']), + 'fp_im_mask':fpath_im_mask, + 'fp_seg_mask':fpath_seg_mask, + 'valid':True + } + else: + print('[-] no reults. process background only') + celery_result = { + 'score':None, + 'name':None, + 'class_index':None, + 'color':None, + 'fp_im_mask':None, + 'fp_seg_mask':None, + 'valid':False + } + + print('[+] Start celery') + async_task = task.apply_async(args=[uuid_name, agree, celery_result]) + task_url = url_for('main.task_status', task_name=style, task_id=async_task.id) + + return jsonify({ + 'result': True, + 'task_url': task_url, + 'uuid': uuid_name + }) + + + +# ---------------------------------------------------- +# Fileserver, temp solution +# ---------------------------------------------------- + +@main.route('/img//') +def get_image(imtype,uuid_name): + """Return image files from render or uploads""" + if imtype == 'uploads': + d = app.config['UPLOADS'] + suffix = '' + elif imtype == 'renders': + d = app.config['RENDERS'] + suffix = '' + elif imtype == 'fcn': + d = app.config['RENDERS'] + suffix = '_fcn8' + + fname = uuid_name + suffix + '.jpg' + fpath = os.path.join(d, fname) + + if os.path.isfile(fpath): + return send_from_directory(d,fname) + else: + return send_from_directory('static', 'img/404.jpg') + +# ---------------------------------------------------- +# Deleting images +# ---------------------------------------------------- + +def destroy_data(uuid_name, is_public): + uri_base = app.config['URI_BASE'] + upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(is_public) + + json_path = os.path.join(json_dir, uuid_name + '.json') + with open(json_path) as json_file: + data = json.load(json_file) + for f in data['files']: + path = os.path.join(uri_base, f['fn'][1:]) + if os.path.exists(path): + os.remove(path) + os.remove(json_path) + +@main.route('/d//destroy', strict_slashes=False) # public +def route_public_destroy(uuid_name): + destroy_data(uuid_name, True) + return redirect("/", code=302) + +@main.route('/p//destroy', strict_slashes=False) # private +def route_private_destroy(uuid_name): + destroy_data(uuid_name, False) + return redirect("/", code=302) + +# ---------------------------------------------------- +# Static routes +# ---------------------------------------------------- + +# Most of the pages are served with the single page app in index.html: + +task_json = make_task_json() + +@main.route('/', strict_slashes=False) +def index(): + return render_template('index.html', task_json=task_json) + +@main.route('/about', strict_slashes=False) +def about(): + return render_template('index.html', task_json=task_json) + +@main.route('/d/', strict_slashes=False) # public +def route_public(uuid_name): + return render_template('index.html', task_json=task_json) + +@main.route('/p/', strict_slashes=False) # private +def route_private(uuid_name): + return render_template('index.html', task_json=task_json) + +@main.route('/privacy', strict_slashes=False) +def privacy(): + return render_template('index.html', task_json=task_json) + +# Some of the pages have their own static file: + +@main.route('/gallery', strict_slashes = False) +def gallery(): + app.logger.info('access gallery') + uploads, renders, urls = utils.get_recent_uploads(limit=50) + uuids = [os.path.splitext(os.path.basename(f))[0] for f in uploads] + images = [{'upload':u,'render':r, 'url':url} for u,r,url in zip(uploads,renders,urls)] + return render_template('gallery.html',images=images) + +@main.route('/zkm', strict_slashes=False) +def zkm(): + app.logger.info('access ZkM') + return render_template('zkm.html') + +@main.route('/celery', strict_slashes=False) +def celery_route(): + return render_template('celery.html') + +@main.route('/projector', strict_slashes=False) +def projector(): + uploads, renders,urls = utils.get_recent_uploads() + return render_template('projector.html', uploads=uploads, renders=renders) diff --git a/old/server/app/static/css/bootstrap.min.css b/old/server/app/static/css/bootstrap.min.css new file mode 100644 index 00000000..ed3905e0 --- /dev/null +++ b/old/server/app/static/css/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/old/server/app/static/css/dullbrown-theme.css b/old/server/app/static/css/dullbrown-theme.css new file mode 100644 index 00000000..98aff038 --- /dev/null +++ b/old/server/app/static/css/dullbrown-theme.css @@ -0,0 +1,502 @@ +* { box-sizing: border-box; } +html { + margin: 0; padding: 0; + width: 100%; height: 100%; +} +body { + margin: 0; padding: 0; + width: 100%; height: 100%; + font-family: Helvetica, sans-serif; +} +body, .modal, #footer { + /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#a5ce3e+0,ffffff+50,a5ce3e+100 */ + background: #7B7568; /* Old browsers */ + background: -moz-linear-gradient(left, #7B7568 0%, #ffffff 50%, #7B7568 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(left, #7B7568 0%,#ffffff 50%,#7B7568 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to right, #7B7568 0%,#ffffff 50%,#7B7568 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#7B7568', endColorstr='#a5ce3e',GradientType=1 ); /* IE6-9 */ +} + +/* ------------------------------------------------ */ +/* navbar */ +.navbar-default a.navbar-brand{ +} +.navbar-default{ + background: transparent; +} +.navbar{ + margin-bottom: 0; + border:0; +} + +.navbar-default .navbar-brand a{ + color:#ccc; +} +.navbar-default a.navbar-brand{ + color:#ccc; +} +.navbar-default a.navbar-brand:hover{ + color:#fff; +} + +/* Hamburger */ +.navbar-default .navbar-toggle{ + color:#ccc; +} +.navbar-default .navbar-toggle .icon-bar{ + color:#ccc; +} +.navbar-default .navbar-toggle .icon-bar:hover{ + color:#fff; +} +.navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover{ + color:#fff; + background: transparent; +} +.navbar{ + border-radius: 0px; + min-height:30px; +} +.navbar-default .navbar-text{ + color:#ccc; +} +.navbar-default .navbar-nav>li>a{ + color:#ccc; +} +.navbar-default .navbar-nav>li>a:hover{ + color:#fff; +} +.navbar-default .navbar-toggle .icon-bar{ + background-color:#ccc; +} +.navbar-default .navbar-toggle:hover .icon-bar{ + background-color: #eee; +} +.navbar-default .navbar-toggle:hover { + border-color: #fff; +} +.navbar-default .navbar-collapse, .navbar-default .navbar-form{ + border:0; +} + +/* ------------------------------------------------ */ +/* Jumbotron */ +.jumbotron { + padding-top: 0px; + padding-bottom: 0px; + margin-bottom: 0px; + color: inherit; +} +.jumbotron{ + background: transparent; + color:black; +} +.jumbotron h1{ + color:#ddd; + margin-bottom:0px; +} +.jumbotron a.btn-primary{ + background:#ddd; + color:#333; +} +.jumbotron a.btn-primary:hover{ + background:#eee; + color:#222; +} +.jumbotron p > a.jcallout{ + color:#eee; + padding-bottom: 3px; + border-bottom:1px dotted; + text-decoration: none; +} +.jumbotron p > a.jcallout:hover{ + color:#fff; + border-bottom:1px solid #ccc; + text-decoration: none; +} +.jumbotron a.btn-default{ + color:#eee; + border:1px solid #eee; + background: transparent; +} +.jumbotron a.btn-default:hover{ + background: #22f; + border:1px solid #ccc; +} +.jumbotron a.btn-default:active{ + color:#eee; + border:1px solid #ccc; +} + +/* Input button override +-------------------------------------------------- */ +/*input[type="file"] { + display: none; +} +input[type="button"] { + display: none; +}*/ +input.hidden_input{ + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; +} + +/* Global styles +-------------------------------------------------- */ +h1,h2,h3,h4,h5,h6 { + font-weight: bold; + font-style: italic; + text-align: center; +} +h1 { + font-size:56px; + margin-top: 20px; + margin-bottom: 0; +} +h2 { + font-size:20px; + margin-top: 0; + margin-bottom: 24px; +} +ul { + list-style: none; + margin:0; + padding:0; +} +li { + display: inline-block; +} +img.img_responsive_dull { + max-width: 100%; + height: auto; +} +#photo_area{ + width: 512px; + height: 512px; + max-width: 97vw; + max-height: 97vw; + min-width: 240px; + min-height: 240px; + margin:0 auto; + text-align: center; +} +.dash_border{ + background-color: rgba(255,255,255,.2); + border:1px dashed #000; +} + +label { + display: block; +} + +div.center_inner { + position: relative; + top: 50%; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} +#upload_controls{ + margin-top:25px; + display: none; +} +#restart_btn, #rotate_btn, #upload_btn, #dropdown_btn { + display: inline-block; + margin-left:5px; + margin-right:5px; +} +.custom-file-upload { + display: inline-block; + padding: 6px 12px; + cursor: pointer; +} + +.align_center{ + text-align: center; +} + +ul.action-buttons{ + margin-top: 40px; + list-style: none; + margin-left: 0; + padding-left:0; +} +li { + list-style: none; + margin-left: 0; + padding-left:0; + /*margin-bottom:20px;*/ +} + +.btn { + display: inline-block; + color: #333; + background-color: #fff; + border: 1px solid #adadad; + font-family: Helvetica, sans-serif; + padding: 6px 12px; + margin: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border-radius: 4px; + text-decoration: none; + transition: all 0.15s; +} +.desktop .btn:hover { + background-color: #fff; +} +.btn.btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn.btn-important { + border-width: 2px; + border-color: #444; +} +#photo_area { + position: relative; + cursor: pointer; +} +#restart_btn { + position: relative; + cursor: pointer; +} +input[type=file] { + cursor: pointer; + opacity: 0; + position: absolute; + top: 0; left: 0; + width: 100%; height: 100%; +} +.desktop #photo_area .btn { + background: #fff; +} +.desktop #photo_area .btn:hover { + background: #eee; +} +.consent_box { + margin-top: 10px; + font-size: smaller; + color: #444; +} + +/* Intro page +-------------------------------------------------- */ +canvas { + display: block; +} +.photo { + display: none; + width:100%; + height:100%; +} + +/* form visibility */ + +#preloader_anim { + display: none; +} +#about_btn { + margin: 20px 0px 80px 0; +} +#share_btns { + display: none; + margin:20px; +} +.notice { + color: #444; + font-size: small; +} +a.btn-default { + background-color: transparent; +} +a.btn-default:hover { + color: #333; + background-color: #fff; + border-color: #adadad; +} +.debug-view { + margin-bottom: 20px; + font-size:14px; + color:#333; +} +.debug-view img { + margin-bottom:4px; +} +#full_results, #hide_more { + display: none; +} + +select { + display: inline-block; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + color: #555; + vertical-align: middle; + background-color: #eee; + background-image: none; + border: 1px solid #bbb; + border-radius: 4px; + transition: all .15s; + cursor: pointer; +} +.desktop select:hover { + background-color: #fff; + cursor: pointer; +} + +/* About +--------------------------------------------------- */ +.modal { + pointer-events: none; + opacity: 0; + width: 100%; + height: 100%; + position: fixed; + top: 0; left: 0; + transition: all 0.2s; +} +.modal.visible { + pointer-events: auto; + opacity: 1; +} +.modal p { + font-size: 16px; + line-height: 24px; +} +.modal .inner { + margin: 10vh auto; + background: rgba(255,255,255,0.5); + padding: 20px 20px 40px 20px; + width: 600px; + max-width: 90vw; +} +.modal .content { + margin-bottom: 20px; +} + +/* Result +--------------------------------------------------- */ +.result_view { + display: none; + text-align: center; +} +.final_result img { + width: 512px; + height: 512px; + border: 1px dashed #000; + margin: 10px; +} +.all_results { + display: none; +} +.all_results div { + margin-bottom: 20px; +} +.all_results img { + width: 384px; + height: 384px; + margin: 10px; +} + +.made_with { + margin-bottom: 10px; +} +#delete_btns { + margin-top: 10px; + font-size: 10px; +} +a#destroy_data { + color: #888; +} +.desktop a#destroy_data:hover { + color: #f00; +} + +/* Footer +--------------------------------------------------- */ +#footer{ + /*background: #ddd;*/ + /*padding: 20px 0;*/ + /*position: fixed;*/ + bottom: 0; + width: 100%; + text-align: center; + padding-top: 40px; + padding-bottom: 20px; +} +#footer ul li a { + font-weight: bold; + text-decoration: none; +} +#footer a { + color:#333; + text-decoration: underline; +} +#footer a:hover { + color:#111; +} + +@media screen and (max-width: 500px) { + .modal { + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + } + .modal .inner { + margin: 0; + width: 100vw; + height: 100vh; + max-width: 100vw; + } +} + + +/* Gallery */ + +.gallery-preview-row{ + margin-bottom: 40px +} +.gallery-preview-row img{ + width:100%; + padding:2px; +} + +@media (max-width: 600px) { + #preloader_anim { + position: relative; + top: -50px; + } +} + +/*hide dropdown*/ +#dropdown_btn{ + display: none; +} \ No newline at end of file diff --git a/old/server/app/static/css/projector.css b/old/server/app/static/css/projector.css new file mode 100644 index 00000000..401f0dff --- /dev/null +++ b/old/server/app/static/css/projector.css @@ -0,0 +1,52 @@ +html, body, #wrapper { + height:100%; + width: 100%; + margin: 0; + padding: 0; + border: 0; + background-color: #000; +} +table{ + padding:0; + margin:0; + border-spacing: 0; +} +table td{ + padding:0; + margin:0; +} +#wrapper td { + vertical-align: middle; + text-align: center; +} +#wrapper img{ + margin-top:-350px; + margin-left:-350px; +} +.left{ + background-color: #000; +} +.right{ + background-color: #000;; +} + +#container{ +} +#container-left{ + width:50%; + float:left; + position: relative; +} +#container-right{ + width:50%; + float:right; + position: relative; +} + +.cycle{position:relative;display: none;} +.cycle img{position:absolute;z-index:1} +.cycle img.active{z-index:100} +.cycle img{ + width:700px; + height:700px; +} \ No newline at end of file diff --git a/old/server/app/static/js/app.js b/old/server/app/static/js/app.js new file mode 100644 index 00000000..454d5c37 --- /dev/null +++ b/old/server/app/static/js/app.js @@ -0,0 +1,158 @@ +var app = (function(){ + + var app = {} + + app.init = function(){ + upload.init() + app.bind() + app.build() + app.resize() + app.route() + } + app.bind = function(){ + $(window).on('resize', app.resize) + $(".about_button").on('click', app.about_show) + $(".privacy_button").on('click', app.privacy_show) + $(".modal").on('click', app.modal_hide) + $(".modal .btn").on('click', app.modal_hide) + $(".modal .inner").on('click', preventDefault) + $("#destroy_data").on('click', app.destroyData) + $("#show_all_results").on('click', app.showAllResults) + } + app.build = function(){ + var items = JSON.parse(decodeEntities($("#dropdown_options").html())) + var $dropdown = $("#dropdown") + var options = Object.keys(items).sort().map(key => { + var item = items[key] + var option = document.createElement('option') + option.value = item.name + option.innerHTML = item.title + if (item.selected) option.selected = true + $dropdown.append(option) + }) + var loader = new Image () + loader.src = '/static/img/loader.gif' + } + app.resize = function(){ + var $el = $('#photo_area') + var w = $el.width() + $el.height($el.width()) + } + app.route = function(){ + const path = window.location.pathname.split('/') + path.shift() + switch (path[0]) { + case 'd': + app.processingComplete(path[1], true) // public + break + case 'p': + app.processingComplete(path[1], false) // private + break + case 'about': + app.about_show() + break + case 'privacy': + app.privacy_show() + break + default: + // load index, default state + break + } + } + + /* upload UI changes */ + + app.didPickPhoto = function(){ + $('#upload_controls').fadeIn() + $('#user_photo_canvas').show() + $('#take_photo_btn').hide() + } + app.didClickUpload = function(){ + $('#upload_controls').slideUp('fast') + $('#user_photo_canvas').hide() + $('#preloader_anim').fadeIn('fast') + $('#progress').fadeIn() + } + app.uploadDidComplete = function(){ + $('#preloader_anim').hide() + $('#progress').hide() + } + app.uploadDidComplete = function(){ + // $('#preloader_anim').hide() + // $('#progress').hide() + } + app.updateProgress = function(message, percentage){ + message = message || "Processing..." + percentage = percentage || 0 + $("#progress").html(message) + } + app.processingComplete = function(uuid, is_public){ + $('#preloader_anim').hide() + $('#progress').hide() + // + $("header h2").html("Your dull result") + $(".upload_view").hide() + $(".results_view").show() + var endpoint = is_public ? 'json' : 'json_private' + $.getJSON('/static/media/' + endpoint + '/' + uuid + '.json', function(data){ + console.log(data) + var template = $("#result_template").html() + var final_result = new Image + final_result.src = data.files[data.files.length-1].fn + $(".final_result").empty() + $(".all_results").empty() + $(".final_result").append(final_result) + data.files.forEach(function(file){ + var t = template.replace(/{img}/, file.fn).replace(/{title}/, file.title) + $(".all_results").append(t) + }) + $(".result_view").show() + $(".permalink").attr('href', window.location.href) + }).fail(function(){ + console.log('error fetching json') + window.location.href = '/' + }) + } + var detailed = false + app.showAllResults = function(){ + if (!detailed) { + detailed = true + $(this).html('Hide') + $(".all_results").fadeIn('fast') + } else { + detailed = false + $(this).html('Detailed Analysis') + $(".all_results").slideUp('fast') + } + } + app.destroyData = function(){ + var uuid = window.location.pathname.split('/')[2] + var confirmed = confirm("Do you really want to delete your dull dream?") + if (confirmed) { + $.get( [window.location.pathname, 'destroy'].join('/').replace('//', '/') ).always(function(){ + alert('Dull dream deleted!') + window.location.href = '/' + }) + } + } + + /* modals */ + + app.about_show = function(e){ + e.preventDefault() + $(".about_view").addClass('visible') + } + app.privacy_show = function(e){ + e.preventDefault() + $(".privacy_view").addClass('visible') + } + app.modal_hide = function(e){ + e.preventDefault() + e.stopPropagation() + $(".modal").removeClass('visible') + } + + document.addEventListener('DOMContentLoaded', app.init) + + return app +})() \ No newline at end of file diff --git a/old/server/app/static/js/upload.js b/old/server/app/static/js/upload.js new file mode 100644 index 00000000..27437e43 --- /dev/null +++ b/old/server/app/static/js/upload.js @@ -0,0 +1,319 @@ +var messages = { + is_processing: "Running semantic segmentation...", + upload_failed: "Error attempting to upload the file.", + upload_cancelled: "Upload cancelled or browser dropped connection.", + unable_to_compute: "We're sorry! We were unable to compute your image.", + pending: "Sending to Generative Adversarial Network...", + complete: "Processing complete!", +} + +var upload = (function(){ + var upload = {} + var uploading = false + + var MAX_SIDE = 512 + + upload.init = function(){ + upload.bind() + } + + upload.bind = function(){ + $("input[type=file]").on('change', upload.change) + $("#upload_btn").on('click', upload.go) + document.body.addEventListener("dragover", upload.dragover) + document.body.addEventListener("dragleave", upload.dragover) + document.body.addEventListener("drop", upload.change) + } + + upload.dragover = function(e){ + e.stopPropagation() + e.preventDefault() + } + + upload.change = function(e){ + e.preventDefault() + var files = e.dataTransfer ? e.dataTransfer.files : e.target.files + if (files.length) { + var file = files[files.length - 1] + if (!file.type.match('image.*')) + return + var reader = new FileReader() + reader.onload = onReaderLoad + reader.readAsDataURL(file) + } + function onReaderLoad(e) { + // Don't leak! + reader.onload = null + var img = new Image + img.onload = function(){ + img.onload = null + upload.ready(img) + } + img.src = e.target.result + } + } + + upload.ready = function(img){ + var resized = renderToCanvas(img, { correctOrientation: true }) + var canvas = document.querySelector('#user_photo_canvas') + ctx = canvas.getContext('2d') + ctx.fillStyle = 'black' + ctx.fillRect(0, 0, MAX_SIDE, MAX_SIDE) + var x_offset = (MAX_SIDE - resized.width) / 2 + var y_offset = (MAX_SIDE - resized.height) / 2 + + ctx.drawImage(resized, x_offset, y_offset) + app.didPickPhoto() + } + + upload.go = function(){ + if (uploading) return + uploading = true + app.didClickUpload() + try { + var canvas = document.querySelector('#user_photo_canvas') + var cb = canvas.toBlob(function(blob){ + upload.send(blob) + }, 'image/jpeg', 89) + } catch(e){ + app.updateProgress(messages.unable_to_compute) + } + } + + upload.send = function(blob){ + console.log("sending upload...") + var fd = new FormData() + fd.append('user_image', blob) + fd.append('ext', 'jpg') + fd.append('style', $("#dropdown").val()) + fd.append('agree', $("#agree").val() || 0) + + var xhr = new XMLHttpRequest() + xhr.upload.addEventListener("progress", upload.progress, false) + xhr.addEventListener("load", upload.complete, false) + xhr.addEventListener("error", upload.failed, false) + xhr.addEventListener("abort", upload.canceled, false) + xhr.open("POST", "/upload") + xhr.send(fd) + } + + upload.progress = function (e) { + if (e.lengthComputable) { + var percentComplete = Math.round(e.loaded * 100 / e.total) + if (percentComplete > 99) { + app.updateProgress(messages.is_processing) + } else { + app.updateProgress("Uploaded " + percentComplete.toString() + '%') + } + } + else { + app.updateProgress(messages.unable_to_compute) + } + } + + upload.complete = function (e) { + uploading = false + try { + var data = JSON.parse(e.target.responseText) + } catch (e) { + return app.updateProgress(messages.upload_failed) + } + app.uploadDidComplete() + upload.data = data + upload.task_progress(data.task_url) + } + + upload.failed = function (evt) { + uploading = false + app.updateProgress(messages.upload_failed) + } + + upload.cancelled = function (evt) { + uploading = false + app.updateProgress(messages.upload_cancelled) + } + + upload.task_progress = function (status_url) { + var is_public = $("#agree").val() || 0 + var uuid = upload.data.uuid + $.getJSON(status_url, function(data){ + console.log(data) + var alive = true + var delay = 500 + switch(data.state) { + case 'PENDING': + app.updateProgress(messages.pending) + delay = 2000 + break + case 'PROCESSING': + app.updateProgress(data.message, data.percent) + delay = 500 + break + case 'SUCCESS': + app.updateProgress(messages.complete) + if (is_public) { + history.pushState({}, 'DullDream', '/d/' + uuid) + } else { + history.pushState({}, 'DullDream', '/p/' + uuid) + } + app.processingComplete(uuid, is_public) // truthy if private + alive = false + break + default: + // NB: error state + alive = false + break + } + if (alive) { + setTimeout(function() { + upload.task_progress(status_url) + }, delay) + } + }) + } + + + function renderToCanvas(img, options) { + if (!img) return + options = options || {} + + // Canvas max size for any side + var maxSize = MAX_SIDE + var canvas = document.createElement('canvas') + var ctx = canvas.getContext('2d') + var initialScale = options.scale || 1 + // Scale to needed to constrain canvas to max size + var scale = getScale(img.width * initialScale, img.height * initialScale, maxSize, maxSize, true) + // Still need to apply the user defined scale + scale *= initialScale + var width = canvas.width = Math.round(img.width * scale) + var height = canvas.height = Math.round(img.height * scale) + var correctOrientation = options.correctOrientation + var jpeg = !!img.src.match(/data:image\/jpeg|\.jpeg$|\.jpg$/i) + var hasDataURI = !!img.src.match(/^data:/) + + ctx.save() + + // Can only correct orientation on JPEGs represented as dataURIs + // for the time being + if (correctOrientation && jpeg && hasDataURI) { + applyOrientationCorrection(canvas, ctx, img.src) + } + // Resize image if too large + if (scale !== 1) { + ctx.scale(scale, scale) + } + + ctx.drawImage(img, 0, 0) + ctx.restore() + + return canvas + } + + function getScale(width, height, viewportWidth, viewportHeight, fillViewport) { + fillViewport = !!fillViewport + var landscape = (width / height) > (viewportWidth / viewportHeight) + if (landscape) { + if (fillViewport) { + return fitVertical() + } else if (width > viewportWidth) { + return fitHorizontal() + } + } else { + if (fillViewport) { + return fitHorizontal() + } else if (height > viewportHeight) { + return fitVertical() + } + } + return 1 + + function fitHorizontal() { + return viewportWidth / width + } + + function fitVertical() { + return viewportHeight / height + } + } + + function applyOrientationCorrection(canvas, ctx, uri) { + var orientation = getOrientation(uri) + // Only apply transform if there is some non-normal orientation + if (orientation && orientation !== 1) { + var transform = orientationToTransform[orientation] + var rotation = transform.rotation + var mirror = transform.mirror + var flipAspect = rotation === 90 || rotation === 270 + if (flipAspect) { + // Fancy schmancy swap algo + canvas.width = canvas.height + canvas.width + canvas.height = canvas.width - canvas.height + canvas.width -= canvas.height + } + if (rotation > 0) { + applyRotation(canvas, ctx, rotation) + } + } + } + + function applyRotation(canvas, ctx, deg) { + var radians = deg * (Math.PI / 180) + if (deg === 90) { + ctx.translate(canvas.width, 0) + } else if (deg === 180) { + ctx.translate(canvas.width, canvas.height) + } else if (deg == 270) { + ctx.translate(0, canvas.height) + } + ctx.rotate(radians) + } + + function getOrientation (uri) { + var exif = new ExifReader + // Split off the base64 data + var base64String = uri.split(',')[1] + // Read off first 128KB, which is all we need to + // get the EXIF data + var arr = base64ToUint8Array(base64String, 0, Math.pow(2, 17)) + try { + exif.load(arr.buffer) + return exif.getTagValue('Orientation') + } catch (err) { + return 1 + } + } + + function base64ToUint8Array(string, start, finish) { + var start = start || 0 + var finish = finish || string.length + // atob that shit + var binary = atob(string) + var buffer = new Uint8Array(binary.length) + for (var i = start; i < finish; i++) { + buffer[i] = binary.charCodeAt(i) + } + return buffer + } + + /** + * Mapping from EXIF orientation values to data + * regarding the rotation and mirroring necessary to + * render the canvas correctly + * Derived from: + * http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ + */ + var orientationToTransform = { + 1: { rotation: 0, mirror: false }, + 2: { rotation: 0, mirror: true }, + 3: { rotation: 180, mirror: false }, + 4: { rotation: 180, mirror: true }, + 5: { rotation: 90, mirror: true }, + 6: { rotation: 90, mirror: false }, + 7: { rotation: 270, mirror: true }, + 8: { rotation: 270, mirror: false } + } + + + return upload +})() \ No newline at end of file diff --git a/old/server/app/static/js/util.js b/old/server/app/static/js/util.js new file mode 100644 index 00000000..851f634a --- /dev/null +++ b/old/server/app/static/js/util.js @@ -0,0 +1,32 @@ +var is_iphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) +var is_ipad = (navigator.userAgent.match(/iPad/i)) +var is_android = (navigator.userAgent.match(/Android/i)) +var is_mobile = is_iphone || is_ipad || is_android +var is_desktop = ! is_mobile; + +document.body.parentNode.classList.add(is_desktop ? 'desktop' : 'mobile') + +function preventDefault(e){ + e.preventDefault() + e.stopPropagation() +} + +var decodeEntities = (function() { + // this prevents any overhead from creating the object each time + var element = document.createElement('div'); + + function decodeHTMLEntities (str) { + if(str && typeof str === 'string') { + // strip script/html tags + str = str.replace(/]*>([\S\s]*?)<\/script>/gmi, ''); + str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, ''); + element.innerHTML = str; + str = element.textContent; + element.textContent = ''; + } + + return str; + } + + return decodeHTMLEntities; +})(); \ No newline at end of file diff --git a/old/server/app/static/js/vendor/ExifReader.js b/old/server/app/static/js/vendor/ExifReader.js new file mode 100644 index 00000000..a8343ede --- /dev/null +++ b/old/server/app/static/js/vendor/ExifReader.js @@ -0,0 +1,1363 @@ +// Generated by CoffeeScript 1.6.2 +/* +# ExifReader 1.1.1 +# http://github.com/mattiasw/exifreader +# Copyright (C) 2011-2014 Mattias Wallander +# Licensed under the GNU Lesser General Public License version 3 or later +# See license text at http://www.gnu.org/licenses/lgpl.txt +*/ + + +(function() { + (typeof exports !== "undefined" && exports !== null ? exports : this).ExifReader = (function() { + ExifReader.prototype._MIN_DATA_BUFFER_LENGTH = 2; + + ExifReader.prototype._JPEG_ID_SIZE = 2; + + ExifReader.prototype._JPEG_ID = 0xffd8; + + ExifReader.prototype._APP_MARKER_SIZE = 2; + + ExifReader.prototype._APP0_MARKER = 0xffe0; + + ExifReader.prototype._APP1_MARKER = 0xffe1; + + ExifReader.prototype._APP15_MARKER = 0xffef; + + ExifReader.prototype._APP_ID_OFFSET = 4; + + ExifReader.prototype._BYTES_Exif = 0x45786966; + + ExifReader.prototype._TIFF_HEADER_OFFSET = 10; + + ExifReader.prototype._BYTE_ORDER_BIG_ENDIAN = 0x4949; + + ExifReader.prototype._BYTE_ORDER_LITTLE_ENDIAN = 0x4d4d; + + function ExifReader() { + var _this = this; + + this._getTagValueAt = { + 1: function(offset) { + return _this._getByteAt(offset); + }, + 2: function(offset) { + return _this._getAsciiAt(offset); + }, + 3: function(offset) { + return _this._getShortAt(offset); + }, + 4: function(offset) { + return _this._getLongAt(offset); + }, + 5: function(offset) { + return _this._getRationalAt(offset); + }, + 7: function(offset) { + return _this._getUndefinedAt(offset); + }, + 9: function(offset) { + return _this._getSlongAt(offset); + }, + 10: function(offset) { + return _this._getSrationalAt(offset); + } + }; + this._tiffHeaderOffset = 0; + } + + /* + # Loads all the Exif tags from the specified image file buffer. + # + # data ArrayBuffer Image file data + */ + + + ExifReader.prototype.load = function(data) { + return this.loadView(new DataView(data)); + }; + + /* + # Loads all the Exif tags from the specified image file buffer view. Probably + # used when DataView isn't supported by the browser. + # + # @_dataView DataView Image file data view + */ + + + ExifReader.prototype.loadView = function(_dataView) { + this._dataView = _dataView; + this._tags = {}; + this._checkImageHeader(); + this._readTags(); + return this._dataView = null; + }; + + ExifReader.prototype._checkImageHeader = function() { + if (this._dataView.byteLength < this._MIN_DATA_BUFFER_LENGTH || this._dataView.getUint16(0, false) !== this._JPEG_ID) { + throw new Error('Invalid image format'); + } + this._parseAppMarkers(this._dataView); + if (!this._hasExifData()) { + throw new Error('No Exif data'); + } + }; + + ExifReader.prototype._parseAppMarkers = function(dataView) { + var appMarkerPosition, fieldLength, _results; + + appMarkerPosition = this._JPEG_ID_SIZE; + _results = []; + while (true) { + if (dataView.byteLength < appMarkerPosition + this._APP_ID_OFFSET + 5) { + break; + } + if (this._isApp1ExifMarker(dataView, appMarkerPosition)) { + fieldLength = dataView.getUint16(appMarkerPosition + this._APP_MARKER_SIZE, false); + this._tiffHeaderOffset = appMarkerPosition + this._TIFF_HEADER_OFFSET; + } else if (this._isAppMarker(dataView, appMarkerPosition)) { + fieldLength = dataView.getUint16(appMarkerPosition + this._APP_MARKER_SIZE, false); + } else { + break; + } + _results.push(appMarkerPosition += this._APP_MARKER_SIZE + fieldLength); + } + return _results; + }; + + ExifReader.prototype._isApp1ExifMarker = function(dataView, appMarkerPosition) { + return dataView.getUint16(appMarkerPosition, false) === this._APP1_MARKER && dataView.getUint32(appMarkerPosition + this._APP_ID_OFFSET, false) === this._BYTES_Exif && dataView.getUint8(appMarkerPosition + this._APP_ID_OFFSET + 4, false) === 0x00; + }; + + ExifReader.prototype._isAppMarker = function(dataView, appMarkerPosition) { + var appMarker; + + appMarker = dataView.getUint16(appMarkerPosition, false); + return appMarker >= this._APP0_MARKER && appMarker <= this._APP15_MARKER; + }; + + ExifReader.prototype._hasExifData = function() { + return this._tiffHeaderOffset !== 0; + }; + + ExifReader.prototype._readTags = function() { + this._setByteOrder(); + this._read0thIfd(); + this._readExifIfd(); + this._readGpsIfd(); + return this._readInteroperabilityIfd(); + }; + + ExifReader.prototype._setByteOrder = function() { + if (this._dataView.getUint16(this._tiffHeaderOffset) === this._BYTE_ORDER_BIG_ENDIAN) { + return this._littleEndian = true; + } else if (this._dataView.getUint16(this._tiffHeaderOffset) === this._BYTE_ORDER_LITTLE_ENDIAN) { + return this._littleEndian = false; + } else { + throw new Error('Illegal byte order value. Faulty image.'); + } + }; + + ExifReader.prototype._read0thIfd = function() { + var ifdOffset; + + ifdOffset = this._getIfdOffset(); + return this._readIfd('0th', ifdOffset); + }; + + ExifReader.prototype._getIfdOffset = function() { + return this._tiffHeaderOffset + this._getLongAt(this._tiffHeaderOffset + 4); + }; + + ExifReader.prototype._readExifIfd = function() { + var ifdOffset; + + if (this._tags['Exif IFD Pointer'] != null) { + ifdOffset = this._tiffHeaderOffset + this._tags['Exif IFD Pointer'].value; + return this._readIfd('exif', ifdOffset); + } + }; + + ExifReader.prototype._readGpsIfd = function() { + var ifdOffset; + + if (this._tags['GPS Info IFD Pointer'] != null) { + ifdOffset = this._tiffHeaderOffset + this._tags['GPS Info IFD Pointer'].value; + return this._readIfd('gps', ifdOffset); + } + }; + + ExifReader.prototype._readInteroperabilityIfd = function() { + var ifdOffset; + + if (this._tags['Interoperability IFD Pointer'] != null) { + ifdOffset = this._tiffHeaderOffset + this._tags['Interoperability IFD Pointer'].value; + return this._readIfd('interoperability', ifdOffset); + } + }; + + ExifReader.prototype._readIfd = function(ifdType, offset) { + var fieldIndex, numberOfFields, tag, _i, _results; + + numberOfFields = this._getShortAt(offset); + offset += 2; + _results = []; + for (fieldIndex = _i = 0; 0 <= numberOfFields ? _i < numberOfFields : _i > numberOfFields; fieldIndex = 0 <= numberOfFields ? ++_i : --_i) { + tag = this._readTag(ifdType, offset); + if (tag !== void 0) { + this._tags[tag.name] = { + 'value': tag.value, + 'description': tag.description + }; + } + _results.push(offset += 12); + } + return _results; + }; + + ExifReader.prototype._readTag = function(ifdType, offset) { + var tagCode, tagCount, tagDescription, tagName, tagType, tagValue, tagValueOffset; + + tagCode = this._getShortAt(offset); + tagType = this._getShortAt(offset + 2); + tagCount = this._getLongAt(offset + 4); + if (this._typeSizes[tagType] === void 0) { + return void 0; + } + if (this._typeSizes[tagType] * tagCount <= 4) { + tagValue = this._getTagValue(offset + 8, tagType, tagCount); + } else { + tagValueOffset = this._getLongAt(offset + 8); + tagValue = this._getTagValue(this._tiffHeaderOffset + tagValueOffset, tagType, tagCount); + } + if (tagType === this._tagTypes['ASCII']) { + tagValue = this._splitNullSeparatedAsciiString(tagValue); + } + if (this._tagNames[ifdType][tagCode] != null) { + if ((this._tagNames[ifdType][tagCode]['name'] != null) && (this._tagNames[ifdType][tagCode]['description'] != null)) { + tagName = this._tagNames[ifdType][tagCode]['name']; + tagDescription = this._tagNames[ifdType][tagCode]['description'](tagValue); + } else { + tagName = this._tagNames[ifdType][tagCode]; + if (tagValue instanceof Array) { + tagDescription = tagValue.join(', '); + } else { + tagDescription = tagValue; + } + } + return { + 'name': tagName, + 'value': tagValue, + 'description': tagDescription + }; + } else { + return { + 'name': "undefined-" + tagCode, + 'value': tagValue, + 'description': tagValue + }; + } + }; + + ExifReader.prototype._getTagValue = function(offset, type, count) { + var tagValue, value, valueIndex; + + value = (function() { + var _i, _results; + + _results = []; + for (valueIndex = _i = 0; 0 <= count ? _i < count : _i > count; valueIndex = 0 <= count ? ++_i : --_i) { + tagValue = this._getTagValueAt[type](offset); + offset += this._typeSizes[type]; + _results.push(tagValue); + } + return _results; + }).call(this); + if (value.length === 1) { + value = value[0]; + } else if (type === this._tagTypes['ASCII']) { + value = this._getAsciiValue(value); + } + return value; + }; + + ExifReader.prototype._getAsciiValue = function(charArray) { + var charCode, newCharArray; + + return newCharArray = (function() { + var _i, _len, _results; + + _results = []; + for (_i = 0, _len = charArray.length; _i < _len; _i++) { + charCode = charArray[_i]; + _results.push(String.fromCharCode(charCode)); + } + return _results; + })(); + }; + + ExifReader.prototype._getByteAt = function(offset) { + return this._dataView.getUint8(offset); + }; + + ExifReader.prototype._getAsciiAt = function(offset) { + return this._dataView.getUint8(offset); + }; + + ExifReader.prototype._getShortAt = function(offset) { + return this._dataView.getUint16(offset, this._littleEndian); + }; + + ExifReader.prototype._getLongAt = function(offset) { + return this._dataView.getUint32(offset, this._littleEndian); + }; + + ExifReader.prototype._getRationalAt = function(offset) { + return this._getLongAt(offset) / this._getLongAt(offset + 4); + }; + + ExifReader.prototype._getUndefinedAt = function(offset) { + return this._getByteAt(offset); + }; + + ExifReader.prototype._getSlongAt = function(offset) { + return this._dataView.getInt32(offset, this._littleEndian); + }; + + ExifReader.prototype._getSrationalAt = function(offset) { + return this._getSlongAt(offset) / this._getSlongAt(offset + 4); + }; + + ExifReader.prototype._splitNullSeparatedAsciiString = function(string) { + var character, i, tagValue, _i, _len; + + tagValue = []; + i = 0; + for (_i = 0, _len = string.length; _i < _len; _i++) { + character = string[_i]; + if (character === '\x00') { + i++; + continue; + } + if (tagValue[i] == null) { + tagValue[i] = ''; + } + tagValue[i] += character; + } + return tagValue; + }; + + ExifReader.prototype._typeSizes = { + 1: 1, + 2: 1, + 3: 2, + 4: 4, + 5: 8, + 7: 1, + 9: 4, + 10: 8 + }; + + ExifReader.prototype._tagTypes = { + 'BYTE': 1, + 'ASCII': 2, + 'SHORT': 3, + 'LONG': 4, + 'RATIONAL': 5, + 'UNDEFINED': 7, + 'SLONG': 9, + 'SRATIONAL': 10 + }; + + ExifReader.prototype._tagNames = { + '0th': { + 0x0100: 'ImageWidth', + 0x0101: 'ImageLength', + 0x0102: 'BitsPerSample', + 0x0103: 'Compression', + 0x0106: 'PhotometricInterpretation', + 0x010e: 'ImageDescription', + 0x010f: 'Make', + 0x0110: 'Model', + 0x0111: 'StripOffsets', + 0x0112: { + 'name': 'Orientation', + 'description': function(value) { + switch (value) { + case 1: + return 'top-left'; + case 2: + return 'top-right'; + case 3: + return 'bottom-right'; + case 4: + return 'bottom-left'; + case 5: + return 'left-top'; + case 6: + return 'right-top'; + case 7: + return 'right-bottom'; + case 8: + return 'left-bottom'; + default: + return 'Undefined'; + } + } + }, + 0x0115: 'SamplesPerPixel', + 0x0116: 'RowsPerStrip', + 0x0117: 'StripByteCounts', + 0x011a: 'XResolution', + 0x011b: 'YResolution', + 0x011c: 'PlanarConfiguration', + 0x0128: { + 'name': 'ResolutionUnit', + 'description': function(value) { + switch (value) { + case 2: + return 'inches'; + case 3: + return 'centimeters'; + default: + return 'Unknown'; + } + } + }, + 0x012d: 'TransferFunction', + 0x0131: 'Software', + 0x0132: 'DateTime', + 0x013b: 'Artist', + 0x013e: 'WhitePoint', + 0x013f: 'PrimaryChromaticities', + 0x0201: 'JPEGInterchangeFormat', + 0x0202: 'JPEGInterchangeFormatLength', + 0x0211: 'YCbCrCoefficients', + 0x0212: 'YCbCrSubSampling', + 0x0213: { + 'name': 'YCbCrPositioning', + 'description': function(value) { + switch (value) { + case 1: + return 'centered'; + case 2: + return 'co-sited'; + default: + return 'undefied ' + value; + } + } + }, + 0x0214: 'ReferenceBlackWhite', + 0x8298: { + 'name': 'Copyright', + 'description': function(value) { + return value.join('; '); + } + }, + 0x8769: 'Exif IFD Pointer', + 0x8825: 'GPS Info IFD Pointer' + }, + 'exif': { + 0x829a: 'ExposureTime', + 0x829d: 'FNumber', + 0x8822: { + 'name': 'ExposureProgram', + 'description': function(value) { + switch (value) { + case 0: + return 'Undefined'; + case 1: + return 'Manual'; + case 2: + return 'Normal program'; + case 3: + return 'Aperture priority'; + case 4: + return 'Shutter priority'; + case 5: + return 'Creative program'; + case 6: + return 'Action program'; + case 7: + return 'Portrait mode'; + case 8: + return 'Landscape mode'; + default: + return 'Unknown'; + } + } + }, + 0x8824: 'SpectralSensitivity', + 0x8827: 'ISOSpeedRatings', + 0x8828: { + 'name': 'OECF', + 'description': function(value) { + return '[Raw OECF table data]'; + } + }, + 0x9000: { + 'name': 'ExifVersion', + 'description': function(value) { + var charCode, string, _i, _len; + + string = ''; + for (_i = 0, _len = value.length; _i < _len; _i++) { + charCode = value[_i]; + string += String.fromCharCode(charCode); + } + return string; + } + }, + 0x9003: 'DateTimeOriginal', + 0x9004: 'DateTimeDigitized', + 0x9101: { + 'name': 'ComponentsConfiguration', + 'description': function(value) { + var character, string, _i, _len; + + string = ''; + for (_i = 0, _len = value.length; _i < _len; _i++) { + character = value[_i]; + switch (character) { + case 0x31: + string += 'Y'; + break; + case 0x32: + string += 'Cb'; + break; + case 0x33: + string += 'Cr'; + break; + case 0x34: + string += 'R'; + break; + case 0x35: + string += 'G'; + break; + case 0x36: + string += 'B'; + } + } + return string; + } + }, + 0x9102: 'CompressedBitsPerPixel', + 0x9201: 'ShutterSpeedValue', + 0x9202: 'ApertureValue', + 0x9203: 'BrightnessValue', + 0x9204: 'ExposureBiasValue', + 0x9205: 'MaxApertureValue', + 0x9206: 'SubjectDistance', + 0x9207: { + 'name': 'MeteringMode', + 'description': function(value) { + switch (value) { + case 1: + return 'Average'; + case 2: + return 'CenterWeightedAverage'; + case 3: + return 'Spot'; + case 4: + return 'MultiSpot'; + case 5: + return 'Pattern'; + case 6: + return 'Partial'; + case 255: + return 'Other'; + default: + return 'Unknown'; + } + } + }, + 0x9208: { + 'name': 'LightSource', + 'description': function(value) { + switch (value) { + case 1: + return 'Daylight'; + case 2: + return 'Fluorescent'; + case 3: + return 'Tungsten (incandescent light)'; + case 4: + return 'Flash'; + case 9: + return 'Fine weather'; + case 10: + return 'Cloudy weather'; + case 11: + return 'Shade'; + case 12: + return 'Daylight fluorescent (D 5700 – 7100K)'; + case 13: + return 'Day white fluorescent (N 4600 – 5400K)'; + case 14: + return 'Cool white fluorescent (W 3900 – 4500K)'; + case 15: + return 'White fluorescent (WW 3200 – 3700K)'; + case 17: + return 'Standard light A'; + case 18: + return 'Standard light B'; + case 19: + return 'Standard light C'; + case 20: + return 'D55'; + case 21: + return 'D65'; + case 22: + return 'D75'; + case 23: + return 'D50'; + case 24: + return 'ISO studio tungsten'; + case 255: + return 'Other light source'; + default: + return 'Unknown'; + } + } + }, + 0x9209: { + 'name': 'Flash', + 'description': function(value) { + switch (value) { + case 0x00: + return 'Flash did not fire'; + case 0x01: + return 'Flash fired'; + case 0x05: + return 'Strobe return light not detected'; + case 0x07: + return 'Strobe return light detected'; + case 0x09: + return 'Flash fired, compulsory flash mode'; + case 0x0d: + return 'Flash fired, compulsory flash mode, return light not detected'; + case 0x0f: + return 'Flash fired, compulsory flash mode, return light detected'; + case 0x10: + return 'Flash did not fire, compulsory flash mode'; + case 0x18: + return 'Flash did not fire, auto mode'; + case 0x19: + return 'Flash fired, auto mode'; + case 0x1d: + return 'Flash fired, auto mode, return light not detected'; + case 0x1f: + return 'Flash fired, auto mode, return light detected'; + case 0x20: + return 'No flash function'; + case 0x41: + return 'Flash fired, red-eye reduction mode'; + case 0x45: + return 'Flash fired, red-eye reduction mode, return light not detected'; + case 0x47: + return 'Flash fired, red-eye reduction mode, return light detected'; + case 0x49: + return 'Flash fired, compulsory flash mode, red-eye reduction mode'; + case 0x4d: + return 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected'; + case 0x4f: + return 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected'; + case 0x59: + return 'Flash fired, auto mode, red-eye reduction mode'; + case 0x5d: + return 'Flash fired, auto mode, return light not detected, red-eye reduction mode'; + case 0x5f: + return 'Flash fired, auto mode, return light detected, red-eye reduction mode'; + default: + return 'Unknown'; + } + } + }, + 0x920a: 'FocalLength', + 0x9214: { + 'name': 'SubjectArea', + 'description': function(value) { + switch (value.length) { + case 2: + return "Location; X: " + value[0] + ", Y: " + value[1]; + case 3: + return "Circle; X: " + value[0] + ", Y: " + value[1] + ", diameter: " + value[2]; + case 4: + return "Rectangle; X: " + value[0] + ", Y: " + value[1] + ", width: " + value[2] + ", height: " + value[3]; + default: + return 'Unknown'; + } + } + }, + 0x927c: { + 'name': 'MakerNote', + 'description': function(value) { + return '[Raw maker note data]'; + } + }, + 0x9286: { + 'name': 'UserComment', + 'description': function(value) { + switch (value.slice(0, 8).map(function(charCode) { + return String.fromCharCode(charCode); + }).join('')) { + case 'ASCII\x00\x00\x00': + return value.slice(8, value.length).map(function(charCode) { + return String.fromCharCode(charCode); + }).join(''); + case 'JIS\x00\x00\x00\x00\x00': + return '[JIS encoded text]'; + case 'UNICODE\x00': + return '[Unicode encoded text]'; + case '\x00\x00\x00\x00\x00\x00\x00\x00': + return '[Undefined encoding]'; + } + } + }, + 0x9290: 'SubSecTime', + 0x9291: 'SubSecTimeOriginal', + 0x9292: 'SubSecTimeDigitized', + 0xa000: { + 'name': 'FlashpixVersion', + 'description': function(value) { + var charCode, string, _i, _len; + + string = ''; + for (_i = 0, _len = value.length; _i < _len; _i++) { + charCode = value[_i]; + string += String.fromCharCode(charCode); + } + return string; + } + }, + 0xa001: { + 'name': 'ColorSpace', + 'description': function(value) { + switch (value) { + case 1: + return 'sRGB'; + case 0xffff: + return 'Uncalibrated'; + default: + return 'Unknown'; + } + } + }, + 0xa002: 'PixelXDimension', + 0xa003: 'PixelYDimension', + 0xa004: 'RelatedSoundFile', + 0xa005: 'Interoperability IFD Pointer', + 0xa20b: 'FlashEnergy', + 0xa20c: { + 'name': 'SpatialFrequencyResponse', + 'description': function(value) { + return '[Raw SFR table data]'; + } + }, + 0xa20e: 'FocalPlaneXResolution', + 0xa20f: 'FocalPlaneYResolution', + 0xa210: { + 'name': 'FocalPlaneResolutionUnit', + 'description': function(value) { + switch (value) { + case 2: + return 'inches'; + case 3: + return 'centimeters'; + default: + return 'Unknown'; + } + } + }, + 0xa214: { + 'name': 'SubjectLocation', + 'description': function(value) { + return "X: " + value[0] + ", Y: " + value[1]; + } + }, + 0xa215: 'ExposureIndex', + 0xa217: { + 'name': 'SensingMethod', + 'description': function(value) { + switch (value) { + case 1: + return 'Undefined'; + case 2: + return 'One-chip color area sensor'; + case 3: + return 'Two-chip color area sensor'; + case 4: + return 'Three-chip color area sensor'; + case 5: + return 'Color sequential area sensor'; + case 7: + return 'Trilinear sensor'; + case 8: + return 'Color sequential linear sensor'; + default: + return 'Unknown'; + } + } + }, + 0xa300: { + 'name': 'FileSource', + 'description': function(value) { + switch (value) { + case 3: + return 'DSC'; + default: + return 'Unknown'; + } + } + }, + 0xa301: { + 'name': 'SceneType', + 'description': function(value) { + switch (value) { + case 1: + return 'A directly photographed image'; + default: + return 'Unknown'; + } + } + }, + 0xa302: { + 'name': 'CFAPattern', + 'description': function(value) { + return '[Raw CFA pattern table data]'; + } + }, + 0xa401: { + 'name': 'CustomRendered', + 'description': function(value) { + switch (value) { + case 0: + return 'Normal process'; + case 1: + return 'Custom process'; + default: + return 'Unknown'; + } + } + }, + 0xa402: { + 'name': 'ExposureMode', + 'description': function(value) { + switch (value) { + case 0: + return 'Auto exposure'; + case 1: + return 'Manual exposure'; + case 2: + return 'Auto bracket'; + default: + return 'Unknown'; + } + } + }, + 0xa403: { + 'name': 'WhiteBalance', + 'description': function(value) { + switch (value) { + case 0: + return 'Auto white balance'; + case 1: + return 'Manual white balance'; + default: + return 'Unknown'; + } + } + }, + 0xa404: { + 'name': 'DigitalZoomRatio', + 'description': function(value) { + switch (value) { + case 0: + return 'Digital zoom was not used'; + default: + return value; + } + } + }, + 0xa405: { + 'name': 'FocalLengthIn35mmFilm', + 'description': function(value) { + switch (value) { + case 0: + return 'Unknown'; + default: + return value; + } + } + }, + 0xa406: { + 'name': 'SceneCaptureType', + 'description': function(value) { + switch (value) { + case 0: + return 'Standard'; + case 1: + return 'Landscape'; + case 2: + return 'Portrait'; + case 3: + return 'Night scene'; + default: + return 'Unknown'; + } + } + }, + 0xa407: { + 'name': 'GainControl', + 'description': function(value) { + switch (value) { + case 0: + return 'None'; + case 1: + return 'Low gain up'; + case 2: + return 'High gain up'; + case 3: + return 'Low gain down'; + case 4: + return 'High gain down'; + default: + return 'Unknown'; + } + } + }, + 0xa408: { + 'name': 'Contrast', + 'description': function(value) { + switch (value) { + case 0: + return 'Normal'; + case 1: + return 'Soft'; + case 2: + return 'Hard'; + default: + return 'Unknown'; + } + } + }, + 0xa409: { + 'name': 'Saturation', + 'description': function(value) { + switch (value) { + case 0: + return 'Normal'; + case 1: + return 'Low saturation'; + case 2: + return 'High saturation'; + default: + return 'Unknown'; + } + } + }, + 0xa40a: { + 'name': 'Sharpness', + 'description': function(value) { + switch (value) { + case 0: + return 'Normal'; + case 1: + return 'Soft'; + case 2: + return 'Hard'; + default: + return 'Unknown'; + } + } + }, + 0xa40b: { + 'name': 'DeviceSettingDescription', + 'description': function(value) { + return '[Raw device settings table data]'; + } + }, + 0xa40c: { + 'name': 'SubjectDistanceRange', + 'description': function(value) { + switch (value) { + case 1: + return 'Macro'; + case 2: + return 'Close view'; + case 3: + return 'Distant view'; + default: + return 'Unknown'; + } + } + }, + 0xa420: 'ImageUniqueID' + }, + 'gps': { + 0x0000: { + 'name': 'GPSVersionID', + 'description': function(value) { + var _ref, _ref1; + + if ((value[0] === (_ref = value[1]) && _ref === 2) && (value[2] === (_ref1 = value[3]) && _ref1 === 0)) { + return 'Version 2.2'; + } else { + return 'Unknown'; + } + } + }, + 0x0001: { + 'name': 'GPSLatitudeRef', + 'description': function(value) { + switch (value.join('')) { + case 'N': + return 'North latitude'; + case 'S': + return 'South latitude'; + default: + return 'Unknown'; + } + } + }, + 0x0002: { + 'name': 'GPSLatitude', + 'description': function(value) { + return value[0] + value[1] / 60 + value[2] / 3600; + } + }, + 0x0003: { + 'name': 'GPSLongitudeRef', + 'description': function(value) { + switch (value.join('')) { + case 'E': + return 'East longitude'; + case 'W': + return 'West longitude'; + default: + return 'Unknown'; + } + } + }, + 0x0004: { + 'name': 'GPSLongitude', + 'description': function(value) { + return value[0] + value[1] / 60 + value[2] / 3600; + } + }, + 0x0005: { + 'name': 'GPSAltitudeRef', + 'description': function(value) { + switch (value) { + case 0: + return 'Sea level'; + case 1: + return 'Sea level reference (negative value)'; + default: + return 'Unknown'; + } + } + }, + 0x0006: { + 'name': 'GPSAltitude', + 'description': function(value) { + return value + ' m'; + } + }, + 0x0007: { + 'name': 'GPSTimeStamp', + 'description': function(value) { + var padZero; + + padZero = function(num) { + var i; + + return ((function() { + var _i, _ref, _results; + + _results = []; + for (i = _i = 0, _ref = 2 - ('' + Math.floor(num)).length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + _results.push('0'); + } + return _results; + })()) + num; + }; + return value.map(padZero).join(':'); + } + }, + 0x0008: 'GPSSatellites', + 0x0009: { + 'name': 'GPSStatus', + 'description': function(value) { + switch (value.join('')) { + case 'A': + return 'Measurement in progress'; + case 'V': + return 'Measurement Interoperability'; + default: + return 'Unknown'; + } + } + }, + 0x000a: { + 'name': 'GPSMeasureMode', + 'description': function(value) { + switch (value.join('')) { + case '2': + return '2-dimensional measurement'; + case '3': + return '3-dimensional measurement'; + default: + return 'Unknown'; + } + } + }, + 0x000b: 'GPSDOP', + 0x000c: { + 'name': 'GPSSpeedRef', + 'description': function(value) { + switch (value.join('')) { + case 'K': + return 'Kilometers per hour'; + case 'M': + return 'Miles per hour'; + case 'N': + return 'Knots'; + default: + return 'Unknown'; + } + } + }, + 0x000d: 'GPSSpeed', + 0x000e: { + 'name': 'GPSTrackRef', + 'description': function(value) { + switch (value.join('')) { + case 'T': + return 'True direction'; + case 'M': + return 'Magnetic direction'; + default: + return 'Unknown'; + } + } + }, + 0x000f: 'GPSTrack', + 0x0010: { + 'name': 'GPSImgDirectionRef', + 'description': function(value) { + switch (value.join('')) { + case 'T': + return 'True direction'; + case 'M': + return 'Magnetic direction'; + default: + return 'Unknown'; + } + } + }, + 0x0011: 'GPSImgDirection', + 0x0012: 'GPSMapDatum', + 0x0013: { + 'name': 'GPSDestLatitudeRef', + 'description': function(value) { + switch (value.join('')) { + case 'N': + return 'North latitude'; + case 'S': + return 'South latitude'; + default: + return 'Unknown'; + } + } + }, + 0x0014: { + 'name': 'GPSDestLatitude', + 'description': function(value) { + return value[0] + value[1] / 60 + value[2] / 3600; + } + }, + 0x0015: { + 'name': 'GPSDestLongitudeRef', + 'description': function(value) { + switch (value.join('')) { + case 'E': + return 'East longitude'; + case 'W': + return 'West longitude'; + default: + return 'Unknown'; + } + } + }, + 0x0016: { + 'name': 'GPSDestLongitude', + 'description': function(value) { + return value[0] + value[1] / 60 + value[2] / 3600; + } + }, + 0x0017: { + 'name': 'GPSDestBearingRef', + 'description': function(value) { + switch (value.join('')) { + case 'T': + return 'True direction'; + case 'M': + return 'Magnetic direction'; + default: + return 'Unknown'; + } + } + }, + 0x0018: 'GPSDestBearing', + 0x0019: { + 'name': 'GPSDestDistanceRef', + 'description': function(value) { + switch (value.join('')) { + case 'K': + return 'Kilometers'; + case 'M': + return 'Miles'; + case 'N': + return 'Knots'; + default: + return 'Unknown'; + } + } + }, + 0x001a: 'GPSDestDistance', + 0x001b: { + 'name': 'GPSProcessingMethod', + 'description': function(value) { + if (value === 0) { + return 'Undefined'; + } else { + switch (value.slice(0, 8).map(function(charCode) { + return String.fromCharCode(charCode); + }).join('')) { + case 'ASCII\x00\x00\x00': + return value.slice(8, value.length).map(function(charCode) { + return String.fromCharCode(charCode); + }).join(''); + case 'JIS\x00\x00\x00\x00\x00': + return '[JIS encoded text]'; + case 'UNICODE\x00': + return '[Unicode encoded text]'; + case '\x00\x00\x00\x00\x00\x00\x00\x00': + return '[Undefined encoding]'; + } + } + } + }, + 0x001c: { + 'name': 'GPSAreaInformation', + 'description': function(value) { + if (value === 0) { + return 'Undefined'; + } else { + switch (value.slice(0, 8).map(function(charCode) { + return String.fromCharCode(charCode); + }).join('')) { + case 'ASCII\x00\x00\x00': + return value.slice(8, value.length).map(function(charCode) { + return String.fromCharCode(charCode); + }).join(''); + case 'JIS\x00\x00\x00\x00\x00': + return '[JIS encoded text]'; + case 'UNICODE\x00': + return '[Unicode encoded text]'; + case '\x00\x00\x00\x00\x00\x00\x00\x00': + return '[Undefined encoding]'; + } + } + } + }, + 0x001d: 'GPSDateStamp', + 0x001e: { + 'name': 'GPSDifferential', + 'description': function(value) { + switch (value) { + case 0: + return 'Measurement without differential correction'; + case 1: + return 'Differential correction applied'; + default: + return 'Unknown'; + } + } + } + }, + 'interoperability': { + 0x0001: 'InteroperabilityIndex', + 0x0002: 'UnknownInteroperabilityTag0x0002', + 0x1001: 'UnknownInteroperabilityTag0x1001', + 0x1002: 'UnknownInteroperabilityTag0x1002' + } + }; + + /* + # Gets the image's value of the tag with the given name. + # + # name string The name of the tag to get the value of + # + # Returns the value of the tag with the given name if it exists, + # otherwise throws "Undefined". + */ + + + ExifReader.prototype.getTagValue = function(name) { + if (this._tags[name] != null) { + return this._tags[name].value; + } else { + return void 0; + } + }; + + /* + # Gets the image's description of the tag with the given name. + # + # name string The name of the tag to get the description of + # + # Returns the description of the tag with the given name if it exists, + # otherwise throws "Undefined". + */ + + + ExifReader.prototype.getTagDescription = function(name) { + if (this._tags[name] != null) { + return this._tags[name].description; + } else { + return void 0; + } + }; + + /* + # Gets all the image's tags. + # + # Returns the image's tags as an associative array: name -> description. + */ + + + ExifReader.prototype.getAllTags = function() { + return this._tags; + }; + + /* + # Delete a tag. + # + # name string The name of the tag to delete + # + # Delete the tag with the given name. Can be used to lower memory usage. + # E.g., the MakerNote tag can be really large. + */ + + + ExifReader.prototype.deleteTag = function(name) { + return delete this._tags[name]; + }; + + return ExifReader; + + })(); + +}).call(this); diff --git a/old/server/app/static/js/vendor/canvas-to-blob.js b/old/server/app/static/js/vendor/canvas-to-blob.js new file mode 100644 index 00000000..32913667 --- /dev/null +++ b/old/server/app/static/js/vendor/canvas-to-blob.js @@ -0,0 +1,111 @@ +/* + * JavaScript Canvas to Blob + * https://github.com/blueimp/JavaScript-Canvas-to-Blob + * + * Copyright 2012, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + * + * Based on stackoverflow user Stoive's code snippet: + * http://stackoverflow.com/q/4998908 + */ + +/* global atob, Blob, define */ + +;(function (window) { + 'use strict' + + var CanvasPrototype = window.HTMLCanvasElement && + window.HTMLCanvasElement.prototype + var hasBlobConstructor = window.Blob && (function () { + try { + return Boolean(new Blob()) + } catch (e) { + return false + } + }()) + var hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array && + (function () { + try { + return new Blob([new Uint8Array(100)]).size === 100 + } catch (e) { + return false + } + }()) + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || + window.MozBlobBuilder || window.MSBlobBuilder + var dataURIPattern = /^data:((.*?)(;charset=.*?)?)(;base64)?,/ + var dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob && + window.ArrayBuffer && window.Uint8Array && + function (dataURI) { + var matches, + mediaType, + isBase64, + dataString, + byteString, + arrayBuffer, + intArray, + i, + bb + // Parse the dataURI components as per RFC 2397 + matches = dataURI.match(dataURIPattern) + if (!matches) { + throw new Error('invalid data URI') + } + // Default to text/plain;charset=US-ASCII + mediaType = matches[2] + ? matches[1] + : 'text/plain' + (matches[3] || ';charset=US-ASCII') + isBase64 = !!matches[4] + dataString = dataURI.slice(matches[0].length) + if (isBase64) { + // Convert base64 to raw binary data held in a string: + byteString = atob(dataString) + } else { + // Convert base64/URLEncoded data component to raw binary: + byteString = decodeURIComponent(dataString) + } + // Write the bytes of the string to an ArrayBuffer: + arrayBuffer = new ArrayBuffer(byteString.length) + intArray = new Uint8Array(arrayBuffer) + for (i = 0; i < byteString.length; i += 1) { + intArray[i] = byteString.charCodeAt(i) + } + // Write the ArrayBuffer (or ArrayBufferView) to a blob: + if (hasBlobConstructor) { + return new Blob( + [hasArrayBufferViewSupport ? intArray : arrayBuffer], + {type: mediaType} + ) + } + bb = new BlobBuilder() + bb.append(arrayBuffer) + return bb.getBlob(mediaType) + } + if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) { + if (CanvasPrototype.mozGetAsFile) { + CanvasPrototype.toBlob = function (callback, type, quality) { + if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) { + callback(dataURLtoBlob(this.toDataURL(type, quality))) + } else { + callback(this.mozGetAsFile('blob', type)) + } + } + } else if (CanvasPrototype.toDataURL && dataURLtoBlob) { + CanvasPrototype.toBlob = function (callback, type, quality) { + callback(dataURLtoBlob(this.toDataURL(type, quality))) + } + } + } + if (typeof define === 'function' && define.amd) { + define(function () { + return dataURLtoBlob + }) + } else if (typeof module === 'object' && module.exports) { + module.exports = dataURLtoBlob + } else { + window.dataURLtoBlob = dataURLtoBlob + } +}(window)) diff --git a/old/server/app/static/js/vendor/jquery-3.3.1.min.js b/old/server/app/static/js/vendor/jquery-3.3.1.min.js new file mode 100644 index 00000000..4d9b3a25 --- /dev/null +++ b/old/server/app/static/js/vendor/jquery-3.3.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + + + +{% endblock scripts %} \ No newline at end of file diff --git a/old/server/app/templates/display.html b/old/server/app/templates/display.html new file mode 100644 index 00000000..f73a6ca5 --- /dev/null +++ b/old/server/app/templates/display.html @@ -0,0 +1,69 @@ +{%- extends "base.html" %} + +{% import "bootstrap/utils.html" as utils %} + +{% block content %} + + +
    + +
    +

    +

    Result

    +
    + +
    +
    +
    + +

    Rendered result

    +
    +
    + +
    + +

    Original image

    +
    +
    + +
    + +

    Semantic segmentation

    +
    + + + + +
    +
    +
    + Home +
    +
    +
    + + + + +{% block footer %} +{{super()}} +{% endblock footer %} + +{% endblock %} + diff --git a/old/server/app/templates/index.html b/old/server/app/templates/index.html new file mode 100644 index 00000000..f740bb5b --- /dev/null +++ b/old/server/app/templates/index.html @@ -0,0 +1,161 @@ + + + + + + + DullDream (v2 x ZkM) + + + + +
    +

    DullDream

    +

    Neural network photo effect

    +
    + +
    +
    +
    + + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    + Change Image + +
    + +
    + Upload +
    +
    + +
    + +
    +
    + About + Privacy +

    + All images uploaded can be used for exhibition and review purposes. +

    +

    + Currently this work is on view at ZKM. View recent DullDreams here. +

    +
    +
    +
    + + + + + + +
    +
    +
    + +
    + Made with DullDream.xyz for ZKM OpenCodes 2017 +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + Home + About + Privacy +
    + +
    + +
    + + + + + + + + + + + + + + \ No newline at end of file diff --git a/old/server/celery_worker.py b/old/server/celery_worker.py new file mode 100644 index 00000000..1545a884 --- /dev/null +++ b/old/server/celery_worker.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +import os +from app.basemodels import celery +from app import create_app + +app = create_app(os.getenv('FLASK_CONFIG') or 'default') +app.app_context().push() diff --git a/old/server/config.py b/old/server/config.py new file mode 100644 index 00000000..5042efb6 --- /dev/null +++ b/old/server/config.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +"""Application configuration.""" +import os +from os.path import join +basedir = os.path.abspath(os.path.dirname(__file__)) + +class Config(object): + """Base configuration.""" + + #SECRET_KEY = os.environ.get('MYFLASKAPP_SECRET', 'secret-key') # TODO: Change me + APP_DIR = os.path.abspath(os.path.dirname(__file__)) # This directory + PROJECT_ROOT = os.path.abspath(os.path.join(APP_DIR, os.pardir)) + #BCRYPT_LOG_ROUNDS = 13 + DEBUG_TB_ENABLED = False # Disable Debug toolbar + #DEBUG_TB_INTERCEPT_REDIRECTS = False + CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc. + HOST = '0.0.0.0' + FLASK_DEBUG_DISABLE_STRICT = True + #WTF_CSRF_SECRET_KEY = '94ksadkf49DKEDFJ.&' + BOOTSTRAP_GOOGLE_ANALYTICS_ACCOUNT = None + BOOTSTRAP_SERVE_LOCAL = True + SECRET_KEY = os.environ.get('SECRET_KEY') or '94ksadkf49DKEDFJ.&' + CELERY_BROKER_URL = 'redis://localhost:6379/0' + CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' + + FLASKY_SLOW_DB_QUERY_TIME=0.5 + + @staticmethod + def init_app(app): + pass + + +class DevelopmentConfig(Config): + """Development configuration.""" + ENV = 'dev' + DEBUG = True + +class ProductionConfig(Config): + ENV = 'production' + DEBUG = False + # @classmethod + # def init_app(cls, app): + # Config.init_app(app) + # # import logging + # # app.logger.addHandler(mail_handler) + + +class DigitalOceanConfig(Config): + """Production configuration.""" + def init_app(cls, app): + ProductionConfig.init_app(app) + # log to syslog + import logging + from logging.handlers import SysLogHandler + syslog_handler = SysLogHandler() + syslog_handler.setLevel(logging.WARNING) + app.logger.addHandler(syslog_handler) + + +class UnixConfig(ProductionConfig): + @classmethod + def init_app(cls, app): + ProductionConfig.init_app(app) + + # log to syslog + import logging + from logging.handlers import SysLogHandler + syslog_handler = SysLogHandler() + syslog_handler.setLevel(logging.WARNING) + app.logger.addHandler(syslog_handler) + + +config = { + 'development': DevelopmentConfig, + 'production': ProductionConfig, + 'digitalocean': DigitalOceanConfig, + 'default': DevelopmentConfig +} diff --git a/old/server/deploy.sh b/old/server/deploy.sh new file mode 100755 index 00000000..c2594cab --- /dev/null +++ b/old/server/deploy.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +d_src="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/ +d_dst=/home/dull/dulldream/www/dulldream_xyz +ssh_alis=dulldream-root +#DOCKER_DIR_P1="$(dirname "$CWD")" + +echo "Syncing DullDream LOCAL to -> REMOTE" +echo $d_src +echo $d_dst + + +#rsync -a -e 'ssh' \ +rsync -r -v --progress -e 'ssh' \ + --delete \ + --exclude='.DS_Store' \ + --exclude='deploy.sh' \ + $d_src $ssh_alis:$d_dst + +echo "Synced :)" \ No newline at end of file diff --git a/old/server/dulldream.wsgi.py b/old/server/dulldream.wsgi.py new file mode 100644 index 00000000..ed992528 --- /dev/null +++ b/old/server/dulldream.wsgi.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +import sys +sys.path.insert(0, "/home/dulldream/dulldream/www/dulldream_xyz/") + +from app import create_app + +import logging +logging.basicConfig(stream=sys.stderr) +# logging.basicConfig(filename='error.log',level=logging.DEBUG) + +application = create_app('production') +application.secret_key = 'curlier6982!1decentralizationists' + diff --git a/old/server/run-celery.sh b/old/server/run-celery.sh new file mode 100755 index 00000000..e38174fa --- /dev/null +++ b/old/server/run-celery.sh @@ -0,0 +1,3 @@ +#!/bin/bash +celery worker -A celery_worker.celery --loglevel=info + diff --git a/old/server/run-dev.sh b/old/server/run-dev.sh new file mode 100755 index 00000000..b4eb2a61 --- /dev/null +++ b/old/server/run-dev.sh @@ -0,0 +1 @@ +FLASK_CONFIG=development python run.py \ No newline at end of file diff --git a/old/server/run-gunicorn.sh b/old/server/run-gunicorn.sh new file mode 100755 index 00000000..64debabd --- /dev/null +++ b/old/server/run-gunicorn.sh @@ -0,0 +1,2 @@ +#!/bin/bash +gunicorn -w 1 -b 0.0.0.0:8000 run:app diff --git a/old/server/run-redis.sh b/old/server/run-redis.sh new file mode 100755 index 00000000..e9ceb845 --- /dev/null +++ b/old/server/run-redis.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/usr/local/bin/redis-server /etc/redis/redis.conf diff --git a/old/server/run.py b/old/server/run.py new file mode 100644 index 00000000..c4c3e8d7 --- /dev/null +++ b/old/server/run.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +import os +from flask import Flask +from app import create_app + +app = create_app(os.getenv('FLASK_CONFIG') or 'default') +import logging +logging.basicConfig(filename='error.log',level=logging.DEBUG) + +if __name__ == '__main__': + app.run(host='0.0.0.0',debug=False,threaded=False,port=8000) + pass diff --git a/scraper/client/actions.js b/scraper/client/actions.js deleted file mode 100644 index ba899f06..00000000 --- a/scraper/client/actions.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as search from './search/search.actions' -import * as review from './review/review.actions' -import * as metadata from './metadata/metadata.actions' - -export { - search, - review, - metadata, -} diff --git a/scraper/client/app.js b/scraper/client/app.js deleted file mode 100644 index 6c008ec6..00000000 --- a/scraper/client/app.js +++ /dev/null @@ -1,46 +0,0 @@ -import React, { Component } from 'react' -import { ConnectedRouter } from 'connected-react-router' -import { Route, Switch } from 'react-router' - -import { Header, Sidebar, Footer } from './common' -import * as Metadata from './metadata' -import * as Search from './search' -import * as Review from './review' - -export default class App extends Component { - render() { - return ( - -
    -
    -
    - -
    - - - - - - - - - - - - -

    NOT FOUND

    } /> - - - - - - -
    -
    -
    -
    -
    -
    - ) - } -} diff --git a/scraper/client/common/activeLink.component.js b/scraper/client/common/activeLink.component.js deleted file mode 100644 index 59f63881..00000000 --- a/scraper/client/common/activeLink.component.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import { NavLink } from 'react-router-dom' - -export default function ActiveLink({ - to, - className = 'navlink', - children -}) { - return ( - - - {children} - - - ) -} diff --git a/scraper/client/common/classifier.component.js b/scraper/client/common/classifier.component.js deleted file mode 100644 index af6a4934..00000000 --- a/scraper/client/common/classifier.component.js +++ /dev/null @@ -1,99 +0,0 @@ -import React, { Component } from 'react' -import { courtesyS } from '../util' - -import { TableTuples, DetectionList, Keyframe } from '.' - -export default class Classifier extends Component { - render() { - const { - tag, - sha256, - verified, - keyframes = {}, - labels, - summary, - aspectRatio = 1.777, - showAll, - } = this.props - let totalDetections = 0 - const keys = Object - .keys(keyframes) - .map(s => parseInt(s, 10)) - const validKeyframes = keys - .sort((a, b) => a - b) - .map(frame => { - const detections = keyframes[frame] - if (detections.length || showAll) { - totalDetections += detections.length - return { frame, detections } - } - return null - }) - .filter(f => !!f) - const detectionLookup = validKeyframes - .reduce((a, b) => { - b.detections.reduce((aa, { idx }) => { - if (!(idx in aa)) aa[idx] = [labels[idx], 0] - aa[idx][1] += 1 - return aa - }, a) - return a - }, {}) - const detectionCounts = Object.keys(detectionLookup) - .map(idx => detectionLookup[idx]) - .sort((a, b) => b[1] - a[1]) - - if (summary) { - return ( -
    -

    {tag}{' Detections'}

    - -
    - ) - } - return ( -
    -

    {tag}

    -

    Detections

    - -

    Frames

    -
      -
    • - {'Displaying '}{validKeyframes.length}{' / '}{courtesyS(keys.length, 'frame')} -
    • -
    • - {courtesyS(totalDetections, 'detection')}{' found'} -
    • -
    -
    - {validKeyframes.map(({ frame, detections }) => ( - - - - ))} -
    -
    - ) - } -} diff --git a/scraper/client/common/common.css b/scraper/client/common/common.css deleted file mode 100644 index 4b939df0..00000000 --- a/scraper/client/common/common.css +++ /dev/null @@ -1,347 +0,0 @@ -/* css boilerplate */ - -* { box-sizing: border-box; } -html,body { - margin: 0; padding: 0; - width: 100%; height: 100%; -} -body { - font-family: Helvetica, sans-serif; - font-weight: 300; -} - -h1 { - -} -h2 { - font-weight: normal; - margin: 10px 0; - padding: 3px; - font-size: 24px; -} -h3 { - font-weight: normal; - margin: 10px 0 0 0; - padding: 3px; - font-size: 18px; -} -h4 { - font-weight: 300; - font-size: 12px; - letter-spacing: 2px; - color: #888; - text-transform: uppercase; - margin: 5px 10px; - margin-top: 20px; -} -h4:first-child { - margin-top: 10px; -} - -.app { - width: 100%; - height: 100%; - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: flex-start; -} - -/* header stuff */ - -header { - width: 100%; - background: #11f; - color: white; - align-items: stretch; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - z-index: 3; -} -header > section { - justify-content: flex-start; - align-items: center; - display: flex; - flex: 1 0; - font-weight: bold; -} -header > section:last-of-type { - justify-content: flex-end; -} - -/* sidebar / body columns */ - -.sidebar { - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - height: 100%; - float: left; - width: 200px; - flex: 0 0 200px; - padding: 10px; - margin-right: 10px; -} -.sidebar a { - display: block; - padding: 10px 10px; - text-decoration: none; - color: #444; -} -.sidebar a.active { - font-weight: bold; - color: #222; -} -.body { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - flex-grow: 1; -} -.body > div { - padding-bottom: 40px; -} - -/* buttons / forms */ - -.btn:focus, .btn:hover { - background: #f1f1fc; - color: #4b48d6 !important; - text-decoration: none; -} -.btn { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: #fff; - border: .05rem solid; - border-radius: 2px; - margin-right: 5px; - color: #11f; - cursor: pointer; - display: inline-block; - font-size: .8rem; - height: 1.8rem; - line-height: 1rem; - outline: none; - padding: .35rem .4rem; - text-align: center; - text-decoration: none; - -webkit-transition: all .2s ease; - -o-transition: all .2s ease; - transition: all .2s ease; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - vertical-align: middle; - white-space: nowrap; -} -.btn.reset, -.btn.panic { - color: #b00; -} -.btn.btn-primary { - background: #11f; - border-color: #11f; - color: white; -} -.btn[disabled] { - color: #bbb !important; - border-color: #bbb !important; - background: white !important; - cursor: default; -} -.btn.btn-primary:focus, -.btn.btn-primary:hover { - background: #0808ee; - color: white !important; -} -.row .btn { - margin: 0 5px 0 0; -} -input[type=text] { - border: 1px solid #888; - padding: 4px; - font-size: 15px; -} - - -/* tables on metadata pages */ - -table { - border: 0; - margin: 0; - padding: 0; - border-spacing: 0; -} -.tableObject td, -.tableObject th { - padding: 3px; - vertical-align: top; -} -.tableObject hr { - width: 100%; - color: transparent; - border: 0; - border-bottom: 1px solid #bbb; - align: left; - margin: 3px 0; - padding: 0; -} -.tableObject th, -.tableTuples th { - min-width: 145px; - text-align: left; - text-transform: capitalize; - padding: 3px; - padding-right: 10px; - font-weight: 300; - color: #333; -} -.tableTuples td { - text-align: right; - padding: 3px; -} -.tableObject td { - font-weight: normal; - color: #000; -} -.tableObject .tableObject { - border: 1px solid #ddd; -} -.tableArray { - border: 1px solid #ddd; - border-spacing: 0; -} -.tableArray td { - border-bottom: 1px solid #ddd; -} -.gray { - font-size: 12px; - color: #888; - display: block; -} -.sha256.heading { - margin: 20px 0 0px; -} -.gray span { - padding-right: 5px; -} -.gray { - margin-bottom: 10px; -} -.gray a { - color: #666; -} - -.verified { - color: #080; - font-weight: bold; -} -.unverified { - color: #f00; - font-weight: 300; -} - -.loading, .error { - font-weight: normal; - margin: 10px 0; - padding: 3px; - font-size: 24px; -} - -.title { - text-transform: capitalize; -} -.rect { - position: absolute; -} -.rect { border: 1px solid rgba(0,0,255); background-color: rgba(0,0,255,0.1); } - -/* videos / video preloader */ - -video { - max-width: 640px; - margin: 10px 0; -} -.video { - margin: 0 0 10px 0; -} -.video .bg { - cursor: pointer; - position: relative; - background-size: cover; -} -.video .play { - position: absolute; - top: 50%; - left: 50%; - transform: translate3d(-50%, -50%, 0); - width: 20%; - height: 20%; - background-image: url(/search/static/img/play.png); - background-position: center center; - background-size: contain; - background-repeat: no-repeat; -} -.desktop .video .play:hover { - -webkit-filter: invert(60%) sepia(100%) saturate(500%) hue-rotate(160deg); -} - -/* spectre.css loader */ - -.loaderWrapper { - display: inline-block; - position: relative; - width: .8rem; - height: .8rem; - padding: 10px; -} -.loader { - color: transparent !important; - min-height: .8rem; - pointer-events: none; - position: relative; -} - -.loader::after { - animation: loader 500ms infinite linear; - border: .1rem solid #5755d9; - border-radius: 50%; - border-right-color: transparent; - border-top-color: transparent; - content: ""; - display: block; - height: .8rem; - left: 50%; - margin-left: -.4rem; - margin-top: -.4rem; - position: absolute; - top: 50%; - width: .8rem; - z-index: 1; -} - -.loader.loader-lg { - min-height: 2rem; -} - -.loader.loader-lg::after { - height: 1.6rem; - margin-left: -.8rem; - margin-top: -.8rem; - width: 1.6rem; -} - -@keyframes loader { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} \ No newline at end of file diff --git a/scraper/client/common/detectionBoxes.component.js b/scraper/client/common/detectionBoxes.component.js deleted file mode 100644 index c4872ea8..00000000 --- a/scraper/client/common/detectionBoxes.component.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' - -import { px } from '../util' - -export default function DetectionBoxes({ detections, width, height }) { - return detections.map(({ rect }, i) => ( - rect && -
    - )) -} diff --git a/scraper/client/common/detectionList.component.js b/scraper/client/common/detectionList.component.js deleted file mode 100644 index 416e66d8..00000000 --- a/scraper/client/common/detectionList.component.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' - -export default function DetectionList({ detections, labels, tag, showEmpty }) { - return ( - - {tag &&

    {tag}

    } - {!detections.length && showEmpty && } - {detections.map(({ idx, score, rect }, i) => ( - - ))} -
    - ) -} diff --git a/scraper/client/common/footer.component.js b/scraper/client/common/footer.component.js deleted file mode 100644 index 7c82b44b..00000000 --- a/scraper/client/common/footer.component.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -export default function Footer(props) { - return ( -
    -
    - ); -} diff --git a/scraper/client/common/gate.component.js b/scraper/client/common/gate.component.js deleted file mode 100644 index 9bf9287b..00000000 --- a/scraper/client/common/gate.component.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' - -function Gate(props) { - const { app, tag, View } = props - const data = app[tag] - if (!data) return null - if (data === 'loading') { - return
    {tag}{': Loading'}
    - } - if (data.err) { - return
    {tag}{' Error: '}{data.err}
    - } - return -} - -const mapStateToProps = state => ({ - app: state.metadata -}) - -export default connect(mapStateToProps)(Gate) diff --git a/scraper/client/common/header.component.js b/scraper/client/common/header.component.js deleted file mode 100644 index 84fe306f..00000000 --- a/scraper/client/common/header.component.js +++ /dev/null @@ -1 +0,0 @@ -/* imported from main vcat application */ diff --git a/scraper/client/common/index.js b/scraper/client/common/index.js deleted file mode 100644 index ad9fe5e1..00000000 --- a/scraper/client/common/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import Header from 'vcat-header' - -import ActiveLink from './activeLink.component' -import Classifier from './classifier.component' -import DetectionBoxes from './detectionBoxes.component' -import DetectionList from './detectionList.component' -// import Header from './header.component' -import Footer from './footer.component' -import Loader from './loader.component' -import Sidebar from './sidebar.component' -import Gate from './gate.component' -import Keyframe from './keyframe.component' -import Keyframes from './keyframes.component' -import Video from './video.component' -import { TableObject, TableArray, TableTuples, TableRow, TableCell } from './table.component' -import './common.css' - -export { - Header, - Footer, - Sidebar, - Loader, - Gate, - TableObject, - TableArray, - TableTuples, - TableRow, - TableCell, - ActiveLink, - Classifier, - DetectionList, - DetectionBoxes, - Keyframe, - Keyframes, - Video, -} diff --git a/scraper/client/common/keyframe.component.js b/scraper/client/common/keyframe.component.js deleted file mode 100644 index c77db3ac..00000000 --- a/scraper/client/common/keyframe.component.js +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { imageUrl, timestamp, keyframeUri, widths, verify } from '../util' -import { DetectionBoxes } from '.' - -import * as searchActions from '../search/search.actions' - -export default function Keyframe({ - verified, - sha256, - frame, - score, - isSaved, - fps = 25, - size = 'th', - className, - showHash, - showFrame, - showTimestamp, - showScore, - showSearchButton, - showSaveButton, - to, - children, - detectionList = [], - aspectRatio = 1.777, - onClick, - reviewActions, -}) { - if (!sha256) return null - const width = widths[size] - const height = Math.round(width / aspectRatio) - return ( -
    -
    - - {'Frame - {detectionList.map(({ labels, detections }, i) => ( - - ))} - - {(reviewActions && (showSearchButton || showSaveButton)) && - - } -
    - {(showHash || showFrame || showTimestamp || showScore) && - - } - {children} -
    - ) -} - -const PossiblyExternalLink = props => { - if (props.onClick) { - return props.children - } - if (props.to.match(/^http/)) { - return {props.children} - } - return -} diff --git a/scraper/client/common/keyframes.component.js b/scraper/client/common/keyframes.component.js deleted file mode 100644 index 62eda45e..00000000 --- a/scraper/client/common/keyframes.component.js +++ /dev/null @@ -1,95 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' - -import { Keyframe } from '.' -import * as reviewActions from '../review/review.actions' -import * as searchActions from '../search/search.actions' - -function Keyframes(props) { - // console.log(props) - let { - frames, - groupByHash, - } = props - let minDistance = 0 - if (frames && frames.length) { - minDistance = frames[0].distance || 0 - } - if (!groupByHash) { - return ( - - ) - } - const frameGroups = frames.reduce((a, b) => { - if (a[b.hash]) { - a[b.hash].push(b) - } else { - a[b.hash] = [b] - } - return a - }, {}) - return Object.keys(frameGroups) - .map(hash => [frameGroups[hash].length, hash]) - .sort((a, b) => b[0] - a[0]) - .map(([count, hash]) => ( - - )) -} - -function KeyframeList(props) { - let { - saved = {}, - frames, - options, - review, - search, - minDistance, - label, - count, - ...frameProps - } = props - if (!frames) return null - return ( -
    - {label &&

    {label} ({count})

    } - {frames.map(({ hash, frame, verified, distance }) => ( - review.toggleSaved({ verified, hash, frame })} - reviewActions={review} - {...frameProps} - /> - ))} -
    - ) -} - -const mapStateToProps = state => ({ - saved: state.review.saved, - options: state.search.options, -}) - -const mapDispatchToProps = dispatch => ({ - review: bindActionCreators({ ...reviewActions }, dispatch), - search: bindActionCreators({ ...searchActions }, dispatch), -}) - -export default connect(mapStateToProps, mapDispatchToProps)(Keyframes) diff --git a/scraper/client/common/loader.component.js b/scraper/client/common/loader.component.js deleted file mode 100644 index 6795424b..00000000 --- a/scraper/client/common/loader.component.js +++ /dev/null @@ -1,10 +0,0 @@ -import React, { Component } from 'react' - -export default function Loader() { - return ( -
    -
    -
    -
    - ) -} \ No newline at end of file diff --git a/scraper/client/common/sidebar.component.js b/scraper/client/common/sidebar.component.js deleted file mode 100644 index 487f3289..00000000 --- a/scraper/client/common/sidebar.component.js +++ /dev/null @@ -1,37 +0,0 @@ -import React, { Component } from 'react' -import { NavLink } from 'react-router-dom' -import { connect } from 'react-redux' - -class Sidebar extends Component { - render() { - const { hash } = this.props - if (!hash) { - return ( -
    -
    - ) - } - return ( -
    -

    Media

    - Summary - Media Record - Media Info - Sugarcube - -

    Keyframes

    - Keyframe - -

    Detectors

    - Places 365 - Coco -
    - ) - } -} - -const mapStateToProps = state => ({ - hash: state.metadata.hash, -}) - -export default connect(mapStateToProps)(Sidebar) diff --git a/scraper/client/common/table.component.js b/scraper/client/common/table.component.js deleted file mode 100644 index 76a1d57c..00000000 --- a/scraper/client/common/table.component.js +++ /dev/null @@ -1,121 +0,0 @@ -import React from 'react' - -import { formatName } from '../util' - -const __HR__ = '__HR__' - -export function TableObject({ tag, object, order, summary }) { - if (!object) return null - if (object === 'loading') { - return
    {tag}{': Loading'}
    - } - if (object.err) { - return
    {tag}{' Error: '}{object.err}
    - } - let objects = Object.keys(object) - if (order) { - const grouped = objects.reduce((a, b) => { - const index = order.indexOf(b) - if (index !== -1) { - a.order.push([index, b]) - } else { - a.alpha.push(b) - } - return a - }, { order: [], alpha: [] }) - objects = grouped.order - .sort((a, b) => a[0] - b[0]) - .map(([i, s]) => s) - if (!summary) { - objects = objects - // .concat([__HR__]) - .concat(grouped.alpha.sort()) - } - } else { - objects = objects.sort() - } - return ( -
    - {tag &&

    {tag}

    } - - - {objects.map((key, i) => ( - - ))} - -
    -
    - ) -} - -export function TableArray({ tag, list }) { - if (!list) return null - return ( -
    - {tag &&

    {tag}

    } - - - {list.map((value, i) => ( - - - - ))} - -
    -
    - ) -} - -export function TableTuples({ tag, list }) { - if (!list) return null - return ( -
    - {tag &&

    {tag}

    } - - - {list.map(([key, ...values], i) => ( - - - {values.map((value, j) => ( - - ))} - - ))} - -
    {formatName(key)}
    -
    - ) -} - -export function TableRow({ name, value }) { - if (name === __HR__) { - return ( - - -
    - - - ) - } - return ( - - {formatName(name)} - - - ) -} - -export function TableCell({ value }) { - if (value && typeof value === 'object') { - if (value._raw) { - value = value.value - } else if (value.length) { - value = - } else { - value = - } - } - return ( - {value} - ) -} diff --git a/scraper/client/common/video.component.js b/scraper/client/common/video.component.js deleted file mode 100644 index e5525bf6..00000000 --- a/scraper/client/common/video.component.js +++ /dev/null @@ -1,47 +0,0 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' -import { imageUrl, widths } from '../util' - -import { Gate } from '.' - -class Video extends Component { - state = { - playing: false, - } - - render() { - const { app, data, size } = this.props - const { playing } = this.state - const { sugarcube } = data.metadata - const url = sugarcube.fp.replace('/var/www/files/', 'https://cube.syrianarchive.org/') - const { sha256, verified } = app.mediainfo - const { video } = app.mediainfo.metadata.mediainfo - const keyframe = app.keyframe.metadata.keyframe.basic[0] - return ( -
    - {playing - ?
    - ) - } -} - -const mapStateToProps = () => ({ - tag: 'sugarcube', -}) - -export default connect(mapStateToProps)(props => ( - -)) diff --git a/scraper/client/index.js b/scraper/client/index.js deleted file mode 100644 index eddc5fb2..00000000 --- a/scraper/client/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import { AppContainer } from 'react-hot-loader' -import { Provider } from 'react-redux' - -import App from './app' - -import { store, history } from './store' - -const container = document.createElement('div') -document.body.appendChild(container) - -ReactDOM.render( - - - - - , container -) diff --git a/scraper/client/metadata/index.js b/scraper/client/metadata/index.js deleted file mode 100644 index 0eef814e..00000000 --- a/scraper/client/metadata/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import Heading from './heading.component' -import MediaInfo from './mediaInfo.component' -import MediaRecord from './mediaRecord.component' -import Summary from './summary.component' -import KeyframeList from './keyframeList.component' -import KeyframeSingle from './keyframeSingle.component' -import KeyframeStatus from './keyframeStatus.component' -import Coco from './coco.component' -import Places365 from './places365.component' -import Sugarcube from './sugarcube.component' - -import './metadata.css' - -export { - Heading, - MediaRecord, - MediaInfo, - Summary, - KeyframeList, - KeyframeSingle, - KeyframeStatus, - Coco, - Places365, - Sugarcube, -} diff --git a/scraper/client/session.js b/scraper/client/session.js deleted file mode 100644 index 5bfae7eb..00000000 --- a/scraper/client/session.js +++ /dev/null @@ -1,5 +0,0 @@ -import Storage from 'store2' - -const session = Storage.namespace('vcat.search') - -export default session diff --git a/scraper/client/store.js b/scraper/client/store.js deleted file mode 100644 index 043af351..00000000 --- a/scraper/client/store.js +++ /dev/null @@ -1,38 +0,0 @@ -import { applyMiddleware, compose, combineReducers, createStore } from 'redux' -import { connectRouter, routerMiddleware } from 'connected-react-router' -import { createBrowserHistory } from 'history' -import thunk from 'redux-thunk' -import { login } from './util' - -import metadataReducer from './metadata/metadata.reducer' -import searchReducer from './search/search.reducer' -import reviewReducer from './review/review.reducer' - -const rootReducer = combineReducers({ - auth: (state = login()) => state, - metadata: metadataReducer, - search: searchReducer, - review: reviewReducer, -}) - -function configureStore(initialState = {}, history) { - const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose - - const store = createStore( - connectRouter(history)(rootReducer), // new root reducer with router state - initialState, - composeEnhancers( - applyMiddleware( - thunk, - routerMiddleware(history) - ), - ), - ) - - return store -} - -const history = createBrowserHistory() -const store = configureStore({}, history) - -export { store, history } diff --git a/scraper/client/types.js b/scraper/client/types.js deleted file mode 100644 index e3c64691..00000000 --- a/scraper/client/types.js +++ /dev/null @@ -1,21 +0,0 @@ -export const asType = (type, name) => [type, name].join('_').toUpperCase() -export const tagAsType = (type, names) => ( - names.reduce((tags, name) => { - tags[name] = asType(type, name) - return tags - }, {}) -) - -export const metadata = tagAsType('metadata', [ - 'loading', 'loaded', 'loaded_many', 'error', 'set_hash' -]) - -export const search = tagAsType('search', [ - 'loading', 'loaded', 'error', 'panic', 'update_options', -]) - -export const review = tagAsType('review', [ - 'loading', 'loaded', 'error', 'save', 'unsave', 'refresh', 'clear', 'dedupe', 'create', 'set_count' -]) - -export const init = '@@INIT' diff --git a/scraper/client/util.js b/scraper/client/util.js deleted file mode 100644 index ad303c64..00000000 --- a/scraper/client/util.js +++ /dev/null @@ -1,167 +0,0 @@ -/* Mobile check */ - -export const isiPhone = !!((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))) -export const isiPad = !!(navigator.userAgent.match(/iPad/i)) -export const isAndroid = !!(navigator.userAgent.match(/Android/i)) -export const isMobile = isiPhone || isiPad || isAndroid -export const isDesktop = !isMobile - -const htmlClassList = document.body.parentNode.classList -htmlClassList.add(isDesktop ? 'desktop' : 'mobile') - -/* Default image dimensions */ - -export const widths = { - th: 160, - sm: 320, - md: 640, - lg: 1280, -} - -/* Formatting functions */ - -const acronyms = 'id url cc sa fp md5 sha256'.split(' ').map(s => '_' + s) -const acronymsUpperCase = acronyms.map(s => s.toUpperCase()) - -export const formatName = s => { - acronyms.forEach((acronym, i) => s = s.replace(acronym, acronymsUpperCase[i])) - return s.replace(/_/g, ' ') -} - -// Use to pad frame numbers with zeroes -export const pad = (n, m) => { - let s = String(n || 0) - while (s.length < m) { - s = '0' + s - } - return s -} - -// Verified is 0/1 when retrieved from SQL, but 'verified' or 'unverified' when retrieved elsewhere -export const isVerified = verified => verified === 1 || verified === '1' || verified === 'verified' -export const verify = verified => isVerified(verified) ? 'verified' : 'unverified' - -export const courtesyS = (n, s) => n + ' ' + (n === 1 ? s : s + 's') - -export const padSeconds = n => n < 10 ? '0' + n : n - -export const timestamp = (n = 0, fps = 25) => { - n /= fps - let s = padSeconds(Math.round(n) % 60) - n = Math.floor(n / 60) - if (n > 60) { - return Math.floor(n / 60) + ':' + padSeconds(n % 60) + ':' + s - } - return (n % 60) + ':' + s -} - -export const percent = n => (n * 100).toFixed(1) + '%' - -export const px = (n, w) => Math.round(n * w) + 'px' - -export const clamp = (n, a, b) => n < a ? a : n < b ? n : b - -/* URLs */ - -export const hashPath = sha256 => { - if (!sha256 || sha256.length < 9) { - throw new Error('Invalid sha256') - } - return [ - sha256.slice(0, 3), - sha256.slice(3, 6), - sha256.slice(6, 9), - sha256, - ].join('/') -} - -export const imageUrl = (verified, sha256, frame, size = 'th') => [ - 'https://' + process.env.S3_HOST + '/v1/media/keyframes', - isVerified(verified) ? null : 'unverified', - hashPath(sha256), - pad(frame, 6), - size, - 'index.jpg' -].filter(s => !!s).join('/') - -export const metadataUri = (sha256, tag) => '/metadata/' + sha256 + '/' + tag + '/' -export const keyframeUri = (sha256, frame) => '/metadata/' + sha256 + '/keyframe/' + pad(frame, 6) + '/' - -export const preloadImage = opt => { - let { verified, hash, frame, url } = opt - if (hash && frame) { - url = imageUrl(verified, hash, frame, 'md') - } - const image = new Image() - let loaded = false - image.onload = () => { - if (loaded) return - loaded = true - image.onload = null - } - // console.log(img.src) - image.crossOrigin = 'anonymous' - image.src = url - if (image.complete) { - image.onload() - } -} - -/* AJAX */ - -let cachedAuth = null -let token = '' -let username = '' - -export const post = (uri, data, credentials) => { - login() - let headers - if (data instanceof FormData) { - headers = { - Accept: 'application/json, application/xml, text/play, text/html, *.*', - } - } else { - headers = { - Accept: 'application/json, application/xml, text/play, text/html, *.*', - 'Content-Type': 'application/json; charset=utf-8', - } - data = JSON.stringify(data) - } - let opt = { - method: 'POST', - body: data, - headers, - credentials: 'include', - } - if (credentials) { - headers.Authorization = 'Token ' + token - } - // console.log(headers) - // headers['X-CSRFToken'] = csrftoken - return fetch(uri, opt).then(res => res.json()) -} - -// api queries -export const login = () => { - if (cachedAuth) return cachedAuth - const isLocal = (window.location.hostname === '0.0.0.0' || window.location.hostname === '127.0.0.1') - try { - const auth = JSON.parse(JSON.parse(localStorage.getItem('persist:root')).auth) - // console.log('auth', auth) - token = auth.token - username = auth.user.username - if (token) { - console.log('logged in', username) - } - cachedAuth = auth - if (!token && !isLocal) { - window.location.href = '/' - } - return auth - } catch (e) { - if (!isLocal) { - window.location.href = '/' - } - return {} - } -} diff --git a/server/app/README.md b/server/app/README.md deleted file mode 100644 index 8bc70132..00000000 --- a/server/app/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Startup - -Run supervisor - -`/usr/bin/supervisord` - -Tail log file - -`tail -f /var/log/uwsgi/app/app.log` - -`/opt/redis/redis-stable/src/redis-server &` - -`celery worker -A celery_worker.celery --loglevel=info &` - -If using on Production Server - -`/usr/bin/nohup /usr/bin/supervisord &` \ No newline at end of file diff --git a/server/app/__init__.py b/server/app/__init__.py deleted file mode 100644 index bce3f9ee..00000000 --- a/server/app/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -import logging -from logging.handlers import RotatingFileHandler - -from flask import Flask -from flask_bootstrap import Bootstrap - -from flask import Flask - -from config import config, Config - -bootstrap = Bootstrap() -#celery = Celery(__name__, broker=Config.CELERY_BROKER_URL) -from .basemodels import celery - -def create_app(config_name): - app = Flask(__name__) - app.config.from_object(config[config_name]) - config[config_name].init_app(app) - - bootstrap.init_app(app) - celery.conf.update(app.config) - - from .main import main as main_blueprint - app.register_blueprint(main_blueprint) - - #handler = RotatingFileHandler('debug.log', maxBytes=10000, backupCount=1) - #handler.setLevel(logging.INFO) - #app.logger.addHandler(handler) - - format = "%(asctime)s - [%(levelname)s] %(message)s" - logging.basicConfig(filename='debug.log', - filemode='a', - format=format, - level=logging.DEBUG) - console = logging.StreamHandler() - console.setLevel(logging.DEBUG) - logging.getLogger(__name__).addHandler(console) - - return app diff --git a/server/app/basemodels.py b/server/app/basemodels.py deleted file mode 100644 index 475ab0c2..00000000 --- a/server/app/basemodels.py +++ /dev/null @@ -1,5 +0,0 @@ -from config import config, Config -from celery import Celery - -#bootstrap = Bootstrap() -celery = Celery(__name__, broker=Config.CELERY_BROKER_URL) diff --git a/server/app/favicon.ico b/server/app/favicon.ico deleted file mode 100644 index 4d001b21..00000000 Binary files a/server/app/favicon.ico and /dev/null differ diff --git a/server/app/index.html b/server/app/index.html deleted file mode 100644 index 3c1b0dfd..00000000 --- a/server/app/index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - DullDream (v2 x ZkM) - - - - -
    -

    DullDream

    -

    Neural network photo effect

    -
    - -
    -
    -
    - - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    - Change Image - -
    - -
    - Upload -
    -
    - -
    - -
    -
    - About - Privacy -

    - All images uploaded can be used for exhibition and review purposes. -

    -

    - Currently this work is on view at ZKM. View recent DullDreams here. -

    -
    -
    -
    - - - - - - -
    -
    -
    - -
    - Made with DullDream.xyz for ZKM OpenCodes 2017 -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - Home - About - Privacy -
    - -
    - -
    - - - - - - - - - - - - - - diff --git a/server/app/main/__init__.py b/server/app/main/__init__.py deleted file mode 100644 index a21e2754..00000000 --- a/server/app/main/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from flask import Blueprint - -main = Blueprint('main', __name__) - -from . import views, errors, tasks, utils \ No newline at end of file diff --git a/server/app/main/errors.py b/server/app/main/errors.py deleted file mode 100644 index 60b5f227..00000000 --- a/server/app/main/errors.py +++ /dev/null @@ -1,32 +0,0 @@ -from flask import render_template, request, jsonify -from . import main - - -@main.app_errorhandler(403) -def forbidden(e): - if request.accept_mimetypes.accept_json and \ - not request.accept_mimetypes.accept_html: - response = jsonify({'error': 'forbidden'}) - response.status_code = 403 - return response - return render_template('403.html'), 403 - - -@main.app_errorhandler(404) -def page_not_found(e): - if request.accept_mimetypes.accept_json and \ - not request.accept_mimetypes.accept_html: - response = jsonify({'error': 'not found'}) - response.status_code = 404 - return response - return render_template('404.html'), 404 - - -@main.app_errorhandler(500) -def internal_server_error(e): - if request.accept_mimetypes.accept_json and \ - not request.accept_mimetypes.accept_html: - response = jsonify({'error': 'internal server error'}) - response.status_code = 500 - return response - return render_template('500.html'), 500 diff --git a/server/app/main/forms.py b/server/app/main/forms.py deleted file mode 100644 index bc1399ad..00000000 --- a/server/app/main/forms.py +++ /dev/null @@ -1,60 +0,0 @@ -from flask.ext.wtf import Form -from wtforms import StringField, TextAreaField, BooleanField, SelectField,\ - SubmitField -from wtforms.validators import Required, Length, Email, Regexp -from wtforms import ValidationError -from flask.ext.pagedown.fields import PageDownField -from ..models import Role, User - - -class NameForm(Form): - name = StringField('What is your name?', validators=[Required()]) - submit = SubmitField('Submit') - - -class EditProfileForm(Form): - name = StringField('Real name', validators=[Length(0, 64)]) - location = StringField('Location', validators=[Length(0, 64)]) - about_me = TextAreaField('About me') - submit = SubmitField('Submit') - - -class EditProfileAdminForm(Form): - email = StringField('Email', validators=[Required(), Length(1, 64), - Email()]) - username = StringField('Username', validators=[ - Required(), Length(1, 64), Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0, - 'Usernames must have only letters, ' - 'numbers, dots or underscores')]) - confirmed = BooleanField('Confirmed') - role = SelectField('Role', coerce=int) - name = StringField('Real name', validators=[Length(0, 64)]) - location = StringField('Location', validators=[Length(0, 64)]) - about_me = TextAreaField('About me') - submit = SubmitField('Submit') - - def __init__(self, user, *args, **kwargs): - super(EditProfileAdminForm, self).__init__(*args, **kwargs) - self.role.choices = [(role.id, role.name) - for role in Role.query.order_by(Role.name).all()] - self.user = user - - def validate_email(self, field): - if field.data != self.user.email and \ - User.query.filter_by(email=field.data).first(): - raise ValidationError('Email already registered.') - - def validate_username(self, field): - if field.data != self.user.username and \ - User.query.filter_by(username=field.data).first(): - raise ValidationError('Username already in use.') - - -class PostForm(Form): - body = PageDownField("What's on your mind?", validators=[Required()]) - submit = SubmitField('Submit') - - -class CommentForm(Form): - body = StringField('Enter your comment', validators=[Required()]) - submit = SubmitField('Submit') diff --git a/server/app/main/img_proc_config.py b/server/app/main/img_proc_config.py deleted file mode 100644 index db124978..00000000 --- a/server/app/main/img_proc_config.py +++ /dev/null @@ -1,20 +0,0 @@ -# paths for image processors -import os -from os.path import join - -class ImgProcConfig: - - def __init__(self): - dir_models = '/data_store/apps/dulldream/dnn_models' - - # mask rcnn - self.mask_rcnn_class_config = '/dulldream/src/config/coco_meta.json' - self.mask_rcnn_model = join(dir_models,'tf/mask_rcnn_coco.h5') - - # p2p - self.p2p_ckpts_dir = join(dir_models,'p2p/coco2014_person') - self.p2p_epoch = 'latest' - - # p2p objects only - self.p2p_bg_ckpts_dir = join(dir_models,'p2p/coco2014_objects') - self.p2p_bg_epoch = 'latest' diff --git a/server/app/main/paths.py b/server/app/main/paths.py deleted file mode 100644 index 69c21627..00000000 --- a/server/app/main/paths.py +++ /dev/null @@ -1,19 +0,0 @@ -from flask import current_app as app - -def get_paths(agree): - if agree: - return ( - app.config['UPLOADS'], - app.config['RENDERS'], - app.config['JSON_DIR'], - app.config['UPLOADS_URI'], - app.config['RENDERS_URI'], - ) - else: - return ( - app.config['UPLOADS_PRIVATE'], - app.config['RENDERS_PRIVATE'], - app.config['JSON_PRIVATE_DIR'], - app.config['UPLOADS_PRIVATE_URI'], - app.config['RENDERS_PRIVATE_URI'], - ) diff --git a/server/app/main/tasks.py b/server/app/main/tasks.py deleted file mode 100644 index 970e6988..00000000 --- a/server/app/main/tasks.py +++ /dev/null @@ -1,374 +0,0 @@ -import os -import sys -import time -import datetime -import json -from PIL import Image, ImageFilter -import cv2 as cv -import numpy as np -from . import main, utils -from .. import basemodels -from flask import current_app as app -from .paths import get_paths -celery = basemodels.celery -from celery.utils.log import get_task_logger -celery_logger = get_task_logger(__name__) -import imutils - - -# init image processors -sys.path.append('/dulldream/src/') -from .img_proc_config import ImgProcConfig -from image_processors.mask_rcnn import MaskRCNN -from image_processors.pix2pix import Pix2Pix -from utils import imx -from utils import fiox - - -# initialize image processor -img_proc_config = ImgProcConfig() -p2p = Pix2Pix(img_proc_config.p2p_ckpts_dir,epoch=img_proc_config.p2p_epoch) -p2p_objects = Pix2Pix(img_proc_config.p2p_bg_ckpts_dir,epoch=img_proc_config.p2p_epoch) - -mask_rcnn = MaskRCNN(img_proc_config.mask_rcnn_class_config, - model_path=img_proc_config.mask_rcnn_model) - - -@celery.task(bind=True) -def task_dull(self, uuid_name, agree, mask_rcnn_result): - """Process image and update during""" - celery_logger.debug('process_image_task, uuid: {}'.format(uuid_name)) - - upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(agree) - - files = [] - im = Image.open(os.path.join(upload_dir, uuid_name + '.jpg')).convert('RGB') - #im_np = cv.cvtColor(imx.ensure_np(im),cv.COLOR_RGB2BGR) - im_np = imx.ensure_np(im) - im_np = im_np[:,:,::-1] - im = im.resize((256,256)) - im_np_256 = imutils.resize(im_np,width=256) - - # Add original - fpath = os.path.join(render_dir, uuid_name + '_orig.jpg') - im.save(fpath, 'JPEG', quality=95) - files.append({ - 'title': 'Original', - 'fn': render_uri + uuid_name + '_orig.jpg' - }) - - if mask_rcnn_result['valid']: - # ----------------------------------------------- - # Segment image (processed in views) - # seems to be an error with async celery processor? - # ----------------------------------------------- - - # parse mrcnn data - im_mask = cv.imread(mask_rcnn_result['fp_im_mask']) - seg_mask = cv.imread(mask_rcnn_result['fp_seg_mask']) - #score = mask_rcnn_result['score'] - #name = mask_rcnn_result['name'] - #color = mask_rcnn_result['color'] - files.append({ - 'title': 'Semantic Segmentation', - 'fn': render_uri + uuid_name + '_seg_mask.jpg' - }) - files.append({ - 'title': 'Semantic Segmentation Isolate', - 'fn': render_uri + uuid_name + '_im_mask.jpg' - }) - - - # ----------------------------------------------- - # run rag generator - # ----------------------------------------------- - - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': 0.50, - 'message': 'Applying Region Adjacency Graph', - 'uuid': uuid_name - }) - - # save the regions adjancency graph - im_rag = imx.create_rag_mean(im_mask,compactness=30,n_segments=128) - fpath = os.path.join(render_dir, uuid_name + '_rgraph.jpg') - imx.save_np_as_pil(fpath,im_rag,quality=95) - files.append({ - 'title': 'Region Adjacency Graph', - 'fn': render_uri + uuid_name + '_rgraph.jpg' - }) - - - # ----------------------------------------------- - # generate p2p fake - # ----------------------------------------------- - - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': 0.75, - 'message': 'Running generative adversarial network...', - 'uuid': uuid_name - }) - - - # convert segmentation to mask - seg_mask_gray = cv.cvtColor(seg_mask,cv.COLOR_BGR2GRAY) - seg_mask_gray[seg_mask_gray > 1] = 255 - - # find best P2P fit - ims_p2p = [] - match_amts = [] - iters = 15 - for i in range(0,iters): - im_p2p = p2p.create_p2p(im_rag) - ims_p2p.append(im_p2p) - im_p2p_mask = cv.cvtColor(im_p2p,cv.COLOR_RGB2GRAY) - im_p2p_mask[im_p2p_mask > 1] = 255 - # find where masks intersect - matches = np.bitwise_and(im_p2p_mask,seg_mask_gray) - amt = len(np.where(matches == 255)[0]) - match_amts.append(amt) - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': 0.75, - 'message': 'Generating ({}/{})'.format(i,iters), - 'uuid': uuid_name - }) - - best_idx = np.argmax(match_amts) - im_p2p = ims_p2p[best_idx] - - fpath = os.path.join(render_dir, uuid_name + '_gan.jpg') - imx.save_np_as_pil(fpath,im_p2p,quality=95) - files.append({ - 'title': 'Generative Adversarial Network', - 'fn': render_uri + uuid_name + '_gan.jpg' - }) - - - # ----------------------------------------------- - # generate p2p fake - # ----------------------------------------------- - - # announce to user - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': 0.90, - 'message': 'Compositing images...', - 'uuid': uuid_name - }) - - - # apply masked cloning - im_p2p_gray = cv.cvtColor(im_p2p,cv.COLOR_BGR2GRAY) - im_clone_mask = np.zeros_like(im_p2p_gray,dtype=np.uint8) - im_clone_mask[im_p2p_gray > 1] = 255 - - - # apply smoothed copy+paste clone - im_blur_mask = np.zeros(im_np_256.shape[:2],dtype=np.float64) - im_blur_mask[im_p2p_gray > 1] = 1.0 - im_blur_mask = np.array([im_blur_mask,im_blur_mask,im_blur_mask]).transpose((1,2,0)) - - # erode mask to remove black border - kernel = np.ones((3,3),np.uint8) - im_blur_mask = cv.erode(im_blur_mask,kernel,iterations = 3) - - # feather mask - feather_amt = (3,3) - im_blur_mask = (cv.GaussianBlur(im_blur_mask,feather_amt, 0) > 0) * 1.0 #? - im_blur_mask = cv.GaussianBlur(im_blur_mask,feather_amt, 0) - im_blur_mask = np.clip(im_blur_mask,0.0,1.0) - - # mask p2p fg --> photo bg - im_dull = im_np_256.astype(np.float64) * (1.0 - im_blur_mask) + im_p2p.astype(np.float64) * im_blur_mask - im_dull = im_dull.astype(np.uint8) - - - else: - print('No person. Apply background P2P') - celery_logger.debug('No person. Apply background P2P, uuid: {}'.format(uuid_name)) - im_bg_blur = cv.GaussianBlur(im_np_256,(31,31),0) - im_bg_rag = imx.create_rag_mean(im_bg_blur,compactness=30,n_segments=64) - - # apply gan - im_dull = p2p_objects.create_p2p(im_bg_rag) - - # resize back to full 512px - im_dull_512 = imutils.resize(im_dull,width=512) - - # save dulldream image - fpath = os.path.join(render_dir, uuid_name + '_dull.jpg') - imx.save_np_as_pil(fpath,im_dull_512,quality=95) - files.append({ - 'title': 'Your DullDream', - 'fn': render_uri + uuid_name + '_dull.jpg' - }) - - - # ----------------------------------------------- - # Write data to disk - # ----------------------------------------------- - - data = { - 'uuid': uuid_name, - 'date': str(datetime.datetime.now()), - 'files': files - } - - json_path = os.path.join(json_dir, uuid_name + '.json') - with open(json_path, 'w') as json_file: - json.dump(data, json_file) - - return { - 'percent': 100, - 'state': 'complete', - 'uuid': uuid_name - } - - - - -@celery.task(bind=True) -def blur_task(self, uuid_name, agree, extra): - """Process image and update during""" - celery_logger.debug('process_image_task, uuid: {}'.format(uuid_name)) - - upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(agree) - - files = [] - - im = Image.open(os.path.join(upload_dir, uuid_name + '.jpg')).convert('RGB') - im = im.resize((256,256)) - files.append({ - 'title': 'Original image', - 'fn': upload_uri + uuid_name + '.jpg' - }) - - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': 0.25, - 'message': 'Applying blur', - 'uuid': uuid_name - }) - - im_np = utils.ensure_np(im) - im_blur = cv.blur(im_np, (5,5), 1.0) - im_blur_pil = utils.ensure_pil(im_blur) - - fn = uuid_name + '_blur.jpg' - fpath = os.path.join(render_dir, fn) - im_blur_pil.save(fpath, 'JPEG', quality=95) - - files.append({ - 'title': 'Blurred image', - 'fn': render_uri + uuid_name + '_blur.jpg' - }) - - time.sleep(3) - - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': 0.50, - 'message': 'Sleeping for some reason', - 'uuid': uuid_name - }) - time.sleep(2) - - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': 0.75, - 'message': 'Sleeping some more', - 'uuid': uuid_name - }) - time.sleep(2) - - data = { - 'uuid': uuid_name, - 'date': str(datetime.datetime.now()), - 'files': files - } - - json_path = os.path.join(json_dir, uuid_name + '.json') - with open(json_path, 'w') as json_file: - json.dump(data, json_file) - - celery_logger.debug('ok') - - return { - 'percent': 100, - 'state': 'complete', - 'uuid': uuid_name, - } - -@celery.task(bind=True) -def sleep_task(self, uuid_name): - celery_logger.debug('sleep_task'.format(uuid_name)) - msgs = [ - {'msg':'Uploaded OK','time':.1}, - {'msg':'Segmenting Image...','time':2}, - {'msg':'Found: Person, Horse','time':1}, - {'msg':'Creating Pix2Pix','time':2} - ] - for i,m in enumerate(msgs): - percent = int(float(i)/float(len(msgs))*100.0) - self.update_state( - state = 'PROCESSING', - meta = { - 'percent': percent, - 'message': m['msg'], - 'uuid': uuid_name - }) - celery_logger.debug(m['msg']) - time.sleep(m['time']) - - return { - 'percent': 100, - 'state': 'complete', - 'uuid': uuid_name - } - -def make_task_json(): - dropdown = {} - for k,v in task_lookup.items(): - if 'active' not in v or v['active'] is not False: - is_default = 'default' in v and v['default'] is True - task = { - 'name': k, - 'title': v['title'], - 'selected': is_default, - } - dropdown[k] = task - return json.dumps(dropdown) - -# Add all valid tasks to this lookup. -# Set 'active': False to disable a task -# Set 'default': True to define the default task - -task_lookup = { - 'sleep': { - 'title': 'Sleep Test', - 'task': sleep_task, - 'active': False - }, - 'blur': { - 'title': 'Blur', - 'task': blur_task, - 'active': False - }, - 'task_dull': { - 'title': 'DullDream V2', - 'task': task_dull, - 'active': True, - 'default': True - } -} - diff --git a/server/app/main/utils.py b/server/app/main/utils.py deleted file mode 100644 index 510e5c23..00000000 --- a/server/app/main/utils.py +++ /dev/null @@ -1,37 +0,0 @@ -from flask import current_app as app -from PIL import Image -import numpy as np -import cv2 as cv -import os -from os.path import join - -def ensure_pil(im): - try: - im.verify() - return im - except: - return Image.fromarray(im.astype('uint8'), 'RGB') - -def ensure_np(im): - if type(im) == np.ndarray: - return im - return np.asarray(im, np.uint8) - -def get_recent_uploads(limit=10): - d_uploads = app.config['UPLOADS'] - d_renders = app.config['RENDERS'] - - # list all files in uploads dir - filenames = [s for s in os.listdir(d_uploads) - if os.path.isfile(os.path.join(d_uploads, s))] - # sort upload files by date - filenames.sort(key=lambda s: os.path.getmtime(os.path.join(d_uploads, s)),reverse=True) - basenames = [os.path.splitext(os.path.basename(f))[0] for f in filenames] - basenames = basenames[:limit] - filenames = [f for f in basenames if os.path.isfile(join(d_renders,'{}_dull.jpg'.format(f)))] - - # create list for uploads and renders - uploads = [join('/img/uploads',f) for f in filenames] - renders = [join('/img/renders','{}_dull'.format(f)) for f in filenames] - urls = [join('/d',f) for f in basenames] - return uploads, renders, urls diff --git a/server/app/main/views.py b/server/app/main/views.py deleted file mode 100644 index 11a8ca53..00000000 --- a/server/app/main/views.py +++ /dev/null @@ -1,300 +0,0 @@ -import os -import uuid -import json -from flask import render_template, redirect, url_for, send_from_directory -from flask import request, make_response, jsonify -from . import main, utils -from .tasks import task_lookup, make_task_json -from PIL import Image, ImageOps -import cv2 as cv - -from .paths import get_paths - -from flask import current_app as app -from werkzeug.utils import secure_filename -import imutils - -# ------------------------------------------------------------ -# Temp: run mask rcnn outside celery -# ------------------------------------------------------------ - -# init image processors -import sys -from .img_proc_config import ImgProcConfig -sys.path.append('/dulldream/src/') -from image_processors.mask_rcnn import MaskRCNN -from utils import imx -from utils import fiox - -img_proc_congif = ImgProcConfig() -mask_rcnn = MaskRCNN(img_proc_congif.mask_rcnn_class_config, - model_path=img_proc_congif.mask_rcnn_model) - -# ------------------------------------------------------------ -# Tasks -# ------------------------------------------------------------ - -@main.route('/status//') -def task_status(task_name, task_id): - """Return celery image processing status""" - if task_name in task_lookup: - task = task_lookup[task_name]['task'].AsyncResult(task_id) - else: - return jsonify({ - 'state': 'error', - 'percent': 100, - 'message': 'Unknown task' - }) - - app.logger.info('task state: {}'.format(task.state)) - if task.state == 'PENDING': - response = { - 'state': task.state, - 'percent': 0, - 'message': 'Pending...' - } - elif task.state != 'FAILURE': - response = { - 'state': task.state, - 'percent': task.info.get('percent', 0), - 'uuid': task.info.get('uuid', 0), - 'message': task.info.get('message', '') - } - if 'result' in task.info: - response['result'] = task.info['result'] - else: - # something went wrong in the background job - response = { - 'state': task.state, - 'percent': 100, - 'message': str(task.info), # this is the exception raised - } - return jsonify(response) - -# ------------------------------------------------------------ -# POST Routes -# ------------------------------------------------------------ - -@main.route('/upload/sleep', methods=['GET', 'POST']) -def sleep_test(): - async_task = task_lookup['sleep']['task'].apply_async(args=['sleep_test']) - task_url = url_for('main.task_status', task_name='sleep', task_id=async_task.id) - return jsonify({ - 'result': True, - 'task_url': task_url, - }) - -@main.route('/upload', methods=['POST']) -def upload(): - - style = request.form['style'] - print('style',style) - if style in task_lookup: - task = task_lookup[style]['task'] - print('task',task) - else: - return jsonify({ - 'result': False, - 'error': 'Unknown task', - }) - - file = request.files['user_image'] - agree = bool(request.form['agree']) - ext = request.form['ext'] - if ext is None: - ext = request.files['ext'] - - uuid_name = str(uuid.uuid4()) - - app.logger.info('[+] style: {}'.format(style)) - app.logger.info('[+] ext: {}'.format(ext)) - app.logger.info('[+] uuid_name: {}'.format(uuid_name)) - app.logger.info('[+] agreed: {}'.format(agree)) - - # convert PNG to JPG - print('[+] Resizing image') - - # LOL MaskRCNN needs to be run outside of the Celery Task - im = Image.open(file.stream).convert('RGB') - im = ImageOps.fit(im,(512,512)) - if agree: - upload_folder = app.config['UPLOADS'] - else: - upload_folder = app.config['UPLOADS_PRIVATE'] - - fpath = os.path.join(upload_folder, uuid_name + '.jpg') - - # Save image to disk - print('[+] Save image to {}'.format(fpath)) - im.save(fpath, 'JPEG', quality=100) - im_pil_256 = im.resize((256,256)) - - print('[+] ensure_np...') - im_np = imx.ensure_np(im_pil_256) - #print('[+] resize np...') - #im_np = imutils.resize(im_np,width=256) - - upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(agree) - - print('[+] Run mrcnn...') - try: - result = mask_rcnn.create_segmentations(im_np,concat=True) - except: - print('[-] Error. Could not run mask_rcnn') - result = [] - - if len(result) > 0: - result = result[0] - - # save data, then pass to celery task - print('[+] Save masks') - seg_mask = result['seg_mask'] - fpath_seg_mask = os.path.join(render_dir, uuid_name + '_seg_mask.jpg') - #cv.imwrite(fpath_seg_mask,cv.cvtColor(seg_mask,cv.COLOR_BGR2RGB)) - #seg_mask = seg_mask[:,:,::-1] - seg_mask_pil = imx.ensure_pil(seg_mask) - seg_mask_pil.save(fpath_seg_mask, 'JPEG', quality=100) - - im_mask = result['im_mask'] - fpath_im_mask = os.path.join(render_dir, uuid_name + '_im_mask.jpg') - #im_mask = im_mask[:,:,::-1] - im_mask_pil = imx.ensure_pil(im_mask) - im_mask_pil.save(fpath_im_mask, 'JPEG',quality=100) - #cv.imwrite(fpath_im_mask,cv.cvtColor(im_mask,cv.COLOR_BGR2RGB)) - - celery_result = { - 'score':str(result['score']), - 'name':str(result['name']), - 'class_index':str(result['class_index']), - 'color':str(result['color']), - 'fp_im_mask':fpath_im_mask, - 'fp_seg_mask':fpath_seg_mask, - 'valid':True - } - else: - print('[-] no reults. process background only') - celery_result = { - 'score':None, - 'name':None, - 'class_index':None, - 'color':None, - 'fp_im_mask':None, - 'fp_seg_mask':None, - 'valid':False - } - - print('[+] Start celery') - async_task = task.apply_async(args=[uuid_name, agree, celery_result]) - task_url = url_for('main.task_status', task_name=style, task_id=async_task.id) - - return jsonify({ - 'result': True, - 'task_url': task_url, - 'uuid': uuid_name - }) - - - -# ---------------------------------------------------- -# Fileserver, temp solution -# ---------------------------------------------------- - -@main.route('/img//') -def get_image(imtype,uuid_name): - """Return image files from render or uploads""" - if imtype == 'uploads': - d = app.config['UPLOADS'] - suffix = '' - elif imtype == 'renders': - d = app.config['RENDERS'] - suffix = '' - elif imtype == 'fcn': - d = app.config['RENDERS'] - suffix = '_fcn8' - - fname = uuid_name + suffix + '.jpg' - fpath = os.path.join(d, fname) - - if os.path.isfile(fpath): - return send_from_directory(d,fname) - else: - return send_from_directory('static', 'img/404.jpg') - -# ---------------------------------------------------- -# Deleting images -# ---------------------------------------------------- - -def destroy_data(uuid_name, is_public): - uri_base = app.config['URI_BASE'] - upload_dir, render_dir, json_dir, upload_uri, render_uri = get_paths(is_public) - - json_path = os.path.join(json_dir, uuid_name + '.json') - with open(json_path) as json_file: - data = json.load(json_file) - for f in data['files']: - path = os.path.join(uri_base, f['fn'][1:]) - if os.path.exists(path): - os.remove(path) - os.remove(json_path) - -@main.route('/d//destroy', strict_slashes=False) # public -def route_public_destroy(uuid_name): - destroy_data(uuid_name, True) - return redirect("/", code=302) - -@main.route('/p//destroy', strict_slashes=False) # private -def route_private_destroy(uuid_name): - destroy_data(uuid_name, False) - return redirect("/", code=302) - -# ---------------------------------------------------- -# Static routes -# ---------------------------------------------------- - -# Most of the pages are served with the single page app in index.html: - -task_json = make_task_json() - -@main.route('/', strict_slashes=False) -def index(): - return render_template('index.html', task_json=task_json) - -@main.route('/about', strict_slashes=False) -def about(): - return render_template('index.html', task_json=task_json) - -@main.route('/d/', strict_slashes=False) # public -def route_public(uuid_name): - return render_template('index.html', task_json=task_json) - -@main.route('/p/', strict_slashes=False) # private -def route_private(uuid_name): - return render_template('index.html', task_json=task_json) - -@main.route('/privacy', strict_slashes=False) -def privacy(): - return render_template('index.html', task_json=task_json) - -# Some of the pages have their own static file: - -@main.route('/gallery', strict_slashes = False) -def gallery(): - app.logger.info('access gallery') - uploads, renders, urls = utils.get_recent_uploads(limit=50) - uuids = [os.path.splitext(os.path.basename(f))[0] for f in uploads] - images = [{'upload':u,'render':r, 'url':url} for u,r,url in zip(uploads,renders,urls)] - return render_template('gallery.html',images=images) - -@main.route('/zkm', strict_slashes=False) -def zkm(): - app.logger.info('access ZkM') - return render_template('zkm.html') - -@main.route('/celery', strict_slashes=False) -def celery_route(): - return render_template('celery.html') - -@main.route('/projector', strict_slashes=False) -def projector(): - uploads, renders,urls = utils.get_recent_uploads() - return render_template('projector.html', uploads=uploads, renders=renders) diff --git a/server/app/static/css/bootstrap.min.css b/server/app/static/css/bootstrap.min.css deleted file mode 100644 index ed3905e0..00000000 --- a/server/app/static/css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/server/app/static/css/dullbrown-theme.css b/server/app/static/css/dullbrown-theme.css deleted file mode 100644 index 98aff038..00000000 --- a/server/app/static/css/dullbrown-theme.css +++ /dev/null @@ -1,502 +0,0 @@ -* { box-sizing: border-box; } -html { - margin: 0; padding: 0; - width: 100%; height: 100%; -} -body { - margin: 0; padding: 0; - width: 100%; height: 100%; - font-family: Helvetica, sans-serif; -} -body, .modal, #footer { - /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#a5ce3e+0,ffffff+50,a5ce3e+100 */ - background: #7B7568; /* Old browsers */ - background: -moz-linear-gradient(left, #7B7568 0%, #ffffff 50%, #7B7568 100%); /* FF3.6-15 */ - background: -webkit-linear-gradient(left, #7B7568 0%,#ffffff 50%,#7B7568 100%); /* Chrome10-25,Safari5.1-6 */ - background: linear-gradient(to right, #7B7568 0%,#ffffff 50%,#7B7568 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#7B7568', endColorstr='#a5ce3e',GradientType=1 ); /* IE6-9 */ -} - -/* ------------------------------------------------ */ -/* navbar */ -.navbar-default a.navbar-brand{ -} -.navbar-default{ - background: transparent; -} -.navbar{ - margin-bottom: 0; - border:0; -} - -.navbar-default .navbar-brand a{ - color:#ccc; -} -.navbar-default a.navbar-brand{ - color:#ccc; -} -.navbar-default a.navbar-brand:hover{ - color:#fff; -} - -/* Hamburger */ -.navbar-default .navbar-toggle{ - color:#ccc; -} -.navbar-default .navbar-toggle .icon-bar{ - color:#ccc; -} -.navbar-default .navbar-toggle .icon-bar:hover{ - color:#fff; -} -.navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover{ - color:#fff; - background: transparent; -} -.navbar{ - border-radius: 0px; - min-height:30px; -} -.navbar-default .navbar-text{ - color:#ccc; -} -.navbar-default .navbar-nav>li>a{ - color:#ccc; -} -.navbar-default .navbar-nav>li>a:hover{ - color:#fff; -} -.navbar-default .navbar-toggle .icon-bar{ - background-color:#ccc; -} -.navbar-default .navbar-toggle:hover .icon-bar{ - background-color: #eee; -} -.navbar-default .navbar-toggle:hover { - border-color: #fff; -} -.navbar-default .navbar-collapse, .navbar-default .navbar-form{ - border:0; -} - -/* ------------------------------------------------ */ -/* Jumbotron */ -.jumbotron { - padding-top: 0px; - padding-bottom: 0px; - margin-bottom: 0px; - color: inherit; -} -.jumbotron{ - background: transparent; - color:black; -} -.jumbotron h1{ - color:#ddd; - margin-bottom:0px; -} -.jumbotron a.btn-primary{ - background:#ddd; - color:#333; -} -.jumbotron a.btn-primary:hover{ - background:#eee; - color:#222; -} -.jumbotron p > a.jcallout{ - color:#eee; - padding-bottom: 3px; - border-bottom:1px dotted; - text-decoration: none; -} -.jumbotron p > a.jcallout:hover{ - color:#fff; - border-bottom:1px solid #ccc; - text-decoration: none; -} -.jumbotron a.btn-default{ - color:#eee; - border:1px solid #eee; - background: transparent; -} -.jumbotron a.btn-default:hover{ - background: #22f; - border:1px solid #ccc; -} -.jumbotron a.btn-default:active{ - color:#eee; - border:1px solid #ccc; -} - -/* Input button override --------------------------------------------------- */ -/*input[type="file"] { - display: none; -} -input[type="button"] { - display: none; -}*/ -input.hidden_input{ - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0; -} - -/* Global styles --------------------------------------------------- */ -h1,h2,h3,h4,h5,h6 { - font-weight: bold; - font-style: italic; - text-align: center; -} -h1 { - font-size:56px; - margin-top: 20px; - margin-bottom: 0; -} -h2 { - font-size:20px; - margin-top: 0; - margin-bottom: 24px; -} -ul { - list-style: none; - margin:0; - padding:0; -} -li { - display: inline-block; -} -img.img_responsive_dull { - max-width: 100%; - height: auto; -} -#photo_area{ - width: 512px; - height: 512px; - max-width: 97vw; - max-height: 97vw; - min-width: 240px; - min-height: 240px; - margin:0 auto; - text-align: center; -} -.dash_border{ - background-color: rgba(255,255,255,.2); - border:1px dashed #000; -} - -label { - display: block; -} - -div.center_inner { - position: relative; - top: 50%; - -webkit-transform: translateY(-50%); - -ms-transform: translateY(-50%); - transform: translateY(-50%); -} -#upload_controls{ - margin-top:25px; - display: none; -} -#restart_btn, #rotate_btn, #upload_btn, #dropdown_btn { - display: inline-block; - margin-left:5px; - margin-right:5px; -} -.custom-file-upload { - display: inline-block; - padding: 6px 12px; - cursor: pointer; -} - -.align_center{ - text-align: center; -} - -ul.action-buttons{ - margin-top: 40px; - list-style: none; - margin-left: 0; - padding-left:0; -} -li { - list-style: none; - margin-left: 0; - padding-left:0; - /*margin-bottom:20px;*/ -} - -.btn { - display: inline-block; - color: #333; - background-color: #fff; - border: 1px solid #adadad; - font-family: Helvetica, sans-serif; - padding: 6px 12px; - margin: 0; - font-size: 14px; - font-weight: 400; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - border-radius: 4px; - text-decoration: none; - transition: all 0.15s; -} -.desktop .btn:hover { - background-color: #fff; -} -.btn.btn-lg { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn.btn-sm { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn.btn-important { - border-width: 2px; - border-color: #444; -} -#photo_area { - position: relative; - cursor: pointer; -} -#restart_btn { - position: relative; - cursor: pointer; -} -input[type=file] { - cursor: pointer; - opacity: 0; - position: absolute; - top: 0; left: 0; - width: 100%; height: 100%; -} -.desktop #photo_area .btn { - background: #fff; -} -.desktop #photo_area .btn:hover { - background: #eee; -} -.consent_box { - margin-top: 10px; - font-size: smaller; - color: #444; -} - -/* Intro page --------------------------------------------------- */ -canvas { - display: block; -} -.photo { - display: none; - width:100%; - height:100%; -} - -/* form visibility */ - -#preloader_anim { - display: none; -} -#about_btn { - margin: 20px 0px 80px 0; -} -#share_btns { - display: none; - margin:20px; -} -.notice { - color: #444; - font-size: small; -} -a.btn-default { - background-color: transparent; -} -a.btn-default:hover { - color: #333; - background-color: #fff; - border-color: #adadad; -} -.debug-view { - margin-bottom: 20px; - font-size:14px; - color:#333; -} -.debug-view img { - margin-bottom:4px; -} -#full_results, #hide_more { - display: none; -} - -select { - display: inline-block; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.428571429; - color: #555; - vertical-align: middle; - background-color: #eee; - background-image: none; - border: 1px solid #bbb; - border-radius: 4px; - transition: all .15s; - cursor: pointer; -} -.desktop select:hover { - background-color: #fff; - cursor: pointer; -} - -/* About ---------------------------------------------------- */ -.modal { - pointer-events: none; - opacity: 0; - width: 100%; - height: 100%; - position: fixed; - top: 0; left: 0; - transition: all 0.2s; -} -.modal.visible { - pointer-events: auto; - opacity: 1; -} -.modal p { - font-size: 16px; - line-height: 24px; -} -.modal .inner { - margin: 10vh auto; - background: rgba(255,255,255,0.5); - padding: 20px 20px 40px 20px; - width: 600px; - max-width: 90vw; -} -.modal .content { - margin-bottom: 20px; -} - -/* Result ---------------------------------------------------- */ -.result_view { - display: none; - text-align: center; -} -.final_result img { - width: 512px; - height: 512px; - border: 1px dashed #000; - margin: 10px; -} -.all_results { - display: none; -} -.all_results div { - margin-bottom: 20px; -} -.all_results img { - width: 384px; - height: 384px; - margin: 10px; -} - -.made_with { - margin-bottom: 10px; -} -#delete_btns { - margin-top: 10px; - font-size: 10px; -} -a#destroy_data { - color: #888; -} -.desktop a#destroy_data:hover { - color: #f00; -} - -/* Footer ---------------------------------------------------- */ -#footer{ - /*background: #ddd;*/ - /*padding: 20px 0;*/ - /*position: fixed;*/ - bottom: 0; - width: 100%; - text-align: center; - padding-top: 40px; - padding-bottom: 20px; -} -#footer ul li a { - font-weight: bold; - text-decoration: none; -} -#footer a { - color:#333; - text-decoration: underline; -} -#footer a:hover { - color:#111; -} - -@media screen and (max-width: 500px) { - .modal { - width: 100vw; - height: 100vh; - display: flex; - align-items: center; - justify-content: center; - } - .modal .inner { - margin: 0; - width: 100vw; - height: 100vh; - max-width: 100vw; - } -} - - -/* Gallery */ - -.gallery-preview-row{ - margin-bottom: 40px -} -.gallery-preview-row img{ - width:100%; - padding:2px; -} - -@media (max-width: 600px) { - #preloader_anim { - position: relative; - top: -50px; - } -} - -/*hide dropdown*/ -#dropdown_btn{ - display: none; -} \ No newline at end of file diff --git a/server/app/static/css/projector.css b/server/app/static/css/projector.css deleted file mode 100644 index 401f0dff..00000000 --- a/server/app/static/css/projector.css +++ /dev/null @@ -1,52 +0,0 @@ -html, body, #wrapper { - height:100%; - width: 100%; - margin: 0; - padding: 0; - border: 0; - background-color: #000; -} -table{ - padding:0; - margin:0; - border-spacing: 0; -} -table td{ - padding:0; - margin:0; -} -#wrapper td { - vertical-align: middle; - text-align: center; -} -#wrapper img{ - margin-top:-350px; - margin-left:-350px; -} -.left{ - background-color: #000; -} -.right{ - background-color: #000;; -} - -#container{ -} -#container-left{ - width:50%; - float:left; - position: relative; -} -#container-right{ - width:50%; - float:right; - position: relative; -} - -.cycle{position:relative;display: none;} -.cycle img{position:absolute;z-index:1} -.cycle img.active{z-index:100} -.cycle img{ - width:700px; - height:700px; -} \ No newline at end of file diff --git a/server/app/static/js/app.js b/server/app/static/js/app.js deleted file mode 100644 index 454d5c37..00000000 --- a/server/app/static/js/app.js +++ /dev/null @@ -1,158 +0,0 @@ -var app = (function(){ - - var app = {} - - app.init = function(){ - upload.init() - app.bind() - app.build() - app.resize() - app.route() - } - app.bind = function(){ - $(window).on('resize', app.resize) - $(".about_button").on('click', app.about_show) - $(".privacy_button").on('click', app.privacy_show) - $(".modal").on('click', app.modal_hide) - $(".modal .btn").on('click', app.modal_hide) - $(".modal .inner").on('click', preventDefault) - $("#destroy_data").on('click', app.destroyData) - $("#show_all_results").on('click', app.showAllResults) - } - app.build = function(){ - var items = JSON.parse(decodeEntities($("#dropdown_options").html())) - var $dropdown = $("#dropdown") - var options = Object.keys(items).sort().map(key => { - var item = items[key] - var option = document.createElement('option') - option.value = item.name - option.innerHTML = item.title - if (item.selected) option.selected = true - $dropdown.append(option) - }) - var loader = new Image () - loader.src = '/static/img/loader.gif' - } - app.resize = function(){ - var $el = $('#photo_area') - var w = $el.width() - $el.height($el.width()) - } - app.route = function(){ - const path = window.location.pathname.split('/') - path.shift() - switch (path[0]) { - case 'd': - app.processingComplete(path[1], true) // public - break - case 'p': - app.processingComplete(path[1], false) // private - break - case 'about': - app.about_show() - break - case 'privacy': - app.privacy_show() - break - default: - // load index, default state - break - } - } - - /* upload UI changes */ - - app.didPickPhoto = function(){ - $('#upload_controls').fadeIn() - $('#user_photo_canvas').show() - $('#take_photo_btn').hide() - } - app.didClickUpload = function(){ - $('#upload_controls').slideUp('fast') - $('#user_photo_canvas').hide() - $('#preloader_anim').fadeIn('fast') - $('#progress').fadeIn() - } - app.uploadDidComplete = function(){ - $('#preloader_anim').hide() - $('#progress').hide() - } - app.uploadDidComplete = function(){ - // $('#preloader_anim').hide() - // $('#progress').hide() - } - app.updateProgress = function(message, percentage){ - message = message || "Processing..." - percentage = percentage || 0 - $("#progress").html(message) - } - app.processingComplete = function(uuid, is_public){ - $('#preloader_anim').hide() - $('#progress').hide() - // - $("header h2").html("Your dull result") - $(".upload_view").hide() - $(".results_view").show() - var endpoint = is_public ? 'json' : 'json_private' - $.getJSON('/static/media/' + endpoint + '/' + uuid + '.json', function(data){ - console.log(data) - var template = $("#result_template").html() - var final_result = new Image - final_result.src = data.files[data.files.length-1].fn - $(".final_result").empty() - $(".all_results").empty() - $(".final_result").append(final_result) - data.files.forEach(function(file){ - var t = template.replace(/{img}/, file.fn).replace(/{title}/, file.title) - $(".all_results").append(t) - }) - $(".result_view").show() - $(".permalink").attr('href', window.location.href) - }).fail(function(){ - console.log('error fetching json') - window.location.href = '/' - }) - } - var detailed = false - app.showAllResults = function(){ - if (!detailed) { - detailed = true - $(this).html('Hide') - $(".all_results").fadeIn('fast') - } else { - detailed = false - $(this).html('Detailed Analysis') - $(".all_results").slideUp('fast') - } - } - app.destroyData = function(){ - var uuid = window.location.pathname.split('/')[2] - var confirmed = confirm("Do you really want to delete your dull dream?") - if (confirmed) { - $.get( [window.location.pathname, 'destroy'].join('/').replace('//', '/') ).always(function(){ - alert('Dull dream deleted!') - window.location.href = '/' - }) - } - } - - /* modals */ - - app.about_show = function(e){ - e.preventDefault() - $(".about_view").addClass('visible') - } - app.privacy_show = function(e){ - e.preventDefault() - $(".privacy_view").addClass('visible') - } - app.modal_hide = function(e){ - e.preventDefault() - e.stopPropagation() - $(".modal").removeClass('visible') - } - - document.addEventListener('DOMContentLoaded', app.init) - - return app -})() \ No newline at end of file diff --git a/server/app/static/js/upload.js b/server/app/static/js/upload.js deleted file mode 100644 index 27437e43..00000000 --- a/server/app/static/js/upload.js +++ /dev/null @@ -1,319 +0,0 @@ -var messages = { - is_processing: "Running semantic segmentation...", - upload_failed: "Error attempting to upload the file.", - upload_cancelled: "Upload cancelled or browser dropped connection.", - unable_to_compute: "We're sorry! We were unable to compute your image.", - pending: "Sending to Generative Adversarial Network...", - complete: "Processing complete!", -} - -var upload = (function(){ - var upload = {} - var uploading = false - - var MAX_SIDE = 512 - - upload.init = function(){ - upload.bind() - } - - upload.bind = function(){ - $("input[type=file]").on('change', upload.change) - $("#upload_btn").on('click', upload.go) - document.body.addEventListener("dragover", upload.dragover) - document.body.addEventListener("dragleave", upload.dragover) - document.body.addEventListener("drop", upload.change) - } - - upload.dragover = function(e){ - e.stopPropagation() - e.preventDefault() - } - - upload.change = function(e){ - e.preventDefault() - var files = e.dataTransfer ? e.dataTransfer.files : e.target.files - if (files.length) { - var file = files[files.length - 1] - if (!file.type.match('image.*')) - return - var reader = new FileReader() - reader.onload = onReaderLoad - reader.readAsDataURL(file) - } - function onReaderLoad(e) { - // Don't leak! - reader.onload = null - var img = new Image - img.onload = function(){ - img.onload = null - upload.ready(img) - } - img.src = e.target.result - } - } - - upload.ready = function(img){ - var resized = renderToCanvas(img, { correctOrientation: true }) - var canvas = document.querySelector('#user_photo_canvas') - ctx = canvas.getContext('2d') - ctx.fillStyle = 'black' - ctx.fillRect(0, 0, MAX_SIDE, MAX_SIDE) - var x_offset = (MAX_SIDE - resized.width) / 2 - var y_offset = (MAX_SIDE - resized.height) / 2 - - ctx.drawImage(resized, x_offset, y_offset) - app.didPickPhoto() - } - - upload.go = function(){ - if (uploading) return - uploading = true - app.didClickUpload() - try { - var canvas = document.querySelector('#user_photo_canvas') - var cb = canvas.toBlob(function(blob){ - upload.send(blob) - }, 'image/jpeg', 89) - } catch(e){ - app.updateProgress(messages.unable_to_compute) - } - } - - upload.send = function(blob){ - console.log("sending upload...") - var fd = new FormData() - fd.append('user_image', blob) - fd.append('ext', 'jpg') - fd.append('style', $("#dropdown").val()) - fd.append('agree', $("#agree").val() || 0) - - var xhr = new XMLHttpRequest() - xhr.upload.addEventListener("progress", upload.progress, false) - xhr.addEventListener("load", upload.complete, false) - xhr.addEventListener("error", upload.failed, false) - xhr.addEventListener("abort", upload.canceled, false) - xhr.open("POST", "/upload") - xhr.send(fd) - } - - upload.progress = function (e) { - if (e.lengthComputable) { - var percentComplete = Math.round(e.loaded * 100 / e.total) - if (percentComplete > 99) { - app.updateProgress(messages.is_processing) - } else { - app.updateProgress("Uploaded " + percentComplete.toString() + '%') - } - } - else { - app.updateProgress(messages.unable_to_compute) - } - } - - upload.complete = function (e) { - uploading = false - try { - var data = JSON.parse(e.target.responseText) - } catch (e) { - return app.updateProgress(messages.upload_failed) - } - app.uploadDidComplete() - upload.data = data - upload.task_progress(data.task_url) - } - - upload.failed = function (evt) { - uploading = false - app.updateProgress(messages.upload_failed) - } - - upload.cancelled = function (evt) { - uploading = false - app.updateProgress(messages.upload_cancelled) - } - - upload.task_progress = function (status_url) { - var is_public = $("#agree").val() || 0 - var uuid = upload.data.uuid - $.getJSON(status_url, function(data){ - console.log(data) - var alive = true - var delay = 500 - switch(data.state) { - case 'PENDING': - app.updateProgress(messages.pending) - delay = 2000 - break - case 'PROCESSING': - app.updateProgress(data.message, data.percent) - delay = 500 - break - case 'SUCCESS': - app.updateProgress(messages.complete) - if (is_public) { - history.pushState({}, 'DullDream', '/d/' + uuid) - } else { - history.pushState({}, 'DullDream', '/p/' + uuid) - } - app.processingComplete(uuid, is_public) // truthy if private - alive = false - break - default: - // NB: error state - alive = false - break - } - if (alive) { - setTimeout(function() { - upload.task_progress(status_url) - }, delay) - } - }) - } - - - function renderToCanvas(img, options) { - if (!img) return - options = options || {} - - // Canvas max size for any side - var maxSize = MAX_SIDE - var canvas = document.createElement('canvas') - var ctx = canvas.getContext('2d') - var initialScale = options.scale || 1 - // Scale to needed to constrain canvas to max size - var scale = getScale(img.width * initialScale, img.height * initialScale, maxSize, maxSize, true) - // Still need to apply the user defined scale - scale *= initialScale - var width = canvas.width = Math.round(img.width * scale) - var height = canvas.height = Math.round(img.height * scale) - var correctOrientation = options.correctOrientation - var jpeg = !!img.src.match(/data:image\/jpeg|\.jpeg$|\.jpg$/i) - var hasDataURI = !!img.src.match(/^data:/) - - ctx.save() - - // Can only correct orientation on JPEGs represented as dataURIs - // for the time being - if (correctOrientation && jpeg && hasDataURI) { - applyOrientationCorrection(canvas, ctx, img.src) - } - // Resize image if too large - if (scale !== 1) { - ctx.scale(scale, scale) - } - - ctx.drawImage(img, 0, 0) - ctx.restore() - - return canvas - } - - function getScale(width, height, viewportWidth, viewportHeight, fillViewport) { - fillViewport = !!fillViewport - var landscape = (width / height) > (viewportWidth / viewportHeight) - if (landscape) { - if (fillViewport) { - return fitVertical() - } else if (width > viewportWidth) { - return fitHorizontal() - } - } else { - if (fillViewport) { - return fitHorizontal() - } else if (height > viewportHeight) { - return fitVertical() - } - } - return 1 - - function fitHorizontal() { - return viewportWidth / width - } - - function fitVertical() { - return viewportHeight / height - } - } - - function applyOrientationCorrection(canvas, ctx, uri) { - var orientation = getOrientation(uri) - // Only apply transform if there is some non-normal orientation - if (orientation && orientation !== 1) { - var transform = orientationToTransform[orientation] - var rotation = transform.rotation - var mirror = transform.mirror - var flipAspect = rotation === 90 || rotation === 270 - if (flipAspect) { - // Fancy schmancy swap algo - canvas.width = canvas.height + canvas.width - canvas.height = canvas.width - canvas.height - canvas.width -= canvas.height - } - if (rotation > 0) { - applyRotation(canvas, ctx, rotation) - } - } - } - - function applyRotation(canvas, ctx, deg) { - var radians = deg * (Math.PI / 180) - if (deg === 90) { - ctx.translate(canvas.width, 0) - } else if (deg === 180) { - ctx.translate(canvas.width, canvas.height) - } else if (deg == 270) { - ctx.translate(0, canvas.height) - } - ctx.rotate(radians) - } - - function getOrientation (uri) { - var exif = new ExifReader - // Split off the base64 data - var base64String = uri.split(',')[1] - // Read off first 128KB, which is all we need to - // get the EXIF data - var arr = base64ToUint8Array(base64String, 0, Math.pow(2, 17)) - try { - exif.load(arr.buffer) - return exif.getTagValue('Orientation') - } catch (err) { - return 1 - } - } - - function base64ToUint8Array(string, start, finish) { - var start = start || 0 - var finish = finish || string.length - // atob that shit - var binary = atob(string) - var buffer = new Uint8Array(binary.length) - for (var i = start; i < finish; i++) { - buffer[i] = binary.charCodeAt(i) - } - return buffer - } - - /** - * Mapping from EXIF orientation values to data - * regarding the rotation and mirroring necessary to - * render the canvas correctly - * Derived from: - * http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ - */ - var orientationToTransform = { - 1: { rotation: 0, mirror: false }, - 2: { rotation: 0, mirror: true }, - 3: { rotation: 180, mirror: false }, - 4: { rotation: 180, mirror: true }, - 5: { rotation: 90, mirror: true }, - 6: { rotation: 90, mirror: false }, - 7: { rotation: 270, mirror: true }, - 8: { rotation: 270, mirror: false } - } - - - return upload -})() \ No newline at end of file diff --git a/server/app/static/js/util.js b/server/app/static/js/util.js deleted file mode 100644 index 851f634a..00000000 --- a/server/app/static/js/util.js +++ /dev/null @@ -1,32 +0,0 @@ -var is_iphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) -var is_ipad = (navigator.userAgent.match(/iPad/i)) -var is_android = (navigator.userAgent.match(/Android/i)) -var is_mobile = is_iphone || is_ipad || is_android -var is_desktop = ! is_mobile; - -document.body.parentNode.classList.add(is_desktop ? 'desktop' : 'mobile') - -function preventDefault(e){ - e.preventDefault() - e.stopPropagation() -} - -var decodeEntities = (function() { - // this prevents any overhead from creating the object each time - var element = document.createElement('div'); - - function decodeHTMLEntities (str) { - if(str && typeof str === 'string') { - // strip script/html tags - str = str.replace(/]*>([\S\s]*?)<\/script>/gmi, ''); - str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, ''); - element.innerHTML = str; - str = element.textContent; - element.textContent = ''; - } - - return str; - } - - return decodeHTMLEntities; -})(); \ No newline at end of file diff --git a/server/app/static/js/vendor/ExifReader.js b/server/app/static/js/vendor/ExifReader.js deleted file mode 100644 index a8343ede..00000000 --- a/server/app/static/js/vendor/ExifReader.js +++ /dev/null @@ -1,1363 +0,0 @@ -// Generated by CoffeeScript 1.6.2 -/* -# ExifReader 1.1.1 -# http://github.com/mattiasw/exifreader -# Copyright (C) 2011-2014 Mattias Wallander -# Licensed under the GNU Lesser General Public License version 3 or later -# See license text at http://www.gnu.org/licenses/lgpl.txt -*/ - - -(function() { - (typeof exports !== "undefined" && exports !== null ? exports : this).ExifReader = (function() { - ExifReader.prototype._MIN_DATA_BUFFER_LENGTH = 2; - - ExifReader.prototype._JPEG_ID_SIZE = 2; - - ExifReader.prototype._JPEG_ID = 0xffd8; - - ExifReader.prototype._APP_MARKER_SIZE = 2; - - ExifReader.prototype._APP0_MARKER = 0xffe0; - - ExifReader.prototype._APP1_MARKER = 0xffe1; - - ExifReader.prototype._APP15_MARKER = 0xffef; - - ExifReader.prototype._APP_ID_OFFSET = 4; - - ExifReader.prototype._BYTES_Exif = 0x45786966; - - ExifReader.prototype._TIFF_HEADER_OFFSET = 10; - - ExifReader.prototype._BYTE_ORDER_BIG_ENDIAN = 0x4949; - - ExifReader.prototype._BYTE_ORDER_LITTLE_ENDIAN = 0x4d4d; - - function ExifReader() { - var _this = this; - - this._getTagValueAt = { - 1: function(offset) { - return _this._getByteAt(offset); - }, - 2: function(offset) { - return _this._getAsciiAt(offset); - }, - 3: function(offset) { - return _this._getShortAt(offset); - }, - 4: function(offset) { - return _this._getLongAt(offset); - }, - 5: function(offset) { - return _this._getRationalAt(offset); - }, - 7: function(offset) { - return _this._getUndefinedAt(offset); - }, - 9: function(offset) { - return _this._getSlongAt(offset); - }, - 10: function(offset) { - return _this._getSrationalAt(offset); - } - }; - this._tiffHeaderOffset = 0; - } - - /* - # Loads all the Exif tags from the specified image file buffer. - # - # data ArrayBuffer Image file data - */ - - - ExifReader.prototype.load = function(data) { - return this.loadView(new DataView(data)); - }; - - /* - # Loads all the Exif tags from the specified image file buffer view. Probably - # used when DataView isn't supported by the browser. - # - # @_dataView DataView Image file data view - */ - - - ExifReader.prototype.loadView = function(_dataView) { - this._dataView = _dataView; - this._tags = {}; - this._checkImageHeader(); - this._readTags(); - return this._dataView = null; - }; - - ExifReader.prototype._checkImageHeader = function() { - if (this._dataView.byteLength < this._MIN_DATA_BUFFER_LENGTH || this._dataView.getUint16(0, false) !== this._JPEG_ID) { - throw new Error('Invalid image format'); - } - this._parseAppMarkers(this._dataView); - if (!this._hasExifData()) { - throw new Error('No Exif data'); - } - }; - - ExifReader.prototype._parseAppMarkers = function(dataView) { - var appMarkerPosition, fieldLength, _results; - - appMarkerPosition = this._JPEG_ID_SIZE; - _results = []; - while (true) { - if (dataView.byteLength < appMarkerPosition + this._APP_ID_OFFSET + 5) { - break; - } - if (this._isApp1ExifMarker(dataView, appMarkerPosition)) { - fieldLength = dataView.getUint16(appMarkerPosition + this._APP_MARKER_SIZE, false); - this._tiffHeaderOffset = appMarkerPosition + this._TIFF_HEADER_OFFSET; - } else if (this._isAppMarker(dataView, appMarkerPosition)) { - fieldLength = dataView.getUint16(appMarkerPosition + this._APP_MARKER_SIZE, false); - } else { - break; - } - _results.push(appMarkerPosition += this._APP_MARKER_SIZE + fieldLength); - } - return _results; - }; - - ExifReader.prototype._isApp1ExifMarker = function(dataView, appMarkerPosition) { - return dataView.getUint16(appMarkerPosition, false) === this._APP1_MARKER && dataView.getUint32(appMarkerPosition + this._APP_ID_OFFSET, false) === this._BYTES_Exif && dataView.getUint8(appMarkerPosition + this._APP_ID_OFFSET + 4, false) === 0x00; - }; - - ExifReader.prototype._isAppMarker = function(dataView, appMarkerPosition) { - var appMarker; - - appMarker = dataView.getUint16(appMarkerPosition, false); - return appMarker >= this._APP0_MARKER && appMarker <= this._APP15_MARKER; - }; - - ExifReader.prototype._hasExifData = function() { - return this._tiffHeaderOffset !== 0; - }; - - ExifReader.prototype._readTags = function() { - this._setByteOrder(); - this._read0thIfd(); - this._readExifIfd(); - this._readGpsIfd(); - return this._readInteroperabilityIfd(); - }; - - ExifReader.prototype._setByteOrder = function() { - if (this._dataView.getUint16(this._tiffHeaderOffset) === this._BYTE_ORDER_BIG_ENDIAN) { - return this._littleEndian = true; - } else if (this._dataView.getUint16(this._tiffHeaderOffset) === this._BYTE_ORDER_LITTLE_ENDIAN) { - return this._littleEndian = false; - } else { - throw new Error('Illegal byte order value. Faulty image.'); - } - }; - - ExifReader.prototype._read0thIfd = function() { - var ifdOffset; - - ifdOffset = this._getIfdOffset(); - return this._readIfd('0th', ifdOffset); - }; - - ExifReader.prototype._getIfdOffset = function() { - return this._tiffHeaderOffset + this._getLongAt(this._tiffHeaderOffset + 4); - }; - - ExifReader.prototype._readExifIfd = function() { - var ifdOffset; - - if (this._tags['Exif IFD Pointer'] != null) { - ifdOffset = this._tiffHeaderOffset + this._tags['Exif IFD Pointer'].value; - return this._readIfd('exif', ifdOffset); - } - }; - - ExifReader.prototype._readGpsIfd = function() { - var ifdOffset; - - if (this._tags['GPS Info IFD Pointer'] != null) { - ifdOffset = this._tiffHeaderOffset + this._tags['GPS Info IFD Pointer'].value; - return this._readIfd('gps', ifdOffset); - } - }; - - ExifReader.prototype._readInteroperabilityIfd = function() { - var ifdOffset; - - if (this._tags['Interoperability IFD Pointer'] != null) { - ifdOffset = this._tiffHeaderOffset + this._tags['Interoperability IFD Pointer'].value; - return this._readIfd('interoperability', ifdOffset); - } - }; - - ExifReader.prototype._readIfd = function(ifdType, offset) { - var fieldIndex, numberOfFields, tag, _i, _results; - - numberOfFields = this._getShortAt(offset); - offset += 2; - _results = []; - for (fieldIndex = _i = 0; 0 <= numberOfFields ? _i < numberOfFields : _i > numberOfFields; fieldIndex = 0 <= numberOfFields ? ++_i : --_i) { - tag = this._readTag(ifdType, offset); - if (tag !== void 0) { - this._tags[tag.name] = { - 'value': tag.value, - 'description': tag.description - }; - } - _results.push(offset += 12); - } - return _results; - }; - - ExifReader.prototype._readTag = function(ifdType, offset) { - var tagCode, tagCount, tagDescription, tagName, tagType, tagValue, tagValueOffset; - - tagCode = this._getShortAt(offset); - tagType = this._getShortAt(offset + 2); - tagCount = this._getLongAt(offset + 4); - if (this._typeSizes[tagType] === void 0) { - return void 0; - } - if (this._typeSizes[tagType] * tagCount <= 4) { - tagValue = this._getTagValue(offset + 8, tagType, tagCount); - } else { - tagValueOffset = this._getLongAt(offset + 8); - tagValue = this._getTagValue(this._tiffHeaderOffset + tagValueOffset, tagType, tagCount); - } - if (tagType === this._tagTypes['ASCII']) { - tagValue = this._splitNullSeparatedAsciiString(tagValue); - } - if (this._tagNames[ifdType][tagCode] != null) { - if ((this._tagNames[ifdType][tagCode]['name'] != null) && (this._tagNames[ifdType][tagCode]['description'] != null)) { - tagName = this._tagNames[ifdType][tagCode]['name']; - tagDescription = this._tagNames[ifdType][tagCode]['description'](tagValue); - } else { - tagName = this._tagNames[ifdType][tagCode]; - if (tagValue instanceof Array) { - tagDescription = tagValue.join(', '); - } else { - tagDescription = tagValue; - } - } - return { - 'name': tagName, - 'value': tagValue, - 'description': tagDescription - }; - } else { - return { - 'name': "undefined-" + tagCode, - 'value': tagValue, - 'description': tagValue - }; - } - }; - - ExifReader.prototype._getTagValue = function(offset, type, count) { - var tagValue, value, valueIndex; - - value = (function() { - var _i, _results; - - _results = []; - for (valueIndex = _i = 0; 0 <= count ? _i < count : _i > count; valueIndex = 0 <= count ? ++_i : --_i) { - tagValue = this._getTagValueAt[type](offset); - offset += this._typeSizes[type]; - _results.push(tagValue); - } - return _results; - }).call(this); - if (value.length === 1) { - value = value[0]; - } else if (type === this._tagTypes['ASCII']) { - value = this._getAsciiValue(value); - } - return value; - }; - - ExifReader.prototype._getAsciiValue = function(charArray) { - var charCode, newCharArray; - - return newCharArray = (function() { - var _i, _len, _results; - - _results = []; - for (_i = 0, _len = charArray.length; _i < _len; _i++) { - charCode = charArray[_i]; - _results.push(String.fromCharCode(charCode)); - } - return _results; - })(); - }; - - ExifReader.prototype._getByteAt = function(offset) { - return this._dataView.getUint8(offset); - }; - - ExifReader.prototype._getAsciiAt = function(offset) { - return this._dataView.getUint8(offset); - }; - - ExifReader.prototype._getShortAt = function(offset) { - return this._dataView.getUint16(offset, this._littleEndian); - }; - - ExifReader.prototype._getLongAt = function(offset) { - return this._dataView.getUint32(offset, this._littleEndian); - }; - - ExifReader.prototype._getRationalAt = function(offset) { - return this._getLongAt(offset) / this._getLongAt(offset + 4); - }; - - ExifReader.prototype._getUndefinedAt = function(offset) { - return this._getByteAt(offset); - }; - - ExifReader.prototype._getSlongAt = function(offset) { - return this._dataView.getInt32(offset, this._littleEndian); - }; - - ExifReader.prototype._getSrationalAt = function(offset) { - return this._getSlongAt(offset) / this._getSlongAt(offset + 4); - }; - - ExifReader.prototype._splitNullSeparatedAsciiString = function(string) { - var character, i, tagValue, _i, _len; - - tagValue = []; - i = 0; - for (_i = 0, _len = string.length; _i < _len; _i++) { - character = string[_i]; - if (character === '\x00') { - i++; - continue; - } - if (tagValue[i] == null) { - tagValue[i] = ''; - } - tagValue[i] += character; - } - return tagValue; - }; - - ExifReader.prototype._typeSizes = { - 1: 1, - 2: 1, - 3: 2, - 4: 4, - 5: 8, - 7: 1, - 9: 4, - 10: 8 - }; - - ExifReader.prototype._tagTypes = { - 'BYTE': 1, - 'ASCII': 2, - 'SHORT': 3, - 'LONG': 4, - 'RATIONAL': 5, - 'UNDEFINED': 7, - 'SLONG': 9, - 'SRATIONAL': 10 - }; - - ExifReader.prototype._tagNames = { - '0th': { - 0x0100: 'ImageWidth', - 0x0101: 'ImageLength', - 0x0102: 'BitsPerSample', - 0x0103: 'Compression', - 0x0106: 'PhotometricInterpretation', - 0x010e: 'ImageDescription', - 0x010f: 'Make', - 0x0110: 'Model', - 0x0111: 'StripOffsets', - 0x0112: { - 'name': 'Orientation', - 'description': function(value) { - switch (value) { - case 1: - return 'top-left'; - case 2: - return 'top-right'; - case 3: - return 'bottom-right'; - case 4: - return 'bottom-left'; - case 5: - return 'left-top'; - case 6: - return 'right-top'; - case 7: - return 'right-bottom'; - case 8: - return 'left-bottom'; - default: - return 'Undefined'; - } - } - }, - 0x0115: 'SamplesPerPixel', - 0x0116: 'RowsPerStrip', - 0x0117: 'StripByteCounts', - 0x011a: 'XResolution', - 0x011b: 'YResolution', - 0x011c: 'PlanarConfiguration', - 0x0128: { - 'name': 'ResolutionUnit', - 'description': function(value) { - switch (value) { - case 2: - return 'inches'; - case 3: - return 'centimeters'; - default: - return 'Unknown'; - } - } - }, - 0x012d: 'TransferFunction', - 0x0131: 'Software', - 0x0132: 'DateTime', - 0x013b: 'Artist', - 0x013e: 'WhitePoint', - 0x013f: 'PrimaryChromaticities', - 0x0201: 'JPEGInterchangeFormat', - 0x0202: 'JPEGInterchangeFormatLength', - 0x0211: 'YCbCrCoefficients', - 0x0212: 'YCbCrSubSampling', - 0x0213: { - 'name': 'YCbCrPositioning', - 'description': function(value) { - switch (value) { - case 1: - return 'centered'; - case 2: - return 'co-sited'; - default: - return 'undefied ' + value; - } - } - }, - 0x0214: 'ReferenceBlackWhite', - 0x8298: { - 'name': 'Copyright', - 'description': function(value) { - return value.join('; '); - } - }, - 0x8769: 'Exif IFD Pointer', - 0x8825: 'GPS Info IFD Pointer' - }, - 'exif': { - 0x829a: 'ExposureTime', - 0x829d: 'FNumber', - 0x8822: { - 'name': 'ExposureProgram', - 'description': function(value) { - switch (value) { - case 0: - return 'Undefined'; - case 1: - return 'Manual'; - case 2: - return 'Normal program'; - case 3: - return 'Aperture priority'; - case 4: - return 'Shutter priority'; - case 5: - return 'Creative program'; - case 6: - return 'Action program'; - case 7: - return 'Portrait mode'; - case 8: - return 'Landscape mode'; - default: - return 'Unknown'; - } - } - }, - 0x8824: 'SpectralSensitivity', - 0x8827: 'ISOSpeedRatings', - 0x8828: { - 'name': 'OECF', - 'description': function(value) { - return '[Raw OECF table data]'; - } - }, - 0x9000: { - 'name': 'ExifVersion', - 'description': function(value) { - var charCode, string, _i, _len; - - string = ''; - for (_i = 0, _len = value.length; _i < _len; _i++) { - charCode = value[_i]; - string += String.fromCharCode(charCode); - } - return string; - } - }, - 0x9003: 'DateTimeOriginal', - 0x9004: 'DateTimeDigitized', - 0x9101: { - 'name': 'ComponentsConfiguration', - 'description': function(value) { - var character, string, _i, _len; - - string = ''; - for (_i = 0, _len = value.length; _i < _len; _i++) { - character = value[_i]; - switch (character) { - case 0x31: - string += 'Y'; - break; - case 0x32: - string += 'Cb'; - break; - case 0x33: - string += 'Cr'; - break; - case 0x34: - string += 'R'; - break; - case 0x35: - string += 'G'; - break; - case 0x36: - string += 'B'; - } - } - return string; - } - }, - 0x9102: 'CompressedBitsPerPixel', - 0x9201: 'ShutterSpeedValue', - 0x9202: 'ApertureValue', - 0x9203: 'BrightnessValue', - 0x9204: 'ExposureBiasValue', - 0x9205: 'MaxApertureValue', - 0x9206: 'SubjectDistance', - 0x9207: { - 'name': 'MeteringMode', - 'description': function(value) { - switch (value) { - case 1: - return 'Average'; - case 2: - return 'CenterWeightedAverage'; - case 3: - return 'Spot'; - case 4: - return 'MultiSpot'; - case 5: - return 'Pattern'; - case 6: - return 'Partial'; - case 255: - return 'Other'; - default: - return 'Unknown'; - } - } - }, - 0x9208: { - 'name': 'LightSource', - 'description': function(value) { - switch (value) { - case 1: - return 'Daylight'; - case 2: - return 'Fluorescent'; - case 3: - return 'Tungsten (incandescent light)'; - case 4: - return 'Flash'; - case 9: - return 'Fine weather'; - case 10: - return 'Cloudy weather'; - case 11: - return 'Shade'; - case 12: - return 'Daylight fluorescent (D 5700 – 7100K)'; - case 13: - return 'Day white fluorescent (N 4600 – 5400K)'; - case 14: - return 'Cool white fluorescent (W 3900 – 4500K)'; - case 15: - return 'White fluorescent (WW 3200 – 3700K)'; - case 17: - return 'Standard light A'; - case 18: - return 'Standard light B'; - case 19: - return 'Standard light C'; - case 20: - return 'D55'; - case 21: - return 'D65'; - case 22: - return 'D75'; - case 23: - return 'D50'; - case 24: - return 'ISO studio tungsten'; - case 255: - return 'Other light source'; - default: - return 'Unknown'; - } - } - }, - 0x9209: { - 'name': 'Flash', - 'description': function(value) { - switch (value) { - case 0x00: - return 'Flash did not fire'; - case 0x01: - return 'Flash fired'; - case 0x05: - return 'Strobe return light not detected'; - case 0x07: - return 'Strobe return light detected'; - case 0x09: - return 'Flash fired, compulsory flash mode'; - case 0x0d: - return 'Flash fired, compulsory flash mode, return light not detected'; - case 0x0f: - return 'Flash fired, compulsory flash mode, return light detected'; - case 0x10: - return 'Flash did not fire, compulsory flash mode'; - case 0x18: - return 'Flash did not fire, auto mode'; - case 0x19: - return 'Flash fired, auto mode'; - case 0x1d: - return 'Flash fired, auto mode, return light not detected'; - case 0x1f: - return 'Flash fired, auto mode, return light detected'; - case 0x20: - return 'No flash function'; - case 0x41: - return 'Flash fired, red-eye reduction mode'; - case 0x45: - return 'Flash fired, red-eye reduction mode, return light not detected'; - case 0x47: - return 'Flash fired, red-eye reduction mode, return light detected'; - case 0x49: - return 'Flash fired, compulsory flash mode, red-eye reduction mode'; - case 0x4d: - return 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected'; - case 0x4f: - return 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected'; - case 0x59: - return 'Flash fired, auto mode, red-eye reduction mode'; - case 0x5d: - return 'Flash fired, auto mode, return light not detected, red-eye reduction mode'; - case 0x5f: - return 'Flash fired, auto mode, return light detected, red-eye reduction mode'; - default: - return 'Unknown'; - } - } - }, - 0x920a: 'FocalLength', - 0x9214: { - 'name': 'SubjectArea', - 'description': function(value) { - switch (value.length) { - case 2: - return "Location; X: " + value[0] + ", Y: " + value[1]; - case 3: - return "Circle; X: " + value[0] + ", Y: " + value[1] + ", diameter: " + value[2]; - case 4: - return "Rectangle; X: " + value[0] + ", Y: " + value[1] + ", width: " + value[2] + ", height: " + value[3]; - default: - return 'Unknown'; - } - } - }, - 0x927c: { - 'name': 'MakerNote', - 'description': function(value) { - return '[Raw maker note data]'; - } - }, - 0x9286: { - 'name': 'UserComment', - 'description': function(value) { - switch (value.slice(0, 8).map(function(charCode) { - return String.fromCharCode(charCode); - }).join('')) { - case 'ASCII\x00\x00\x00': - return value.slice(8, value.length).map(function(charCode) { - return String.fromCharCode(charCode); - }).join(''); - case 'JIS\x00\x00\x00\x00\x00': - return '[JIS encoded text]'; - case 'UNICODE\x00': - return '[Unicode encoded text]'; - case '\x00\x00\x00\x00\x00\x00\x00\x00': - return '[Undefined encoding]'; - } - } - }, - 0x9290: 'SubSecTime', - 0x9291: 'SubSecTimeOriginal', - 0x9292: 'SubSecTimeDigitized', - 0xa000: { - 'name': 'FlashpixVersion', - 'description': function(value) { - var charCode, string, _i, _len; - - string = ''; - for (_i = 0, _len = value.length; _i < _len; _i++) { - charCode = value[_i]; - string += String.fromCharCode(charCode); - } - return string; - } - }, - 0xa001: { - 'name': 'ColorSpace', - 'description': function(value) { - switch (value) { - case 1: - return 'sRGB'; - case 0xffff: - return 'Uncalibrated'; - default: - return 'Unknown'; - } - } - }, - 0xa002: 'PixelXDimension', - 0xa003: 'PixelYDimension', - 0xa004: 'RelatedSoundFile', - 0xa005: 'Interoperability IFD Pointer', - 0xa20b: 'FlashEnergy', - 0xa20c: { - 'name': 'SpatialFrequencyResponse', - 'description': function(value) { - return '[Raw SFR table data]'; - } - }, - 0xa20e: 'FocalPlaneXResolution', - 0xa20f: 'FocalPlaneYResolution', - 0xa210: { - 'name': 'FocalPlaneResolutionUnit', - 'description': function(value) { - switch (value) { - case 2: - return 'inches'; - case 3: - return 'centimeters'; - default: - return 'Unknown'; - } - } - }, - 0xa214: { - 'name': 'SubjectLocation', - 'description': function(value) { - return "X: " + value[0] + ", Y: " + value[1]; - } - }, - 0xa215: 'ExposureIndex', - 0xa217: { - 'name': 'SensingMethod', - 'description': function(value) { - switch (value) { - case 1: - return 'Undefined'; - case 2: - return 'One-chip color area sensor'; - case 3: - return 'Two-chip color area sensor'; - case 4: - return 'Three-chip color area sensor'; - case 5: - return 'Color sequential area sensor'; - case 7: - return 'Trilinear sensor'; - case 8: - return 'Color sequential linear sensor'; - default: - return 'Unknown'; - } - } - }, - 0xa300: { - 'name': 'FileSource', - 'description': function(value) { - switch (value) { - case 3: - return 'DSC'; - default: - return 'Unknown'; - } - } - }, - 0xa301: { - 'name': 'SceneType', - 'description': function(value) { - switch (value) { - case 1: - return 'A directly photographed image'; - default: - return 'Unknown'; - } - } - }, - 0xa302: { - 'name': 'CFAPattern', - 'description': function(value) { - return '[Raw CFA pattern table data]'; - } - }, - 0xa401: { - 'name': 'CustomRendered', - 'description': function(value) { - switch (value) { - case 0: - return 'Normal process'; - case 1: - return 'Custom process'; - default: - return 'Unknown'; - } - } - }, - 0xa402: { - 'name': 'ExposureMode', - 'description': function(value) { - switch (value) { - case 0: - return 'Auto exposure'; - case 1: - return 'Manual exposure'; - case 2: - return 'Auto bracket'; - default: - return 'Unknown'; - } - } - }, - 0xa403: { - 'name': 'WhiteBalance', - 'description': function(value) { - switch (value) { - case 0: - return 'Auto white balance'; - case 1: - return 'Manual white balance'; - default: - return 'Unknown'; - } - } - }, - 0xa404: { - 'name': 'DigitalZoomRatio', - 'description': function(value) { - switch (value) { - case 0: - return 'Digital zoom was not used'; - default: - return value; - } - } - }, - 0xa405: { - 'name': 'FocalLengthIn35mmFilm', - 'description': function(value) { - switch (value) { - case 0: - return 'Unknown'; - default: - return value; - } - } - }, - 0xa406: { - 'name': 'SceneCaptureType', - 'description': function(value) { - switch (value) { - case 0: - return 'Standard'; - case 1: - return 'Landscape'; - case 2: - return 'Portrait'; - case 3: - return 'Night scene'; - default: - return 'Unknown'; - } - } - }, - 0xa407: { - 'name': 'GainControl', - 'description': function(value) { - switch (value) { - case 0: - return 'None'; - case 1: - return 'Low gain up'; - case 2: - return 'High gain up'; - case 3: - return 'Low gain down'; - case 4: - return 'High gain down'; - default: - return 'Unknown'; - } - } - }, - 0xa408: { - 'name': 'Contrast', - 'description': function(value) { - switch (value) { - case 0: - return 'Normal'; - case 1: - return 'Soft'; - case 2: - return 'Hard'; - default: - return 'Unknown'; - } - } - }, - 0xa409: { - 'name': 'Saturation', - 'description': function(value) { - switch (value) { - case 0: - return 'Normal'; - case 1: - return 'Low saturation'; - case 2: - return 'High saturation'; - default: - return 'Unknown'; - } - } - }, - 0xa40a: { - 'name': 'Sharpness', - 'description': function(value) { - switch (value) { - case 0: - return 'Normal'; - case 1: - return 'Soft'; - case 2: - return 'Hard'; - default: - return 'Unknown'; - } - } - }, - 0xa40b: { - 'name': 'DeviceSettingDescription', - 'description': function(value) { - return '[Raw device settings table data]'; - } - }, - 0xa40c: { - 'name': 'SubjectDistanceRange', - 'description': function(value) { - switch (value) { - case 1: - return 'Macro'; - case 2: - return 'Close view'; - case 3: - return 'Distant view'; - default: - return 'Unknown'; - } - } - }, - 0xa420: 'ImageUniqueID' - }, - 'gps': { - 0x0000: { - 'name': 'GPSVersionID', - 'description': function(value) { - var _ref, _ref1; - - if ((value[0] === (_ref = value[1]) && _ref === 2) && (value[2] === (_ref1 = value[3]) && _ref1 === 0)) { - return 'Version 2.2'; - } else { - return 'Unknown'; - } - } - }, - 0x0001: { - 'name': 'GPSLatitudeRef', - 'description': function(value) { - switch (value.join('')) { - case 'N': - return 'North latitude'; - case 'S': - return 'South latitude'; - default: - return 'Unknown'; - } - } - }, - 0x0002: { - 'name': 'GPSLatitude', - 'description': function(value) { - return value[0] + value[1] / 60 + value[2] / 3600; - } - }, - 0x0003: { - 'name': 'GPSLongitudeRef', - 'description': function(value) { - switch (value.join('')) { - case 'E': - return 'East longitude'; - case 'W': - return 'West longitude'; - default: - return 'Unknown'; - } - } - }, - 0x0004: { - 'name': 'GPSLongitude', - 'description': function(value) { - return value[0] + value[1] / 60 + value[2] / 3600; - } - }, - 0x0005: { - 'name': 'GPSAltitudeRef', - 'description': function(value) { - switch (value) { - case 0: - return 'Sea level'; - case 1: - return 'Sea level reference (negative value)'; - default: - return 'Unknown'; - } - } - }, - 0x0006: { - 'name': 'GPSAltitude', - 'description': function(value) { - return value + ' m'; - } - }, - 0x0007: { - 'name': 'GPSTimeStamp', - 'description': function(value) { - var padZero; - - padZero = function(num) { - var i; - - return ((function() { - var _i, _ref, _results; - - _results = []; - for (i = _i = 0, _ref = 2 - ('' + Math.floor(num)).length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - _results.push('0'); - } - return _results; - })()) + num; - }; - return value.map(padZero).join(':'); - } - }, - 0x0008: 'GPSSatellites', - 0x0009: { - 'name': 'GPSStatus', - 'description': function(value) { - switch (value.join('')) { - case 'A': - return 'Measurement in progress'; - case 'V': - return 'Measurement Interoperability'; - default: - return 'Unknown'; - } - } - }, - 0x000a: { - 'name': 'GPSMeasureMode', - 'description': function(value) { - switch (value.join('')) { - case '2': - return '2-dimensional measurement'; - case '3': - return '3-dimensional measurement'; - default: - return 'Unknown'; - } - } - }, - 0x000b: 'GPSDOP', - 0x000c: { - 'name': 'GPSSpeedRef', - 'description': function(value) { - switch (value.join('')) { - case 'K': - return 'Kilometers per hour'; - case 'M': - return 'Miles per hour'; - case 'N': - return 'Knots'; - default: - return 'Unknown'; - } - } - }, - 0x000d: 'GPSSpeed', - 0x000e: { - 'name': 'GPSTrackRef', - 'description': function(value) { - switch (value.join('')) { - case 'T': - return 'True direction'; - case 'M': - return 'Magnetic direction'; - default: - return 'Unknown'; - } - } - }, - 0x000f: 'GPSTrack', - 0x0010: { - 'name': 'GPSImgDirectionRef', - 'description': function(value) { - switch (value.join('')) { - case 'T': - return 'True direction'; - case 'M': - return 'Magnetic direction'; - default: - return 'Unknown'; - } - } - }, - 0x0011: 'GPSImgDirection', - 0x0012: 'GPSMapDatum', - 0x0013: { - 'name': 'GPSDestLatitudeRef', - 'description': function(value) { - switch (value.join('')) { - case 'N': - return 'North latitude'; - case 'S': - return 'South latitude'; - default: - return 'Unknown'; - } - } - }, - 0x0014: { - 'name': 'GPSDestLatitude', - 'description': function(value) { - return value[0] + value[1] / 60 + value[2] / 3600; - } - }, - 0x0015: { - 'name': 'GPSDestLongitudeRef', - 'description': function(value) { - switch (value.join('')) { - case 'E': - return 'East longitude'; - case 'W': - return 'West longitude'; - default: - return 'Unknown'; - } - } - }, - 0x0016: { - 'name': 'GPSDestLongitude', - 'description': function(value) { - return value[0] + value[1] / 60 + value[2] / 3600; - } - }, - 0x0017: { - 'name': 'GPSDestBearingRef', - 'description': function(value) { - switch (value.join('')) { - case 'T': - return 'True direction'; - case 'M': - return 'Magnetic direction'; - default: - return 'Unknown'; - } - } - }, - 0x0018: 'GPSDestBearing', - 0x0019: { - 'name': 'GPSDestDistanceRef', - 'description': function(value) { - switch (value.join('')) { - case 'K': - return 'Kilometers'; - case 'M': - return 'Miles'; - case 'N': - return 'Knots'; - default: - return 'Unknown'; - } - } - }, - 0x001a: 'GPSDestDistance', - 0x001b: { - 'name': 'GPSProcessingMethod', - 'description': function(value) { - if (value === 0) { - return 'Undefined'; - } else { - switch (value.slice(0, 8).map(function(charCode) { - return String.fromCharCode(charCode); - }).join('')) { - case 'ASCII\x00\x00\x00': - return value.slice(8, value.length).map(function(charCode) { - return String.fromCharCode(charCode); - }).join(''); - case 'JIS\x00\x00\x00\x00\x00': - return '[JIS encoded text]'; - case 'UNICODE\x00': - return '[Unicode encoded text]'; - case '\x00\x00\x00\x00\x00\x00\x00\x00': - return '[Undefined encoding]'; - } - } - } - }, - 0x001c: { - 'name': 'GPSAreaInformation', - 'description': function(value) { - if (value === 0) { - return 'Undefined'; - } else { - switch (value.slice(0, 8).map(function(charCode) { - return String.fromCharCode(charCode); - }).join('')) { - case 'ASCII\x00\x00\x00': - return value.slice(8, value.length).map(function(charCode) { - return String.fromCharCode(charCode); - }).join(''); - case 'JIS\x00\x00\x00\x00\x00': - return '[JIS encoded text]'; - case 'UNICODE\x00': - return '[Unicode encoded text]'; - case '\x00\x00\x00\x00\x00\x00\x00\x00': - return '[Undefined encoding]'; - } - } - } - }, - 0x001d: 'GPSDateStamp', - 0x001e: { - 'name': 'GPSDifferential', - 'description': function(value) { - switch (value) { - case 0: - return 'Measurement without differential correction'; - case 1: - return 'Differential correction applied'; - default: - return 'Unknown'; - } - } - } - }, - 'interoperability': { - 0x0001: 'InteroperabilityIndex', - 0x0002: 'UnknownInteroperabilityTag0x0002', - 0x1001: 'UnknownInteroperabilityTag0x1001', - 0x1002: 'UnknownInteroperabilityTag0x1002' - } - }; - - /* - # Gets the image's value of the tag with the given name. - # - # name string The name of the tag to get the value of - # - # Returns the value of the tag with the given name if it exists, - # otherwise throws "Undefined". - */ - - - ExifReader.prototype.getTagValue = function(name) { - if (this._tags[name] != null) { - return this._tags[name].value; - } else { - return void 0; - } - }; - - /* - # Gets the image's description of the tag with the given name. - # - # name string The name of the tag to get the description of - # - # Returns the description of the tag with the given name if it exists, - # otherwise throws "Undefined". - */ - - - ExifReader.prototype.getTagDescription = function(name) { - if (this._tags[name] != null) { - return this._tags[name].description; - } else { - return void 0; - } - }; - - /* - # Gets all the image's tags. - # - # Returns the image's tags as an associative array: name -> description. - */ - - - ExifReader.prototype.getAllTags = function() { - return this._tags; - }; - - /* - # Delete a tag. - # - # name string The name of the tag to delete - # - # Delete the tag with the given name. Can be used to lower memory usage. - # E.g., the MakerNote tag can be really large. - */ - - - ExifReader.prototype.deleteTag = function(name) { - return delete this._tags[name]; - }; - - return ExifReader; - - })(); - -}).call(this); diff --git a/server/app/static/js/vendor/canvas-to-blob.js b/server/app/static/js/vendor/canvas-to-blob.js deleted file mode 100644 index 32913667..00000000 --- a/server/app/static/js/vendor/canvas-to-blob.js +++ /dev/null @@ -1,111 +0,0 @@ -/* - * JavaScript Canvas to Blob - * https://github.com/blueimp/JavaScript-Canvas-to-Blob - * - * Copyright 2012, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - * - * Based on stackoverflow user Stoive's code snippet: - * http://stackoverflow.com/q/4998908 - */ - -/* global atob, Blob, define */ - -;(function (window) { - 'use strict' - - var CanvasPrototype = window.HTMLCanvasElement && - window.HTMLCanvasElement.prototype - var hasBlobConstructor = window.Blob && (function () { - try { - return Boolean(new Blob()) - } catch (e) { - return false - } - }()) - var hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array && - (function () { - try { - return new Blob([new Uint8Array(100)]).size === 100 - } catch (e) { - return false - } - }()) - var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || - window.MozBlobBuilder || window.MSBlobBuilder - var dataURIPattern = /^data:((.*?)(;charset=.*?)?)(;base64)?,/ - var dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob && - window.ArrayBuffer && window.Uint8Array && - function (dataURI) { - var matches, - mediaType, - isBase64, - dataString, - byteString, - arrayBuffer, - intArray, - i, - bb - // Parse the dataURI components as per RFC 2397 - matches = dataURI.match(dataURIPattern) - if (!matches) { - throw new Error('invalid data URI') - } - // Default to text/plain;charset=US-ASCII - mediaType = matches[2] - ? matches[1] - : 'text/plain' + (matches[3] || ';charset=US-ASCII') - isBase64 = !!matches[4] - dataString = dataURI.slice(matches[0].length) - if (isBase64) { - // Convert base64 to raw binary data held in a string: - byteString = atob(dataString) - } else { - // Convert base64/URLEncoded data component to raw binary: - byteString = decodeURIComponent(dataString) - } - // Write the bytes of the string to an ArrayBuffer: - arrayBuffer = new ArrayBuffer(byteString.length) - intArray = new Uint8Array(arrayBuffer) - for (i = 0; i < byteString.length; i += 1) { - intArray[i] = byteString.charCodeAt(i) - } - // Write the ArrayBuffer (or ArrayBufferView) to a blob: - if (hasBlobConstructor) { - return new Blob( - [hasArrayBufferViewSupport ? intArray : arrayBuffer], - {type: mediaType} - ) - } - bb = new BlobBuilder() - bb.append(arrayBuffer) - return bb.getBlob(mediaType) - } - if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) { - if (CanvasPrototype.mozGetAsFile) { - CanvasPrototype.toBlob = function (callback, type, quality) { - if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) { - callback(dataURLtoBlob(this.toDataURL(type, quality))) - } else { - callback(this.mozGetAsFile('blob', type)) - } - } - } else if (CanvasPrototype.toDataURL && dataURLtoBlob) { - CanvasPrototype.toBlob = function (callback, type, quality) { - callback(dataURLtoBlob(this.toDataURL(type, quality))) - } - } - } - if (typeof define === 'function' && define.amd) { - define(function () { - return dataURLtoBlob - }) - } else if (typeof module === 'object' && module.exports) { - module.exports = dataURLtoBlob - } else { - window.dataURLtoBlob = dataURLtoBlob - } -}(window)) diff --git a/server/app/static/js/vendor/jquery-3.3.1.min.js b/server/app/static/js/vendor/jquery-3.3.1.min.js deleted file mode 100644 index 4d9b3a25..00000000 --- a/server/app/static/js/vendor/jquery-3.3.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" - - - -{% endblock scripts %} \ No newline at end of file diff --git a/server/app/templates/display.html b/server/app/templates/display.html deleted file mode 100644 index f73a6ca5..00000000 --- a/server/app/templates/display.html +++ /dev/null @@ -1,69 +0,0 @@ -{%- extends "base.html" %} - -{% import "bootstrap/utils.html" as utils %} - -{% block content %} - - -
    - -
    -

    -

    Result

    -
    - -
    -
    -
    - -

    Rendered result

    -
    -
    - -
    - -

    Original image

    -
    -
    - -
    - -

    Semantic segmentation

    -
    -
    - - - -
    -
    -
    - Home -
    -
    -
    - - - - -{% block footer %} -{{super()}} -{% endblock footer %} - -{% endblock %} - diff --git a/server/app/templates/index.html b/server/app/templates/index.html deleted file mode 100644 index f740bb5b..00000000 --- a/server/app/templates/index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - DullDream (v2 x ZkM) - - - - -
    -

    DullDream

    -

    Neural network photo effect

    -
    - -
    -
    -
    - - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    - Change Image - -
    - -
    - Upload -
    -
    - -
    - -
    -
    - About - Privacy -

    - All images uploaded can be used for exhibition and review purposes. -

    -

    - Currently this work is on view at ZKM. View recent DullDreams here. -

    -
    -
    -
    - - - - - - -
    -
    -
    - -
    - Made with DullDream.xyz for ZKM OpenCodes 2017 -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - Home - About - Privacy -
    - -
    - -
    - - - - - - - - - - - - - - \ No newline at end of file diff --git a/server/celery_worker.py b/server/celery_worker.py deleted file mode 100644 index 1545a884..00000000 --- a/server/celery_worker.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python -import os -from app.basemodels import celery -from app import create_app - -app = create_app(os.getenv('FLASK_CONFIG') or 'default') -app.app_context().push() diff --git a/server/config.py b/server/config.py deleted file mode 100644 index 5042efb6..00000000 --- a/server/config.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf-8 -*- -"""Application configuration.""" -import os -from os.path import join -basedir = os.path.abspath(os.path.dirname(__file__)) - -class Config(object): - """Base configuration.""" - - #SECRET_KEY = os.environ.get('MYFLASKAPP_SECRET', 'secret-key') # TODO: Change me - APP_DIR = os.path.abspath(os.path.dirname(__file__)) # This directory - PROJECT_ROOT = os.path.abspath(os.path.join(APP_DIR, os.pardir)) - #BCRYPT_LOG_ROUNDS = 13 - DEBUG_TB_ENABLED = False # Disable Debug toolbar - #DEBUG_TB_INTERCEPT_REDIRECTS = False - CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc. - HOST = '0.0.0.0' - FLASK_DEBUG_DISABLE_STRICT = True - #WTF_CSRF_SECRET_KEY = '94ksadkf49DKEDFJ.&' - BOOTSTRAP_GOOGLE_ANALYTICS_ACCOUNT = None - BOOTSTRAP_SERVE_LOCAL = True - SECRET_KEY = os.environ.get('SECRET_KEY') or '94ksadkf49DKEDFJ.&' - CELERY_BROKER_URL = 'redis://localhost:6379/0' - CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' - - FLASKY_SLOW_DB_QUERY_TIME=0.5 - - @staticmethod - def init_app(app): - pass - - -class DevelopmentConfig(Config): - """Development configuration.""" - ENV = 'dev' - DEBUG = True - -class ProductionConfig(Config): - ENV = 'production' - DEBUG = False - # @classmethod - # def init_app(cls, app): - # Config.init_app(app) - # # import logging - # # app.logger.addHandler(mail_handler) - - -class DigitalOceanConfig(Config): - """Production configuration.""" - def init_app(cls, app): - ProductionConfig.init_app(app) - # log to syslog - import logging - from logging.handlers import SysLogHandler - syslog_handler = SysLogHandler() - syslog_handler.setLevel(logging.WARNING) - app.logger.addHandler(syslog_handler) - - -class UnixConfig(ProductionConfig): - @classmethod - def init_app(cls, app): - ProductionConfig.init_app(app) - - # log to syslog - import logging - from logging.handlers import SysLogHandler - syslog_handler = SysLogHandler() - syslog_handler.setLevel(logging.WARNING) - app.logger.addHandler(syslog_handler) - - -config = { - 'development': DevelopmentConfig, - 'production': ProductionConfig, - 'digitalocean': DigitalOceanConfig, - 'default': DevelopmentConfig -} diff --git a/server/deploy.sh b/server/deploy.sh deleted file mode 100755 index c2594cab..00000000 --- a/server/deploy.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -d_src="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/ -d_dst=/home/dull/dulldream/www/dulldream_xyz -ssh_alis=dulldream-root -#DOCKER_DIR_P1="$(dirname "$CWD")" - -echo "Syncing DullDream LOCAL to -> REMOTE" -echo $d_src -echo $d_dst - - -#rsync -a -e 'ssh' \ -rsync -r -v --progress -e 'ssh' \ - --delete \ - --exclude='.DS_Store' \ - --exclude='deploy.sh' \ - $d_src $ssh_alis:$d_dst - -echo "Synced :)" \ No newline at end of file diff --git a/server/dulldream.wsgi.py b/server/dulldream.wsgi.py deleted file mode 100644 index ed992528..00000000 --- a/server/dulldream.wsgi.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/python -import sys -sys.path.insert(0, "/home/dulldream/dulldream/www/dulldream_xyz/") - -from app import create_app - -import logging -logging.basicConfig(stream=sys.stderr) -# logging.basicConfig(filename='error.log',level=logging.DEBUG) - -application = create_app('production') -application.secret_key = 'curlier6982!1decentralizationists' - diff --git a/server/run-celery.sh b/server/run-celery.sh deleted file mode 100755 index e38174fa..00000000 --- a/server/run-celery.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -celery worker -A celery_worker.celery --loglevel=info - diff --git a/server/run-dev.sh b/server/run-dev.sh deleted file mode 100755 index b4eb2a61..00000000 --- a/server/run-dev.sh +++ /dev/null @@ -1 +0,0 @@ -FLASK_CONFIG=development python run.py \ No newline at end of file diff --git a/server/run-gunicorn.sh b/server/run-gunicorn.sh deleted file mode 100755 index 64debabd..00000000 --- a/server/run-gunicorn.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -gunicorn -w 1 -b 0.0.0.0:8000 run:app diff --git a/server/run-redis.sh b/server/run-redis.sh deleted file mode 100755 index e9ceb845..00000000 --- a/server/run-redis.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -/usr/local/bin/redis-server /etc/redis/redis.conf diff --git a/server/run.py b/server/run.py deleted file mode 100644 index c4c3e8d7..00000000 --- a/server/run.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/python3 -import os -from flask import Flask -from app import create_app - -app = create_app(os.getenv('FLASK_CONFIG') or 'default') -import logging -logging.basicConfig(filename='error.log',level=logging.DEBUG) - -if __name__ == '__main__': - app.run(host='0.0.0.0',debug=False,threaded=False,port=8000) - pass -- cgit v1.2.3-70-g09d2 From a53a598461a25e8bf1d0bd3e63c47642e3213aef Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 15 Dec 2018 22:47:28 +0100 Subject: client stuff --- client/actions.js | 8 +++--- client/index.js | 60 ++++++++++++++++++++++++++++++++++++-------- client/session.js | 6 ++--- client/store.js | 26 ++++++++----------- client/util.js | 2 ++ megapixels/app/server/api.py | 26 ++++++++++++++++++- megapixels/cli_flask.py | 1 + site/templates/research.html | 4 +++ 8 files changed, 98 insertions(+), 35 deletions(-) (limited to 'megapixels/app/server') diff --git a/client/actions.js b/client/actions.js index ba899f06..3fd86e0d 100644 --- a/client/actions.js +++ b/client/actions.js @@ -1,9 +1,9 @@ import * as search from './search/search.actions' -import * as review from './review/review.actions' -import * as metadata from './metadata/metadata.actions' +// import * as review from './review/review.actions' +// import * as metadata from './metadata/metadata.actions' export { search, - review, - metadata, + // review, + // metadata, } diff --git a/client/index.js b/client/index.js index eddc5fb2..03015988 100644 --- a/client/index.js +++ b/client/index.js @@ -3,17 +3,55 @@ import ReactDOM from 'react-dom' import { AppContainer } from 'react-hot-loader' import { Provider } from 'react-redux' -import App from './app' +// import App from './app' -import { store, history } from './store' +import { store } from './store' -const container = document.createElement('div') -document.body.appendChild(container) +// const container = document.createElement('div') +// document.body.appendChild(container) -ReactDOM.render( - - - - - , container -) +toArray(document.querySelectorAll('.applet')).forEach(el => { + try { + const payload = JSON.parse(el.dataSet.getItem('payload')) + } catch(e) { + return + } + switch (payload.command) { + case 'load file': + append_tabulator(el, payload) + break + default: + append_react_applet(el, payload) + break + } +}) + +function append_react_applet(el, payload) { + ReactDOM.render( + + + + + , el + ) +} +function append_tabulator(el, payload) { + const table = new Tabulator(el, { + height:"311px", + layout:"fitColumns", + placeholder:"No Data Set", + columns:[ + // {title:"Name", field:"name", sorter:"string", width:200}, + // {title:"Progress", field:"progress", sorter:"number", formatter:"progress"}, + // {title:"Gender", field:"gender", sorter:"string"}, + // {title:"Rating", field:"rating", formatter:"star", align:"center", width:100}, + // {title:"Favourite Color", field:"col", sorter:"string", sortable:false}, + // {title:"Date Of Birth", field:"dob", sorter:"date", align:"center"}, + // {title:"Driver", field:"car", align:"center", formatter:"tickCross", sorter:"boolean"}, + ], + }) + const columns = payload.fields.split(', ') + console.log(columns) + if () + table.setData(path) +} \ No newline at end of file diff --git a/client/session.js b/client/session.js index 5bfae7eb..0fae31d2 100644 --- a/client/session.js +++ b/client/session.js @@ -1,5 +1,5 @@ -import Storage from 'store2' +// import Storage from 'store2' -const session = Storage.namespace('vcat.search') +// const session = Storage.namespace('vcat.search') -export default session +// export default session diff --git a/client/store.js b/client/store.js index 043af351..9c0f78cd 100644 --- a/client/store.js +++ b/client/store.js @@ -1,30 +1,25 @@ import { applyMiddleware, compose, combineReducers, createStore } from 'redux' -import { connectRouter, routerMiddleware } from 'connected-react-router' -import { createBrowserHistory } from 'history' import thunk from 'redux-thunk' -import { login } from './util' -import metadataReducer from './metadata/metadata.reducer' -import searchReducer from './search/search.reducer' -import reviewReducer from './review/review.reducer' +// import metadataReducer from './metadata/metadata.reducer' +// import searchReducer from './search/search.reducer' +// import reviewReducer from './review/review.reducer' const rootReducer = combineReducers({ - auth: (state = login()) => state, - metadata: metadataReducer, - search: searchReducer, - review: reviewReducer, + // auth: (state = login()) => state, + // metadata: metadataReducer, + // search: searchReducer, + // review: reviewReducer, }) -function configureStore(initialState = {}, history) { +function configureStore(initialState = {}) { const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose const store = createStore( - connectRouter(history)(rootReducer), // new root reducer with router state initialState, composeEnhancers( applyMiddleware( thunk, - routerMiddleware(history) ), ), ) @@ -32,7 +27,6 @@ function configureStore(initialState = {}, history) { return store } -const history = createBrowserHistory() -const store = configureStore({}, history) +const store = configureStore({}) -export { store, history } +export { store } diff --git a/client/util.js b/client/util.js index ad303c64..92b4addc 100644 --- a/client/util.js +++ b/client/util.js @@ -6,6 +6,8 @@ export const isAndroid = !!(navigator.userAgent.match(/Android/i)) export const isMobile = isiPhone || isiPad || isAndroid export const isDesktop = !isMobile +export const toArray = a => Array.prototype.slice.apply(a) + const htmlClassList = document.body.parentNode.classList htmlClassList.add(isDesktop ? 'desktop' : 'mobile') diff --git a/megapixels/app/server/api.py b/megapixels/app/server/api.py index c5e27dd2..cd2b950b 100644 --- a/megapixels/app/server/api.py +++ b/megapixels/app/server/api.py @@ -23,8 +23,10 @@ def show(name): else: return jsonify({ 'status': 404 }) -@api.route('/dataset//face', methods=['POST']) +@api.route('/dataset//face', methods=['POST']) def upload(name): + start = time.time() + dataset = get_dataset(name) file = request.files['query_img'] fn = file.filename if fn.endswith('blob'): @@ -42,6 +44,28 @@ def upload(name): # print(vec.shape) # results = db.search(vec, limit=limit) + # with the result we have an ID + # query the sql dataset for the UUID etc here + + query = { + 'timing': time.time() - start, + } + results = [] + + print(results) + return jsonify({ + 'query': query, + 'results': results, + }) + +@api.route('/dataset//name', methods=['GET']) +def name_lookup(dataset): + start = time.time() + dataset = get_dataset(name) + + # we have a query from the request query string... + # use this to do a like* query on the identities_meta table + query = { 'timing': time.time() - start, } diff --git a/megapixels/cli_flask.py b/megapixels/cli_flask.py index 369bec01..e80526c6 100644 --- a/megapixels/cli_flask.py +++ b/megapixels/cli_flask.py @@ -1,5 +1,6 @@ # -------------------------------------------------------- # wrapper for flask CLI API +# NB: python cli_flask.py run # -------------------------------------------------------- import click diff --git a/site/templates/research.html b/site/templates/research.html index 0bb9fa30..b2ea3a6b 100644 --- a/site/templates/research.html +++ b/site/templates/research.html @@ -21,3 +21,7 @@ {{ content }} {% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 3b10acc73247ec703ed47f0423e7d255a91f074e Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sun, 16 Dec 2018 00:11:20 +0100 Subject: tabulator --- client/index.js | 29 +- megapixels/app/server/api.py | 17 +- megapixels/app/server/create.py | 7 + megapixels/app/site/builder.py | 10 + megapixels/app/site/parser.py | 36 +- megapixels/app/site/s3.py | 3 + site/assets/css/applets.css | 2 - site/assets/css/tabulator.css | 761 +++++++++++++++++++++ site/public/about/credits/index.html | 2 +- site/public/about/disclaimer/index.html | 2 +- site/public/about/index.html | 2 +- site/public/about/press/index.html | 2 +- site/public/about/privacy/index.html | 2 +- site/public/about/style/index.html | 2 +- site/public/about/terms/index.html | 2 +- site/public/datasets/lfw/index.html | 6 +- site/public/datasets/vgg_face2/index.html | 4 +- site/public/index.html | 2 +- site/public/research/00_introduction/index.html | 2 +- .../research/01_from_1_to_100_pixels/index.html | 2 +- site/public/research/index.html | 2 +- site/templates/layout.html | 1 + 22 files changed, 862 insertions(+), 36 deletions(-) create mode 100755 site/assets/css/tabulator.css (limited to 'megapixels/app/server') diff --git a/client/index.js b/client/index.js index bc57f548..2ee12c14 100644 --- a/client/index.js +++ b/client/index.js @@ -23,23 +23,24 @@ function appendTabulator(el, payload) { height: '311px', layout: 'fitColumns', placeholder: 'No Data Set', - columns: [ - // {title:'Name', field:'name', sorter:'string', width:200}, - // {title:'Progress', field:'progress', sorter:'number', formatter:'progress'}, - // {title:'Gender', field:'gender', sorter:"string"}, - // {title:"Rating", field:"rating", formatter:"star", align:"center", width:100}, - // {title:"Favourite Color", field:"col", sorter:"string", sortable:false}, - // {title:"Date Of Birth", field:"dob", sorter:"date", align:"center"}, - // {title:"Driver", field:"car", align:"center", formatter:"tickCross", sorter:"boolean"}, - ], + columns: payload.fields.split(', ').map(field => { + switch (field) { + default: + return { title: field, field: field.toLowerCase(), sorter: 'string' } + } + }), + // {title:'Name', field:'name', sorter:'string', width:200}, + // {title:'Progress', field:'progress', sorter:'number', formatter:'progress'}, + // {title:'Gender', field:'gender', sorter:"string"}, + // {title:"Rating", field:"rating", formatter:"star", align:"center", width:100}, + // {title:"Favourite Color", field:"col", sorter:"string", sortable:false}, + // {title:"Date Of Birth", field:"dob", sorter:"date", align:"center"}, + // {title:"Driver", field:"car", align:"center", formatter:"tickCross", sorter:"boolean"}, }) let path = payload.opt let columns = payload.fields.split(',').map(s => s.trim()) - console.log(columns) - if (path[0] !== '/') { - console.log(path) - } - // table.setData(path) + console.log(path, columns) + table.setData(path) } function appendApplets() { diff --git a/megapixels/app/server/api.py b/megapixels/app/server/api.py index cd2b950b..cf8241bd 100644 --- a/megapixels/app/server/api.py +++ b/megapixels/app/server/api.py @@ -1,9 +1,12 @@ import os import re import time +import dlib from flask import Blueprint, request, jsonify from PIL import Image # todo: try to remove PIL dependency +from app.processors import face_recognition +from app.processors import face_detector from app.models.sql_factory import list_datasets, get_dataset, get_table sanitize_re = re.compile('[\W]+') @@ -39,8 +42,18 @@ def upload(name): img = Image.open(file.stream).convert('RGB') - # vec = db.load_feature_vector_from_file(uploaded_img_path) - # vec = fe.extract(img) + # Face detection + detector = face_detector.DetectorDLIBHOG() + + # get detection as BBox object + bboxes = detector.detect(im, largest=True) + bbox = bboxes[0] + dim = im.shape[:2][::-1] + bbox = bbox.to_dim(dim) # convert back to real dimensions + + # face recognition/vector + recognition = face_recognition.RecognitionDLIB(gpu=-1) + # print(vec.shape) # results = db.search(vec, limit=limit) diff --git a/megapixels/app/server/create.py b/megapixels/app/server/create.py index c1f41dc4..4b1333b9 100644 --- a/megapixels/app/server/create.py +++ b/megapixels/app/server/create.py @@ -7,6 +7,9 @@ from app.server.api import api db = SQLAlchemy() def create_app(script_info=None): + """ + functional pattern for creating the flask app + """ app = Flask(__name__, static_folder='static', static_url_path='') app.config['SQLALCHEMY_DATABASE_URI'] = connection_url app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False @@ -37,6 +40,10 @@ def create_app(script_info=None): return app def serve_page(file_relative_path_to_root): + """ + trying to get this to serve /path/ with /path/index.html, + ...but it doesnt actually matter for production... + """ if file_relative_path_to_root[-1] == '/': file_relative_path_to_root += 'index.html' return send_from_directory("static", file_relative_path_to_root) diff --git a/megapixels/app/site/builder.py b/megapixels/app/site/builder.py index 91df54c2..895f265b 100644 --- a/megapixels/app/site/builder.py +++ b/megapixels/app/site/builder.py @@ -15,6 +15,10 @@ env = Environment( ) def build_page(fn, research_posts): + """ + build a single page from markdown into the appropriate template + - writes it to site/public/ + """ metadata, sections = parser.read_metadata(fn) if metadata is None: @@ -61,6 +65,9 @@ def build_page(fn, research_posts): file.write(html) def build_research_index(research_posts): + """ + build the index of research (blog) posts + """ metadata, sections = parser.read_metadata('../site/content/research/index.md') template = env.get_template("page.html") s3_path = s3.make_s3_path(cfg.S3_SITE_PATH, metadata['path']) @@ -77,6 +84,9 @@ def build_research_index(research_posts): file.write(html) def build_site(): + """ + build the site! =^) + """ research_posts = parser.read_research_post_index() for fn in glob.iglob(os.path.join(cfg.DIR_SITE_CONTENT, "**/*.md"), recursive=True): build_page(fn, research_posts) diff --git a/megapixels/app/site/parser.py b/megapixels/app/site/parser.py index d78cc402..40d9c7f6 100644 --- a/megapixels/app/site/parser.py +++ b/megapixels/app/site/parser.py @@ -11,6 +11,10 @@ renderer = mistune.Renderer(escape=False) markdown = mistune.Markdown(renderer=renderer) def fix_images(lines, s3_path): + """ + do our own tranformation of the markdown around images to handle wide images etc + lines: markdown lines + """ real_lines = [] block = "\n\n".join(lines) for line in block.split("\n"): @@ -29,6 +33,9 @@ def fix_images(lines, s3_path): return "\n".join(real_lines) def format_section(lines, s3_path, type=''): + """ + format a normal markdown section + """ if len(lines): lines = fix_images(lines, s3_path) if type: @@ -38,13 +45,16 @@ def format_section(lines, s3_path, type=''): return "" def format_metadata(section): + """ + format a metadata section (+ key: value pairs) + """ meta = [] for line in section.split('\n'): key, value = line[2:].split(': ', 1) meta.append("
    {}
    {}
    ".format(key, value)) return "
    {}
    ".format(''.join(meta)) -def format_applet(section): +def format_applet(section, s3_path): payload = section.replace('```', '').strip().split('\n') applet = {} if ': ' in payload[0]: @@ -56,10 +66,15 @@ def format_applet(section): if opt: applet['opt'] = opt if command == 'load file': + if opt[0] != '/': + applet['opt'] = s3_path + opt applet['fields'] = payload[1] return "
    ".format(json.dumps(applet)) def parse_markdown(sections, s3_path, skip_h1=False): + """ + parse page into sections, preprocess the markdown to handle our modifications + """ groups = [] current_group = [] for section in sections: @@ -67,7 +82,7 @@ def parse_markdown(sections, s3_path, skip_h1=False): continue elif section.startswith('```'): groups.append(format_section(current_group, s3_path)) - groups.append(format_applet(section)) + groups.append(format_applet(section, s3_path)) current_group = [] elif section.startswith('+ '): groups.append(format_section(current_group, s3_path)) @@ -88,6 +103,9 @@ def parse_markdown(sections, s3_path, skip_h1=False): return content def parse_research_index(research_posts): + """ + Generate an index file for the research pages + """ content = "
    " for post in research_posts: s3_path = s3.make_s3_path(cfg.S3_SITE_PATH, post['path']) @@ -105,6 +123,9 @@ def parse_research_index(research_posts): return content def read_metadata(fn): + """ + Read in read a markdown file and extract the metadata + """ with open(fn, "r") as file: data = file.read() data = data.replace("\n ", "\n") @@ -128,6 +149,9 @@ default_metadata = { } def parse_metadata_section(metadata, section): + """ + parse a metadata key: value pair + """ for line in section.split("\n"): if ': ' not in line: continue @@ -135,6 +159,11 @@ def parse_metadata_section(metadata, section): metadata[key.lower()] = value def parse_metadata(fn, sections): + """ + parse the metadata headers in a markdown file + (everything before the second ---------) + also generates appropriate urls for this page :) + """ found_meta = False metadata = {} valid_sections = [] @@ -175,6 +204,9 @@ def parse_metadata(fn, sections): return metadata, valid_sections def read_research_post_index(): + """ + Generate an index of the research (blog) posts + """ posts = [] for fn in sorted(glob.glob('../site/content/research/*/index.md')): metadata, valid_sections = read_metadata(fn) diff --git a/megapixels/app/site/s3.py b/megapixels/app/site/s3.py index 99726a4d..5464d464 100644 --- a/megapixels/app/site/s3.py +++ b/megapixels/app/site/s3.py @@ -3,6 +3,9 @@ import glob import boto3 def sync_directory(base_fn, s3_path, metadata): + """ + Synchronize a local assets folder with S3 + """ fns = {} for fn in glob.glob(os.path.join(base_fn, 'assets/*')): fns[os.path.basename(fn)] = True diff --git a/site/assets/css/applets.css b/site/assets/css/applets.css index b437886b..e106b3dd 100644 --- a/site/assets/css/applets.css +++ b/site/assets/css/applets.css @@ -1,4 +1,2 @@ .applet { - border: 1px solid #0f0; - color: #0f0; } diff --git a/site/assets/css/tabulator.css b/site/assets/css/tabulator.css new file mode 100755 index 00000000..8c123cf4 --- /dev/null +++ b/site/assets/css/tabulator.css @@ -0,0 +1,761 @@ +/* Tabulator v4.1.3 (c) Oliver Folkerd */ +.tabulator { + position: relative; + font-size: 14px; + text-align: left; + overflow: hidden; + -ms-transform: translatez(0); + transform: translatez(0); +} + +.tabulator[tabulator-layout="fitDataFill"] .tabulator-tableHolder .tabulator-table { + min-width: 100%; +} + +.tabulator.tabulator-block-select { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.tabulator .tabulator-header { + position: relative; + box-sizing: border-box; + width: 100%; + border-bottom: 1px solid #999; + color: #ddd; + white-space: nowrap; + overflow: hidden; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} + +.tabulator .tabulator-header .tabulator-col { + display: inline-block; + position: relative; + box-sizing: border-box; + border-right: 1px solid #aaa; + text-align: left; + vertical-align: bottom; + overflow: hidden; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-moving { + position: absolute; + border: 1px solid #999; + background: rgba(80,20,10,0.2); + pointer-events: none; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-col-content { + box-sizing: border-box; + position: relative; + padding: 4px; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title { + box-sizing: border-box; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: bottom; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor { + box-sizing: border-box; + width: 100%; + border: 1px solid #999; + padding: 1px; + background: #fff; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow { + display: inline-block; + position: absolute; + top: 9px; + right: 8px; + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #bbb; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols { + position: relative; + display: -ms-flexbox; + display: flex; + border-top: 1px solid #aaa; + overflow: hidden; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols .tabulator-col:last-child { + margin-right: -1px; +} + +.tabulator .tabulator-header .tabulator-col:first-child .tabulator-col-resize-handle.prev { + display: none; +} + +.tabulator .tabulator-header .tabulator-col.ui-sortable-helper { + position: absolute; + border: 1px solid #aaa; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-header-filter { + position: relative; + box-sizing: border-box; + margin-top: 2px; + width: 100%; + text-align: center; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-header-filter textarea { + height: auto !important; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-header-filter svg { + margin-top: 3px; +} + +.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input::-ms-clear { + width: 0; + height: 0; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title { + padding-right: 25px; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-sortable:hover { + cursor: pointer; + background-color: #cdcdcd; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="none"] .tabulator-col-content .tabulator-arrow { + border-top: none; + border-bottom: 6px solid #bbb; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="asc"] .tabulator-col-content .tabulator-arrow { + border-top: none; + border-bottom: 6px solid #666; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="desc"] .tabulator-col-content .tabulator-arrow { + border-top: 6px solid #666; + border-bottom: none; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical .tabulator-col-content .tabulator-col-title { + -webkit-writing-mode: vertical-rl; + -ms-writing-mode: tb-rl; + writing-mode: vertical-rl; + text-orientation: mixed; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-col-vertical-flip .tabulator-col-title { + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-title { + padding-right: 0; + padding-top: 20px; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-vertical-flip .tabulator-col-title { + padding-right: 0; + padding-bottom: 20px; +} + +.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-arrow { + right: calc(50% - 6px); +} + +.tabulator .tabulator-header .tabulator-frozen { + display: inline-block; + position: absolute; + z-index: 10; +} + +.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left { + border-right: 2px solid #aaa; +} + +.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right { + border-left: 2px solid #aaa; +} + +.tabulator .tabulator-header .tabulator-calcs-holder { + box-sizing: border-box; + min-width: 400%; + background: rgba(80,20,10,0.2); + border-top: 1px solid #aaa; + border-bottom: 1px solid #aaa; + overflow: hidden; +} + +.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row { + background: rgba(80,20,10,0.2); +} + +.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle { + display: none; +} + +.tabulator .tabulator-header .tabulator-frozen-rows-holder { + min-width: 400%; +} + +.tabulator .tabulator-header .tabulator-frozen-rows-holder:empty { + display: none; +} + +.tabulator .tabulator-tableHolder { + position: relative; + width: 100%; + white-space: nowrap; + overflow: auto; + -webkit-overflow-scrolling: touch; +} + +.tabulator .tabulator-tableHolder:focus { + outline: none; +} + +.tabulator .tabulator-tableHolder .tabulator-placeholder { + box-sizing: border-box; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + width: 100%; +} + +.tabulator .tabulator-tableHolder .tabulator-placeholder[tabulator-render-mode="virtual"] { + position: absolute; + top: 0; + left: 0; + height: 100%; +} + +.tabulator .tabulator-tableHolder .tabulator-placeholder span { + display: inline-block; + margin: 0 auto; + padding: 10px; + color: #ccc; + font-weight: bold; + font-size: 20px; +} + +.tabulator .tabulator-tableHolder .tabulator-table { + position: relative; + display: inline-block; + background-color: #fff; + white-space: nowrap; + overflow: visible; + color: #333; +} + +.tabulator .tabulator-tableHolder .tabulator-table .tabulator-row.tabulator-calcs { + font-weight: bold; + background: rgba(80,20,10,0.2); +} + +.tabulator .tabulator-tableHolder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-top { + border-bottom: 2px solid #aaa; +} + +.tabulator .tabulator-tableHolder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-bottom { + border-top: 2px solid #aaa; +} + +.tabulator .tabulator-footer { + padding: 5px 10px; + border-top: 1px solid #999; + text-align: right; + color: #555; + font-weight: bold; + white-space: nowrap; + -ms-user-select: none; + user-select: none; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} + +.tabulator .tabulator-footer .tabulator-calcs-holder { + box-sizing: border-box; + width: calc(100% + 20px); + margin: -5px -10px 5px -10px; + text-align: left; + background: rgba(80,20,10,0.2); + border-bottom: 1px solid #aaa; + border-top: 1px solid #aaa; + overflow: hidden; +} + +.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row { + background: rgba(80,20,10,0.2); +} + +.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle { + display: none; +} + +.tabulator .tabulator-footer .tabulator-calcs-holder:only-child { + margin-bottom: -5px; + border-bottom: none; +} + +.tabulator .tabulator-footer .tabulator-pages { + margin: 0 7px; +} + +.tabulator .tabulator-footer .tabulator-page { + display: inline-block; + margin: 0 2px; + padding: 2px 5px; + border: 1px solid #aaa; + border-radius: 3px; + background: rgba(255, 255, 255, 0.2); + color: #555; + font-family: inherit; + font-weight: inherit; + font-size: inherit; +} + +.tabulator .tabulator-footer .tabulator-page.active { + color: #d00; +} + +.tabulator .tabulator-footer .tabulator-page:disabled { + opacity: .5; +} + +.tabulator .tabulator-footer .tabulator-page:not(.disabled):hover { + cursor: pointer; + background: rgba(0, 0, 0, 0.2); + color: #fff; +} + +.tabulator .tabulator-col-resize-handle { + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 5px; +} + +.tabulator .tabulator-col-resize-handle.prev { + left: 0; + right: auto; +} + +.tabulator .tabulator-col-resize-handle:hover { + cursor: ew-resize; +} + +.tabulator .tabulator-loader { + position: absolute; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + top: 0; + left: 0; + z-index: 100; + height: 100%; + width: 100%; + background: rgba(0, 0, 0, 0.4); + text-align: center; +} + +.tabulator .tabulator-loader .tabulator-loader-msg { + display: inline-block; + margin: 0 auto; + padding: 10px 20px; + border-radius: 4px; + background: #fff; + font-weight: bold; + font-size: 16px; +} + +.tabulator .tabulator-loader .tabulator-loader-msg.tabulator-loading { + border: 4px solid #333; + color: #000; +} + +.tabulator .tabulator-loader .tabulator-loader-msg.tabulator-error { + color: #000; +} + +.tabulator-row { + position: relative; + box-sizing: border-box; + min-height: 22px; + background-color: #fff; +} + +.tabulator-row.tabulator-row-even { + background-color: #EFEFEF; +} + +.tabulator-row.tabulator-selectable:hover { + background-color: #bbb; + cursor: pointer; +} + +.tabulator-row.tabulator-selected { + background-color: #9ABCEA; +} + +.tabulator-row.tabulator-selected:hover { + background-color: #769BCC; + cursor: pointer; +} + +.tabulator-row.tabulator-row-moving { + border: 1px solid #000; + background: #fff; +} + +.tabulator-row.tabulator-moving { + position: absolute; + border-top: 1px solid #aaa; + border-bottom: 1px solid #aaa; + pointer-events: none; + z-index: 15; +} + +.tabulator-row .tabulator-row-resize-handle { + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 5px; +} + +.tabulator-row .tabulator-row-resize-handle.prev { + top: 0; + bottom: auto; +} + +.tabulator-row .tabulator-row-resize-handle:hover { + cursor: ns-resize; +} + +.tabulator-row .tabulator-frozen { + display: inline-block; + position: absolute; + background-color: inherit; + z-index: 10; +} + +.tabulator-row .tabulator-frozen.tabulator-frozen-left { + border-right: 2px solid #aaa; +} + +.tabulator-row .tabulator-frozen.tabulator-frozen-right { + border-left: 2px solid #aaa; +} + +.tabulator-row .tabulator-responsive-collapse { + box-sizing: border-box; + padding: 5px; + border-top: 1px solid #aaa; + border-bottom: 1px solid #aaa; +} + +.tabulator-row .tabulator-responsive-collapse:empty { + display: none; +} + +.tabulator-row .tabulator-responsive-collapse table { + font-size: 14px; +} + +.tabulator-row .tabulator-responsive-collapse table tr td { + position: relative; +} + +.tabulator-row .tabulator-responsive-collapse table tr td:first-of-type { + padding-right: 10px; +} + +.tabulator-row .tabulator-cell { + display: inline-block; + position: relative; + box-sizing: border-box; + padding: 4px; + border-right: 1px solid #aaa; + vertical-align: middle; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.tabulator-row .tabulator-cell.tabulator-editing { + border: 1px solid #1D68CD; + padding: 0; +} + +.tabulator-row .tabulator-cell.tabulator-editing input, .tabulator-row .tabulator-cell.tabulator-editing select { + border: 1px; + background: transparent; +} + +.tabulator-row .tabulator-cell.tabulator-validation-fail { + border: 1px solid #dd0000; +} + +.tabulator-row .tabulator-cell.tabulator-validation-fail input, .tabulator-row .tabulator-cell.tabulator-validation-fail select { + border: 1px; + background: transparent; + color: #dd0000; +} + +.tabulator-row .tabulator-cell:first-child .tabulator-col-resize-handle.prev { + display: none; +} + +.tabulator-row .tabulator-cell.tabulator-row-handle { + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} + +.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box { + width: 80%; +} + +.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box .tabulator-row-handle-bar { + width: 100%; + height: 3px; + margin-top: 2px; + background: #666; +} + +.tabulator-row .tabulator-cell .tabulator-data-tree-branch { + display: inline-block; + vertical-align: middle; + height: 9px; + width: 7px; + margin-top: -9px; + margin-right: 5px; + border-bottom-left-radius: 1px; + border-left: 2px solid #aaa; + border-bottom: 2px solid #aaa; +} + +.tabulator-row .tabulator-cell .tabulator-data-tree-control { + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-pack: center; + justify-content: center; + -ms-flex-align: center; + align-items: center; + vertical-align: middle; + height: 11px; + width: 11px; + margin-right: 5px; + border: 1px solid #333; + border-radius: 2px; + background: rgba(0, 0, 0, 0.1); + overflow: hidden; +} + +.tabulator-row .tabulator-cell .tabulator-data-tree-control:hover { + cursor: pointer; + background: rgba(0, 0, 0, 0.2); +} + +.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse { + display: inline-block; + position: relative; + height: 7px; + width: 1px; + background: transparent; +} + +.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after { + position: absolute; + content: ""; + left: -3px; + top: 3px; + height: 1px; + width: 7px; + background: #333; +} + +.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand { + display: inline-block; + position: relative; + height: 7px; + width: 1px; + background: #333; +} + +.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand:after { + position: absolute; + content: ""; + left: -3px; + top: 3px; + height: 1px; + width: 7px; + background: #333; +} + +.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle { + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; + height: 15px; + width: 15px; + border-radius: 4px; + background: #666; + color: #fff; + font-weight: bold; + font-size: 1.1em; +} + +.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle:hover { + opacity: .7; +} + +.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-close { + display: initial; +} + +.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-open { + display: none; +} + +.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle .tabulator-responsive-collapse-toggle-close { + display: none; +} + +.tabulator-row.tabulator-group { + box-sizing: border-box; + border-bottom: 1px solid #999; + border-right: 1px solid #aaa; + border-top: 1px solid #999; + padding: 5px; + padding-left: 10px; + background: #ccc; + font-weight: bold; + min-width: 100%; +} + +.tabulator-row.tabulator-group:hover { + cursor: pointer; + background-color: rgba(0, 0, 0, 0.1); +} + +.tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow { + margin-right: 10px; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid #666; + border-bottom: 0; +} + +.tabulator-row.tabulator-group.tabulator-group-level-1 .tabulator-arrow { + margin-left: 20px; +} + +.tabulator-row.tabulator-group.tabulator-group-level-2 .tabulator-arrow { + margin-left: 40px; +} + +.tabulator-row.tabulator-group.tabulator-group-level-3 .tabulator-arrow { + margin-left: 60px; +} + +.tabulator-row.tabulator-group.tabulator-group-level-4 .tabulator-arrow { + margin-left: 80px; +} + +.tabulator-row.tabulator-group.tabulator-group-level-5 .tabulator-arrow { + margin-left: 100px; +} + +.tabulator-row.tabulator-group .tabulator-arrow { + display: inline-block; + width: 0; + height: 0; + margin-right: 16px; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-right: 0; + border-left: 6px solid #666; + vertical-align: middle; +} + +.tabulator-row.tabulator-group span { + margin-left: 10px; + color: #d00; +} + +.tabulator-edit-select-list { + position: absolute; + display: inline-block; + box-sizing: border-box; + max-height: 200px; + background: #fff; + border: 1px solid #aaa; + font-size: 14px; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + z-index: 10000; +} + +.tabulator-edit-select-list .tabulator-edit-select-list-item { + padding: 4px; + color: #333; +} + +.tabulator-edit-select-list .tabulator-edit-select-list-item.active { + color: #fff; + background: #1D68CD; +} + +.tabulator-edit-select-list .tabulator-edit-select-list-item:hover { + cursor: pointer; + color: #fff; + background: #1D68CD; +} + +.tabulator-edit-select-list .tabulator-edit-select-list-group { + border-bottom: 1px solid #aaa; + padding: 4px; + padding-top: 6px; + color: #333; + font-weight: bold; +} diff --git a/site/public/about/credits/index.html b/site/public/about/credits/index.html index 1b1f4d54..67e9dcb8 100644 --- a/site/public/about/credits/index.html +++ b/site/public/about/credits/index.html @@ -8,6 +8,7 @@ + @@ -19,7 +20,6 @@ The Darkside of Datasets