├── Procfile ├── README.md ├── __pycache__ └── config.cpython-34.pyc ├── app ├── __init__.py ├── __init__.pyc ├── __pycache__ │ ├── __init__.cpython-34.pyc │ ├── forms.cpython-34.pyc │ └── views.cpython-34.pyc ├── model │ ├── __init__.pyc │ ├── __pycache__ │ │ ├── __init__.cpython-34.pyc │ │ ├── alpha_cnn_predict.cpython-34.pyc │ │ └── preprocessor.cpython-34.pyc │ ├── alpha_cnn_predict.py │ ├── alpha_cnn_predict.pyc │ ├── alpha_weights.pkl │ ├── preprocessor.py │ └── preprocessor.pyc ├── static │ ├── analytics.js │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ ├── bootstrap.min.js │ ├── css │ ├── img │ │ ├── cnn_ocr.png │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ ├── python.ico │ │ ├── python.png │ │ ├── recognized_o.png │ │ └── recognized_q.png │ ├── jquery.min.js │ ├── js │ │ ├── MathJax.js │ │ ├── bootstrap.min.js │ │ └── ocr_canvas.js │ └── style.css ├── templates │ ├── base.html │ └── index.html ├── views.py └── views.pyc ├── config.pyc ├── convolutional_network_tutorial.ipynb ├── dataset.txt ├── requirements.txt ├── run.py ├── runtime.txt └── temp.jpg /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn --log-file - app:app 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Convolutional Neural Network 2 | 3 | ## Overview 4 | 5 | This is the code for [this](https://youtu.be/FTr3n7uBIuE) video on Youtube by Siraj Raval as part of The Math of Intelligence course. A convolutional neural network implemented in pure numpy. It uses a MNIST-like dataset with about 30 alphanumeric symbols. The author trained a deep convolutional network using Keras and saved the weights using python's pickle utility. Only the the forward propagation code is rewritten in pure numpy (as opposed to Theano or Tensorflow as in Keras). Which lets us run the network as a demo via heroku. For backpropagation in numpy for a convnet see [this](https://github.com/Kankroc/NaiveCNN) 6 | 7 | ![recognized_o.png](https://github.com/greydanus/pythonic_ocr/blob/master/app/static/img/recognized_o.png) ![recognized_q.png](https://github.com/greydanus/pythonic_ocr/blob/master/app/static/img/recognized_q.png) 8 | 9 | Live web app is here: 10 | [Website](https://pythonic-ocr.herokuapp.com/) 11 | 12 | 13 | ## Dependencies 14 | -------- 15 | 16 | Dependencies are packaged in the flask folder, so this app does not have any external depencies. Run `pip install -r requirements.txt` to install them. 17 | 18 | Install pip [here](https://pip.pypa.io/en/stable/). 19 | 20 | 21 | ## Usage 22 | 23 | to start the web app run `python run.py` . To start the notebook run `jupyter notebook` in terminal. 24 | 25 | Install jupyter [here](http://jupyter.readthedocs.io/en/latest/install.html). 26 | 27 | 28 | ## Credits 29 | 30 | Credits for this code go to [greydanus](https://github.com/greydanus/pythonic_ocr). I've merely created a wrapper to get people started. 31 | 32 | -------------------------------------------------------------------------------- /__pycache__/config.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/__pycache__/config.cpython-34.pyc -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["model"] 2 | 3 | from flask import Flask 4 | 5 | app = Flask(__name__) 6 | app.config.from_object('config') 7 | 8 | from app import views -------------------------------------------------------------------------------- /app/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/__init__.pyc -------------------------------------------------------------------------------- /app/__pycache__/__init__.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/__pycache__/__init__.cpython-34.pyc -------------------------------------------------------------------------------- /app/__pycache__/forms.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/__pycache__/forms.cpython-34.pyc -------------------------------------------------------------------------------- /app/__pycache__/views.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/__pycache__/views.cpython-34.pyc -------------------------------------------------------------------------------- /app/model/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/model/__init__.pyc -------------------------------------------------------------------------------- /app/model/__pycache__/__init__.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/model/__pycache__/__init__.cpython-34.pyc -------------------------------------------------------------------------------- /app/model/__pycache__/alpha_cnn_predict.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/model/__pycache__/alpha_cnn_predict.cpython-34.pyc -------------------------------------------------------------------------------- /app/model/__pycache__/preprocessor.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/model/__pycache__/preprocessor.cpython-34.pyc -------------------------------------------------------------------------------- /app/model/alpha_cnn_predict.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import numpy as np 3 | from app.model.preprocessor import Preprocessor as img_prep 4 | 5 | class LiteOCR: 6 | def __init__(self, fn="alpha_weights.pkl", pool_size=2): 7 | [weights, meta] = pickle.load(open(fn, 'rb'), encoding='latin1') #currently, this class MUST be initialized from a pickle file 8 | self.vocab = meta["vocab"] 9 | 10 | self.img_rows = meta["img_side"] ; self.img_cols = meta["img_side"] 11 | 12 | self.CNN = LiteCNN() 13 | self.CNN.load_weights(weights) 14 | self.CNN.pool_size=int(pool_size) 15 | 16 | def predict(self, image): 17 | print(image.shape) 18 | X = np.reshape(image, (1, 1, self.img_rows, self.img_cols)) 19 | X = X.astype("float32") 20 | 21 | predicted_i = self.CNN.predict(X) 22 | return self.vocab[predicted_i] 23 | 24 | class LiteCNN: 25 | def __init__(self): 26 | self.layers = [] # a place to store the layers 27 | self.pool_size = None # size of pooling area for max pooling 28 | 29 | def load_weights(self, weights): 30 | assert not self.layers, "Weights can only be loaded once!" 31 | for k in range(len(weights.keys())): 32 | self.layers.append(weights['layer_{}'.format(k)]) 33 | 34 | def predict(self, X): 35 | assert not not self.layers, "Weights must be loaded before making a prediction!" 36 | h = self.cnn_layer(X, layer_i=0, border_mode="full") ; X = h 37 | h = self.relu_layer(X) ; X = h 38 | h = self.cnn_layer(X, layer_i=2, border_mode="valid") ; X = h 39 | h = self.relu_layer(X) ; X = h 40 | h = self.maxpooling_layer(X) ; X = h 41 | h = self.dropout_layer(X, .25) ; X = h 42 | h = self.flatten_layer(X) ; X = h 43 | h = self.dense_layer(X, layer_i=7) ; X = h 44 | h = self.relu_layer(X) ; X = h 45 | h = self.dropout_layer(X, .5) ; X = h 46 | h = self.dense_layer(X, layer_i=10) ; X = h 47 | h = self.softmax_layer2D(X) ; X = h 48 | max_i = self.classify(X) 49 | return max_i[0] 50 | 51 | def maxpooling_layer(self, convolved_features): 52 | nb_features = convolved_features.shape[0] 53 | nb_images = convolved_features.shape[1] 54 | conv_dim = convolved_features.shape[2] 55 | res_dim = int(conv_dim / self.pool_size) #assumed square shape 56 | 57 | pooled_features = np.zeros((nb_features, nb_images, res_dim, res_dim)) 58 | for image_i in range(nb_images): 59 | for feature_i in range(nb_features): 60 | for pool_row in range(res_dim): 61 | row_start = pool_row * self.pool_size 62 | row_end = row_start + self.pool_size 63 | 64 | for pool_col in range(res_dim): 65 | col_start = pool_col * self.pool_size 66 | col_end = col_start + self.pool_size 67 | 68 | patch = convolved_features[feature_i, image_i, row_start : row_end,col_start : col_end] 69 | pooled_features[feature_i, image_i, pool_row, pool_col] = np.max(patch) 70 | return pooled_features 71 | 72 | def cnn_layer(self, X, layer_i=0, border_mode = "full"): 73 | features = self.layers[layer_i]["param_0"] 74 | bias = self.layers[layer_i]["param_1"] 75 | 76 | patch_dim = features[0].shape[-1] 77 | nb_features = features.shape[0] 78 | image_dim = X.shape[2] #assume image square 79 | image_channels = X.shape[1] 80 | nb_images = X.shape[0] 81 | 82 | if border_mode == "full": 83 | conv_dim = image_dim + patch_dim - 1 84 | elif border_mode == "valid": 85 | conv_dim = image_dim - patch_dim + 1 86 | convolved_features = np.zeros((nb_images, nb_features, conv_dim, conv_dim)); 87 | for image_i in range(nb_images): 88 | for feature_i in range(nb_features): 89 | convolved_image = np.zeros((conv_dim, conv_dim)) 90 | for channel in range(image_channels): 91 | feature = features[feature_i, channel, :, :] 92 | 93 | image = X[image_i, channel, :, :] 94 | convolved_image += self.convolve2d(image, feature, border_mode); 95 | 96 | convolved_image = convolved_image + bias[feature_i] 97 | convolved_features[image_i, feature_i, :, :] = convolved_image 98 | return convolved_features 99 | 100 | def dense_layer(self, X, layer_i=0): 101 | W = self.layers[layer_i]["param_0"] 102 | b = self.layers[layer_i]["param_1"] 103 | output = np.dot(X, W) + b 104 | return output 105 | 106 | @staticmethod 107 | def convolve2d(image, feature, border_mode="full"): 108 | image_dim = np.array(image.shape) 109 | feature_dim = np.array(feature.shape) 110 | target_dim = image_dim + feature_dim - 1 111 | fft_result = np.fft.fft2(image, target_dim) * np.fft.fft2(feature, target_dim) 112 | target = np.fft.ifft2(fft_result).real 113 | 114 | if border_mode == "valid": 115 | # To compute a valid shape, either np.all(x_shape >= y_shape) or 116 | # np.all(y_shape >= x_shape). 117 | valid_dim = image_dim - feature_dim + 1 118 | if np.any(valid_dim < 1): 119 | valid_dim = feature_dim - image_dim + 1 120 | start_i = (target_dim - valid_dim) // 2 121 | end_i = start_i + valid_dim 122 | target = target[start_i[0]:end_i[0], start_i[1]:end_i[1]] 123 | return target 124 | 125 | @staticmethod 126 | def shuffle(X, y): 127 | assert X.shape[0] == y.shape[0], "X and y first dimensions must match" 128 | p = np.random.permutation(X.shape[0]) 129 | return X[p], y[p] 130 | 131 | @staticmethod 132 | def vectorize(y, vocab): 133 | nb_classes = len(vocab) 134 | Y = np.zeros((len(y), nb_classes)) 135 | for i in range(len(y)): 136 | index = np.where(np.char.find(vocab, y[i]) > -1)[0][0] 137 | Y[i, index] = 1. 138 | return Y 139 | 140 | @staticmethod 141 | def trtest_split(X,y,fraction): 142 | boundary=int(X.shape[0]*fraction) 143 | return (X[:boundary], y[:boundary]), (X[boundary:], y[boundary:]) 144 | 145 | @staticmethod 146 | def sigmoid(x): 147 | return 1.0/(1.0+np.exp(-x)) 148 | 149 | @staticmethod 150 | def hard_sigmoid(x): 151 | slope = 0.2 152 | shift = 0.5 153 | x = (x * slope) + shift 154 | x = np.clip(x, 0, 1) 155 | return x 156 | 157 | @staticmethod 158 | def relu_layer(x): 159 | z = np.zeros_like(x) 160 | return np.where(x>z,x,z) 161 | 162 | @staticmethod 163 | def softmax_layer2D(w): 164 | maxes = np.amax(w, axis=1) 165 | maxes = maxes.reshape(maxes.shape[0], 1) 166 | e = np.exp(w - maxes) 167 | dist = e / np.sum(e, axis=1, keepdims=True) 168 | return dist 169 | 170 | @staticmethod 171 | def repeat_vector(X, n): 172 | y = np.ones((X.shape[0], n, X.shape[2])) * X 173 | return y 174 | 175 | @staticmethod 176 | def dropout_layer(X, p): 177 | retain_prob = 1. - p 178 | X *= retain_prob 179 | return X 180 | 181 | @staticmethod 182 | def classify(X): 183 | return X.argmax(axis=-1) 184 | 185 | @staticmethod 186 | def flatten_layer(X): 187 | flatX = np.zeros((X.shape[0],np.prod(X.shape[1:]))) 188 | for i in range(X.shape[0]): 189 | flatX[i,:] = X[i].flatten(order='C') 190 | return flatX 191 | -------------------------------------------------------------------------------- /app/model/alpha_cnn_predict.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/model/alpha_cnn_predict.pyc -------------------------------------------------------------------------------- /app/model/preprocessor.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import numpy as np 3 | import os.path 4 | import base64 5 | from PIL import Image 6 | import sys 7 | 8 | class Preprocessor: 9 | def __init__(self, fn="dataset.txt"): 10 | self.datafile = fn 11 | self.target_shape = [20,20] 12 | self.splitchar = "&" 13 | 14 | if os.path.isfile(self.datafile): 15 | with open(fn, 'r') as f: 16 | self.vocab = eval(f.readline()) 17 | assert type(self.vocab) is list, "The first line of the file was not a vocab list." 18 | else: 19 | self.vocab = None 20 | 21 | def add_sample(self, data, index, vocab): 22 | assert self.vocab == vocab or self.vocab is None, "Vocab list has changed. You cannot append to this dataset" 23 | if self.vocab is None: 24 | with open(self.datafile, 'w') as f: 25 | v = str(vocab).replace("\n","") 26 | f.write(v) 27 | self.vocab = vocab 28 | n = self.preprocess(data) 29 | with open(self.datafile, "a") as f: 30 | n = str(n.tolist()).replace("\n","") 31 | f.write("\n" + str(self.vocab[index]) + self.splitchar + n) 32 | 33 | def load_sample(self, line_i = 0): 34 | X = None ; y = None 35 | if os.path.isfile(self.datafile): 36 | with open(self.datafile, 'r') as f: 37 | vocab = eval(f.readline()) 38 | assert vocab == self.vocab, "The vocab for this datafile does not match the current vocab" 39 | 40 | with open(self.datafile, "r") as f: 41 | lines = f.readlines() 42 | X = np.asarray(eval(lines[line_i + 1].split(self.splitchar)[1])) 43 | y = lines[line_i + 1].split(self.splitchar)[0] 44 | return [X, y] 45 | 46 | else: 47 | raise ValueError("Could not find the datafile") 48 | 49 | def load_all(self): 50 | X = None ; y = None 51 | if os.path.isfile(self.datafile): 52 | with open(self.datafile, 'r') as f: 53 | vocab = eval(f.readline()) 54 | assert vocab == self.vocab, "The vocab for this datafile does not match the current vocab" 55 | 56 | with open(self.datafile, "r") as f: 57 | lines = f.readlines() 58 | 59 | X = np.zeros((len(lines) - 1, self.target_shape[0]*self.target_shape[1])) 60 | y = [] 61 | 62 | for i in range (len(lines)-1): 63 | X[i,:] = np.asarray(eval(lines[i + 1].split(self.splitchar)[1])) 64 | y.append(lines[i + 1].split(self.splitchar)[0]) 65 | y = np.asarray(y) 66 | return [X, y] 67 | 68 | else: 69 | raise ValueError("Could not find the datafile") 70 | 71 | def preprocess(self, jpgtxt): 72 | # data = base64.decodestring(data) 73 | data = jpgtxt.split(',')[-1] 74 | data = base64.b64decode(data.encode('ascii')) 75 | 76 | g = open("temp.jpg", "wb") 77 | g.write(data) 78 | g.close() 79 | 80 | pic = Image.open("temp.jpg") 81 | M = np.array(pic) #now we have image data in numpy 82 | 83 | M = self.rgb2gray(M) 84 | M = self.squareTrim(M,threshold=0) 85 | M = self.naiveInterp2D(M,self.target_shape[0],self.target_shape[0]) 86 | [N, mean, sigma] = self.normalize(M) 87 | n = N.reshape(-1) 88 | if np.isnan(np.sum(n)): 89 | n = np.zeros(n.shape) 90 | return n 91 | 92 | def dataset_length(self): 93 | with open(self.datafile) as f: 94 | for i, l in enumerate(f): 95 | pass 96 | return i + 1 97 | 98 | @staticmethod 99 | def squareTrim(M, min_side=20, threshold=0): 100 | assert M.shape[0]==M.shape[1],"Input matrix must be a square" 101 | wsum = np.sum(M,axis=0) 102 | nonzero = np.where(wsum > threshold*M.shape[1])[0] 103 | if len(nonzero) >=1: 104 | wstart = nonzero[0] 105 | wend = nonzero[-1] 106 | else: 107 | wstart=0 ; wend = 0 108 | 109 | hsum = np.sum(M,axis=1) 110 | nonzero = np.where(hsum > threshold*M.shape[0])[0] 111 | if len(nonzero) >=1: 112 | hstart = nonzero[0] 113 | hend = nonzero[-1] 114 | else: 115 | hstart=0 ; hend = 0 116 | 117 | diff = abs((wend-wstart) - (hend-hstart)) 118 | if (wend-wstart > hend-hstart): 119 | side = max(wend-wstart+1, min_side) 120 | m = np.zeros((side, side)) 121 | cropped = M[hstart:hend+1,wstart:wend+1] 122 | shift = diff/2 123 | m[shift:cropped.shape[0]+shift,:cropped.shape[1]] = cropped 124 | else: 125 | side = max(hend-hstart+1, min_side) 126 | m = np.zeros((side, side)) 127 | cropped = M[hstart:hend+1,wstart:wend+1] 128 | shift=diff/2 129 | m[:cropped.shape[0],shift:cropped.shape[1]+shift] = cropped 130 | return m 131 | 132 | @staticmethod 133 | def rgb2gray(rgb): 134 | r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2] 135 | gray = 0.2989 * r + 0.5870 * g + 0.1140 * b 136 | return gray 137 | 138 | @staticmethod 139 | def naiveInterp2D(M, newx, newy): 140 | result = np.zeros((newx,newy)) 141 | for i in range(M.shape[0]): 142 | for j in range(M.shape[1]): 143 | indx = i*newx / M.shape[0] 144 | indy = j*newy / M.shape[1] 145 | result[indx,indy] +=M[i,j] 146 | return result 147 | 148 | @staticmethod 149 | def normalize(M): 150 | sigma = np.std(M) 151 | mean = np.mean(M) 152 | return [(M-mean)/sigma, mean, sigma] 153 | -------------------------------------------------------------------------------- /app/model/preprocessor.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/Convolutional_neural_network/28fe10899301fc3cb18b1e629e7fe57780bcaca4/app/model/preprocessor.pyc -------------------------------------------------------------------------------- /app/static/analytics.js: -------------------------------------------------------------------------------- 1 | (function(){var $c=function(a){this.w=a||[]};$c.prototype.set=function(a){this.w[a]=!0};$c.prototype.encode=function(){for(var a=[],b=0;b=b.length)wc(a,b,c);else if(8192>=b.length)x(a,b,c)||wd(a,b,c)||wc(a,b,c);else throw ge("len",b.length),new Da(b.length);},wc=function(a,b,c){var d=ta(a+"?"+b);d.onload=d.onerror=function(){d.onload=null;d.onerror=null;c()}},wd=function(a,b,c){var d=O.XMLHttpRequest;if(!d)return!1;var e=new d;if(!("withCredentials"in e))return!1; 5 | e.open("POST",a,!0);e.withCredentials=!0;e.setRequestHeader("Content-Type","text/plain");e.onreadystatechange=function(){4==e.readyState&&(c(),e=null)};e.send(b);return!0},x=function(a,b,c){return O.navigator.sendBeacon?O.navigator.sendBeacon(a,b)?(c(),!0):!1:!1},ge=function(a,b,c){1<=100*Math.random()||Aa("?")||(a=["t=error","_e="+a,"_v=j41","sr=1"],b&&a.push("_f="+b),c&&a.push("_m="+K(c.substring(0,100))),a.push("aip=1"),a.push("z="+hd()),wc(oc()+"/collect",a.join("&"),ua))};var Ha=function(){this.M=[]};Ha.prototype.add=function(a){this.M.push(a)};Ha.prototype.D=function(a){try{for(var b=0;b=100*R(a,Ka))throw"abort";}function Ma(a){if(Aa(P(a,Na)))throw"abort";}function Oa(){var a=M.location.protocol;if("http:"!=a&&"https:"!=a)throw"abort";} 6 | function Pa(a){try{O.navigator.sendBeacon?J(42):O.XMLHttpRequest&&"withCredentials"in new O.XMLHttpRequest&&J(40)}catch(c){}a.set(ld,Td(a),!0);a.set(Ac,R(a,Ac)+1);var b=[];Qa.map(function(c,d){if(d.F){var e=a.get(c);void 0!=e&&e!=d.defaultValue&&("boolean"==typeof e&&(e*=1),b.push(d.F+"="+K(""+e)))}});b.push("z="+Bd());a.set(Ra,b.join("&"),!0)} 7 | function Sa(a){var b=P(a,gd)||oc()+"/collect",c=P(a,fa);!c&&a.get(Vd)&&(c="beacon");if(c){var d=P(a,Ra),e=a.get(Ia),e=e||ua;"image"==c?wc(b,d,e):"xhr"==c&&wd(b,d,e)||"beacon"==c&&x(b,d,e)||ba(b,d,e)}else ba(b,P(a,Ra),a.get(Ia));a.set(Ia,ua,!0)}function Hc(a){var b=O.gaData;b&&(b.expId&&a.set(Nc,b.expId),b.expVar&&a.set(Oc,b.expVar))}function cd(){if(O.navigator&&"preview"==O.navigator.loadPurpose)throw"abort";}function yd(a){var b=O.gaDevIds;ka(b)&&0!=b.length&&a.set("&did",b.join(","),!0)} 8 | function vb(a){if(!a.get(Na))throw"abort";};var hd=function(){return Math.round(2147483647*Math.random())},Bd=function(){try{var a=new Uint32Array(1);O.crypto.getRandomValues(a);return a[0]&2147483647}catch(b){return hd()}};function Ta(a){var b=R(a,Ua);500<=b&&J(15);var c=P(a,Va);if("transaction"!=c&&"item"!=c){var c=R(a,Wa),d=(new Date).getTime(),e=R(a,Xa);0==e&&a.set(Xa,d);e=Math.round(2*(d-e)/1E3);0=c)throw"abort";a.set(Wa,--c)}a.set(Ua,++b)};var Ya=function(){this.data=new ee},Qa=new ee,Za=[];Ya.prototype.get=function(a){var b=$a(a),c=this.data.get(a);b&&void 0==c&&(c=ea(b.defaultValue)?b.defaultValue():b.defaultValue);return b&&b.Z?b.Z(this,a,c):c};var P=function(a,b){var c=a.get(b);return void 0==c?"":""+c},R=function(a,b){var c=a.get(b);return void 0==c||""===c?0:1*c};Ya.prototype.set=function(a,b,c){if(a)if("object"==typeof a)for(var d in a)a.hasOwnProperty(d)&&ab(this,d,a[d],c);else ab(this,a,b,c)}; 9 | var ab=function(a,b,c,d){if(void 0!=c)switch(b){case Na:wb.test(c)}var e=$a(b);e&&e.o?e.o(a,b,c,d):a.data.set(b,c,d)},bb=function(a,b,c,d,e){this.name=a;this.F=b;this.Z=d;this.o=e;this.defaultValue=c},$a=function(a){var b=Qa.get(a);if(!b)for(var c=0;c=b)){var c=(new Date).getHours(),d=[Bd(),Bd(),Bd()].join(".");a=(3==b||5==b?"https:":"http:")+"//www.google-analytics.com/collect?z=br.";a+=[b,"A",c,d].join(".");var e=1!=b%3?"https:":"http:",e=e+"//www.google-analytics.com/collect?z=br.",e=e+[b,"B",c,d].join(".");7==b&&(e=e.replace("//www.","//ssl."));c=function(){4<=b&&6>=b?O.navigator.sendBeacon(e,""):ta(e)};Bd()%2?(ta(a),c()):(c(),ta(a))}}};function fc(){var a,b,c;if((c=(c=O.navigator)?c.plugins:null)&&c.length)for(var d=0;d=c)&&(c={},Ec(c)||Fc(c))){var d=c[Eb];void 0==d||Infinity==d||isNaN(d)||(0c)a[b]=void 0},Fd=function(a){return function(b){"pageview"!=b.get(Va)||a.I||(a.I=!0,gc(b,function(b){a.send("timing",b)}))}};var hc=!1,mc=function(a){if("cookie"==P(a,ac)){var b=P(a,U),c=nd(a),d=kc(P(a,Yb)),e=lc(P(a,W)),g=1E3*R(a,Zb),ca=P(a,Na);if("auto"!=e)zc(b,c,d,e,ca,g)&&(hc=!0);else{J(32);var l;a:{c=[];e=xa().split(".");if(4==e.length&&(l=e[e.length-1],parseInt(l,10)==l)){l=["none"];break a}for(l=e.length-2;0<=l;l--)c.push(e.slice(l).join("."));c.push("none");l=c}for(var k=0;k=a&&d.push({hash:ca[0],R:e[g],O:ca})}return 0==d.length?void 0:1==d.length?d[0]:Zc(b,d)||Zc(c,d)||Zc(null,d)||d[0]}function Zc(a,b){var c,d;null==a?c=d=1:(c=La(a),d=La(D(a,".")?a.substring(1):"."+a));for(var e=0;ed.length)){c=[];for(var e=0;e=ca[0]||0>=ca[1]?"":ca.join("x");a.set(rb,c);a.set(tb, 31 | fc());a.set(ob,M.characterSet||M.charset);a.set(sb,b&&"function"===typeof b.javaEnabled&&b.javaEnabled()||!1);a.set(nb,(b&&(b.language||b.browserLanguage)||"").toLowerCase());if(d&&a.get(cc)&&(b=M.location.hash)){b=b.split(/[?&#]+/);d=[];for(c=0;carguments.length)){var b,c;"string"===typeof arguments[0]?(b=arguments[0],c=[].slice.call(arguments,1)):(b=arguments[0]&&arguments[0][Va],c=arguments);b&&(c=za(qc[b]||[],c),c[Va]=b,this.b.set(c,void 0,!0),this.filters.D(this.b),this.b.data.m={},je(this.b))}};var rc=function(a){if("prerender"==M.visibilityState)return!1;a();return!0};var td=/^(?:(\w+)\.)?(?:(\w+):)?(\w+)$/,sc=function(a){if(ea(a[0]))this.u=a[0];else{var b=td.exec(a[0]);null!=b&&4==b.length&&(this.c=b[1]||"t0",this.K=b[2]||"",this.C=b[3],this.a=[].slice.call(a,1),this.K||(this.A="create"==this.C,this.i="require"==this.C,this.g="provide"==this.C,this.ba="remove"==this.C),this.i&&(3<=this.a.length?(this.X=this.a[1],this.W=this.a[2]):this.a[1]&&(qa(this.a[1])?this.X=this.a[1]:this.W=this.a[1])));b=a[1];a=a[2];if(!this.C)throw"abort";if(this.i&&(!qa(b)||""==b))throw"abort"; 33 | if(this.g&&(!qa(b)||""==b||!ea(a)))throw"abort";if(ud(this.c)||ud(this.K))throw"abort";if(this.g&&"t0"!=this.c)throw"abort";}};function ud(a){return 0<=a.indexOf(".")||0<=a.indexOf(":")};var Yd,Zd,$d;Yd=new ee;$d=new ee;Zd={ec:45,ecommerce:46,linkid:47}; 34 | var ae=function(a){function b(a){var b=(a.hostname||"").split(":")[0].toLowerCase(),c=(a.protocol||"").toLowerCase(),c=1*a.port||("http:"==c?80:"https:"==c?443:"");a=a.pathname||"";D(a,"/")||(a="/"+a);return[b,""+c,a]}var c=M.createElement("a");c.href=M.location.href;var d=(c.protocol||"").toLowerCase(),e=b(c),g=c.search||"",ca=d+"//"+e[0]+(e[1]?":"+e[1]:"");D(a,"//")?a=d+a:D(a,"/")?a=ca+a:!a||D(a,"?")?a=ca+e[2]+(a||g):0>a.split("/")[0].indexOf(":")&&(a=ca+e[2].substring(0,e[2].lastIndexOf("/"))+ 35 | "/"+a);c.href=a;d=b(c);return{protocol:(c.protocol||"").toLowerCase(),host:d[0],port:d[1],path:d[2],query:c.search||"",url:a||""}};var Z={ga:function(){Z.f=[]}};Z.ga();Z.D=function(a){var b=Z.J.apply(Z,arguments),b=Z.f.concat(b);for(Z.f=[];0c;c++){var d=b[c].src;if(d&&0==d.indexOf("https://www.google-analytics.com/analytics")){J(33);b=!0;break a}}b= 40 | !1}b&&(Ba=!0)}Ud()||Ba||!Ed(new Od)||(J(36),Ba=!0);(O.gaplugins=O.gaplugins||{}).Linker=Dc;b=Dc.prototype;Yd.set("linker",Dc);X("decorate",b,b.ca,20);X("autoLink",b,b.S,25);Yd.set("displayfeatures",fd);Yd.set("adfeatures",fd);a=a&&a.q;ka(a)?Z.D.apply(N,a):J(50)}};N.da=function(){for(var a=N.getAll(),b=0;b>21:b;return b};})(window); 42 | -------------------------------------------------------------------------------- /app/static/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-o-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#2d6ca2));background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary:disabled,.btn-primary[disabled]{background-color:#2d6ca2;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f3f3f3));background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-o-linear-gradient(top,#222 0,#282828 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#222),to(#282828));background-image:linear-gradient(to bottom,#222 0,#282828 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-o-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3071a9));background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{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)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-o-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3278b3));background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /app/static/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('