├── README.md ├── downloads ├── Patient1.zip └── Patient2.zip ├── license.txt ├── mainapp.py ├── static ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── carousel.css │ ├── custom.css │ └── styles.css ├── fonts │ ├── Arial Black.ttf │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── img │ ├── Fig1 copy.jpg │ ├── Fig1.jpg │ ├── Fig2 copy.jpg │ ├── Fig2.jpg │ ├── Fig3 copy.jpg │ ├── Fig3.jpg │ ├── Preview1.jpg │ ├── Preview2.jpg │ ├── ajax-loader.gif │ ├── ajaxloader.gif │ └── done.png └── js │ ├── bootstrap.js │ ├── bootstrap.js.old │ ├── bootstrap.min.js │ ├── bootstrap.min.js.old │ ├── display.js │ ├── download.js │ ├── hover.js │ ├── jquery.fileDownload.js │ ├── jquery.filedrop.js │ ├── jquery.mooscan.js │ ├── submit.js │ └── upload.js └── templates └── index.html /README.md: -------------------------------------------------------------------------------- 1 | Chest Detect Computer-Aided Detection Tool 2 | ========================================== 3 | 4 | A Python-based JavaScript/HTML5 computer-aided detection (CAD) tool for pulmonary nodules. Try it [here](http://chestdetect.com). 5 | 6 | Email Jason D. Balkman, MD (jshining@gmail.com) for all coding/implementation contributions and comments. 7 | 8 | Released under GNU GPL license (see [license.txt](license.txt)). -------------------------------------------------------------------------------- /downloads/Patient1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/downloads/Patient1.zip -------------------------------------------------------------------------------- /downloads/Patient2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/downloads/Patient2.zip -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | /** 2 | @licstart The following is the entire license notice for the JavaScript code in this page. 3 | 4 | Copyright (C) 2014 Jason D. Balkman, MD 5 | 6 | The JavaScript code in this page is free software: you can 7 | redistribute it and/or modify it under the terms of the GNU 8 | General Public License (GNU GPL) as published by the Free Software 9 | Foundation, either version 3 of the License, or (at your option) 10 | any later version. The code is distributed WITHOUT ANY WARRANTY; 11 | without even the implied warranty of MERCHANTABILITY or FITNESS 12 | FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. 13 | 14 | As additional permission under GNU GPL version 3 section 7, you 15 | may distribute non-source (e.g., minimized or compacted) forms of 16 | that code without the copy of the GNU GPL normally required by 17 | section 4, provided you include this license notice and a URL 18 | through which recipients can access the Corresponding Source. 19 | 20 | @licend The above is the entire license notice for the JavaScript code in this page. 21 | */ 22 | -------------------------------------------------------------------------------- /mainapp.py: -------------------------------------------------------------------------------- 1 | # Math (can still use '//' if want integer output; this library gives floats) 2 | from __future__ import division 3 | 4 | # General 5 | import os, re 6 | import math, cmath 7 | import cStringIO, StringIO 8 | from datetime import datetime 9 | from flask import Flask, render_template, jsonify, redirect, url_for, request, send_file 10 | 11 | # Image Processing 12 | import numpy as np 13 | from numpy.linalg import inv 14 | import numpy.ma as ma 15 | import cv2, cv 16 | import matplotlib.pyplot as plt 17 | import mahotas 18 | from skimage.restoration import denoise_tv_chambolle, denoise_bilateral 19 | from skimage.feature import blob_doh, peak_local_max 20 | 21 | # Image Drawing 22 | from PIL import ImageFont 23 | from PIL import ImageDraw 24 | from PIL import Image 25 | 26 | # Storage 27 | from boto.s3.connection import S3Connection 28 | from boto.s3.key import Key 29 | 30 | # SciPy 31 | from scipy import ndimage 32 | from scipy import signal 33 | from scipy.signal import argrelextrema 34 | from skimage import morphology 35 | from skimage.morphology import watershed, disk, ball, cube, opening, octahedron 36 | from skimage import data 37 | from skimage.filter import rank, threshold_otsu, canny 38 | from skimage.util import img_as_ubyte 39 | 40 | #--------------------------# 41 | # GLOBALS # 42 | #--------------------------# 43 | 44 | LOGFILE = 'pnod_results.txt' 45 | FONT_PATH = '/Library/Fonts/' 46 | DEBUG = False 47 | 48 | app = Flask(__name__) 49 | app.config.from_object(__name__) 50 | app.config['UPLOAD_FOLDER'] = 'uploads' # change to relative /var/www/ file 51 | 52 | ALLOWED_EXTENSIONS = ['pdf', 'png', 'jpg', 'jpeg', 'gif', 'tif', 'tiff', 'dcm', 'dicom'] 53 | TIFF_EXTENSIONS = ['tif', 'tiff'] 54 | DICOM_EXTENSIONS = ['dcm', 'dicom'] 55 | PNG_EXTENSIONS = ['png'] 56 | JPG_EXTENSIONS = ['jpg', 'jpeg'] 57 | DEBUG = False 58 | FONT_PATH = 'static/fonts/' 59 | ACCESS_KEY = '' 60 | SECRET_KEY = '' 61 | 62 | @app.route('/') 63 | def hello_world(): 64 | print 'Hello World!' 65 | return render_template('index.html') 66 | 67 | @app.route('/process_serve_chestct', methods=['GET']) 68 | def process_serve_chestct(): 69 | 70 | # Init 71 | imgfile = request.args.get('imgfile') 72 | print "Process/Serving Image: "+imgfile 73 | fnamesplit = imgfile.rsplit('.', 1) 74 | ext = fnamesplit[1] 75 | imgprefix = fnamesplit[0] 76 | 77 | prfxsplt = imgprefix.rsplit('-', 2) 78 | prefix = prfxsplt[0] 79 | nfiles = int(prfxsplt[1]) 80 | idx = int(prfxsplt[2]) 81 | 82 | matrix3D = np.array([]) 83 | initFile = True 84 | imgarr = [] 85 | 86 | conn = S3Connection(ACCESS_KEY, SECRET_KEY) 87 | bkt = conn.get_bucket('chestcad') 88 | k = Key(bkt) 89 | 90 | print prefix, str(nfiles) 91 | try: 92 | for x in range(0,nfiles): 93 | mykey = prefix+'-'+str(nfiles)+'-'+str(x) 94 | k.key = mykey 95 | 96 | # NEW: Process file here... 97 | fout = cStringIO.StringIO() 98 | k.get_contents_to_file(fout) 99 | if initFile: 100 | matrix3D = readChestCTFile(fout) 101 | else: 102 | matrix3D = np.concatenate((matrix3D,readChestCTFile(fout)), axis=2) 103 | initFile = False 104 | result = 1 105 | except: 106 | result = 0 107 | 108 | dimension = matrix3D.shape[0] 109 | panel1, panel2, panel3, panel4 = processMatrix(matrix3D, dimension) 110 | print 'prior to printing matrix' 111 | imgarr, data_encode = printMatrix(panel1, panel2, panel3, panel4, prefix, nfiles) 112 | 113 | return jsonify({"success":result,"imagefile":data_encode,"imgarr":imgarr}) 114 | 115 | @app.route('/upload', methods=['POST']) 116 | def upload(): 117 | 118 | if request.method == 'POST': 119 | file = request.files['file'] 120 | if file and allowed_file(file.filename): 121 | now = datetime.now() 122 | 123 | # Naming and storage to S3 database 124 | prefix = file.filename.rsplit('.', 1)[0] 125 | 126 | conn = S3Connection(ACCESS_KEY, SECRET_KEY) 127 | bkt = conn.get_bucket('chestcad') 128 | k = Key(bkt) 129 | k.key = prefix 130 | if istiff(file.filename): 131 | k.set_contents_from_file(file, headers={"Content-Type":"image/tiff"}) 132 | elif isjpg(file.filename): 133 | k.set_contents_from_file(file, headers={"Content-Type":"image/jpeg"}) 134 | elif ispng(file.filename): 135 | k.set_contents_from_file(file, headers={"Content-Type":"image/png"}) 136 | elif isdicom(file.filename): 137 | ds = dicom.read_file(file) 138 | pil_dcm = get_dicom_PIL(ds) 139 | pil_dcm_str = cStringIO.StringIO() 140 | pil_dcm.save(pil_dcm_str, format='tiff') 141 | pil_dcm_str.seek(0) 142 | k.set_contents_from_file(pil_dcm_str, headers={"Content-Type":"image/tiff"}) 143 | else: 144 | k.set_contents_from_file(file) # don't suspect that this will work 145 | 146 | return jsonify({"success":True, "file": file.filename}) # passes to upload.js, function uploadFinished 147 | 148 | #----------------------------# 149 | # FUNCTIONS # 150 | #----------------------------# 151 | 152 | def get_label_size(a): 153 | return 1 154 | 155 | def thresh(a, b, max_value, C): 156 | return max_value if a > b - C else 0 157 | 158 | def block_size(size): 159 | block = np.ones((size, size), dtype='d') 160 | block[(size - 1 ) / 2, (size - 1 ) / 2] = 0 161 | return block 162 | 163 | 164 | def custom_square(n): 165 | mycustom = np.ones((n,n,1), dtype=np.int) 166 | 167 | return mycustom 168 | 169 | def custom_cross(n): 170 | mycustom = np.zeros((n,n,1), dtype=np.int) 171 | mycustom[0:n,1:n-1] = 1 172 | mycustom[1:n-1,0:n] = 1 173 | 174 | return mycustom 175 | 176 | def get_number_neighbours(mask,block): 177 | '''returns number of unmasked neighbours of every element within block''' 178 | mask = mask / 255.0 179 | return signal.convolve2d(mask, block, mode='same', boundary='symm') 180 | #return signal.fftconvolve(mask, block, mode='same') 181 | 182 | def masked_adaptive_threshold(image,mask,max_value,size,C): 183 | '''thresholds only using the unmasked elements''' 184 | block = block_size(size) 185 | conv = signal.convolve2d(image, block, mode='same', boundary='symm') 186 | 187 | #conv = signal.fftconvolve(image, block, mode='same') 188 | mean_conv = conv / get_number_neighbours(mask,block) 189 | 190 | return v_thresh(image, mean_conv, max_value,C) 191 | 192 | def motsu(im,iz): 193 | th = mahotas.otsu(im, ignore_zeros = iz) 194 | int_out, binary_sum, intinv_out, binaryinv_sum = bintoint(im, th) 195 | 196 | return binary_sum, int_out, binaryinv_sum, intinv_out 197 | 198 | def bintoint(im, thresh): 199 | bool_out = im > thresh 200 | binary_out = bool_out.astype('uint8') 201 | binaryinv_out = np.logical_not(bool_out).astype('uint8') 202 | #opened_out = ndimage.binary_opening(binary_out, structure=np.ones((2,2))).astype('uint8') 203 | tot = binary_out.sum() 204 | out = binary_out * 255 205 | 206 | totinv = binaryinv_out.sum() 207 | outinv = binaryinv_out * 255 208 | 209 | return out, tot, outinv, totinv 210 | 211 | def lpf(im, s, o, d): 212 | # Apply Large Gaussian Filter To Cropped Image 213 | blur = ndimage.gaussian_filter(im, (s,s), order=o)/d 214 | 215 | # Apply Subtraction 216 | crpint = im.astype('int') 217 | subtracted = np.zeros_like(im) 218 | np.clip(crpint-blur, 0, 255, out=subtracted) # the choice between 0 and 1 is based on the OTSU calculation, or whether or not to include all fat pixels 219 | return subtracted, blur 220 | 221 | # Same as LPF above but don't subtract values below a certain level 222 | def lpf2(im, s, o, d): 223 | # Apply Large Gaussian Filter To Cropped Image 224 | blur = ndimage.gaussian_filter(im, (s,s), order=o)/d 225 | 226 | m = np.average(blur[blur[:,:] > 15]) 227 | 228 | low_values_indices = blur < m # Where values are low 229 | blur[low_values_indices] = 0 230 | 231 | # Apply Subtraction 232 | crpint = im.astype('int') 233 | subtracted = np.zeros_like(im) 234 | np.clip(crpint-blur, 0, 255, out=subtracted) # the choice between 0 and 1 is based on the OTSU calculation, or whether or not to include all fat pixels 235 | return subtracted, blur 236 | 237 | def allowed_file(filename): 238 | return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS 239 | 240 | def isjpg(filename): 241 | return '.' in filename and filename.rsplit('.', 1)[1] in JPG_EXTENSIONS 242 | 243 | def ispng(filename): 244 | return '.' in filename and filename.rsplit('.', 1)[1] in PNG_EXTENSIONS 245 | 246 | def istiff(filename): 247 | return '.' in filename and filename.rsplit('.', 1)[1] in TIFF_EXTENSIONS 248 | 249 | def isdicom(filename): 250 | return '.' in filename and filename.rsplit('.', 1)[1] in DICOM_EXTENSIONS 251 | 252 | def readChestCTFile(f): 253 | 254 | # Read the file into an array 255 | array = np.frombuffer(f.getvalue(), dtype='uint8') # or use uint16? 256 | img = cv2.imdecode(array, cv2.CV_LOAD_IMAGE_GRAYSCALE) 257 | imarray = np.array(img) 258 | 259 | imarray = denoise_tv_chambolle(imarray, weight=0.001, multichannel=False) 260 | imarray = (imarray*255).astype('uint8') 261 | 262 | slice3D = np.expand_dims(imarray, axis=2) 263 | 264 | return slice3D 265 | 266 | def processMatrix(mtx,dim): 267 | 268 | #-------------------# 269 | # LPF # 270 | #-------------------# 271 | 272 | original = mtx 273 | 274 | # Apply Large Gaussian Filter To Cropped Image 275 | #gmtx = ndimage.gaussian_filter(mtx, (2,2,2), order=0) 276 | gmtx = mtx 277 | gmtx = (gmtx > gmtx.mean()) 278 | gmtx = gmtx.astype('uint8') 279 | gmtx = gmtx * 255 280 | print gmtx.shape 281 | 282 | #------------------------# 283 | # DENOISING # 284 | #------------------------# 285 | erodim = 4 # erosion dimension 286 | cr = 3 # custom radius for the structured 287 | 288 | if dim == 512: 289 | erodim = 4 290 | cr = 3 291 | elif dim == 712: 292 | erodim = 5 293 | cr = 5 294 | 295 | gmtx_eroded = ndimage.binary_erosion(gmtx, structure=custom_cross(erodim)).astype(gmtx.dtype) 296 | #gmtx_eroded = ndimage.binary_erosion(gmtx, structure=myball).astype(gmtx.dtype) 297 | gmtx_eroded = gmtx_eroded.astype('uint8') 298 | eroded = gmtx_eroded * 255 299 | 300 | #gmtx_eroded = gmtx 301 | #eroded = gmtx_eroded 302 | 303 | #---------------------# 304 | # SKIMAGE # 305 | #---------------------# 306 | markers, nummarks = ndimage.label(gmtx_eroded) 307 | bins = np.bincount(markers.flatten()) 308 | bins[0] = 0 309 | cwmark = np.argmax(bins) 310 | for zdim in xrange(markers.shape[2]): 311 | for col in xrange(markers.shape[1]): 312 | first = np.argmax(markers[:,col,zdim] == cwmark) 313 | last = markers.shape[1] - 1 - np.argmax(markers[::-1,col,zdim] == cwmark) 314 | markers[0:first,col,zdim] = cwmark 315 | markers[last:,col,zdim] = cwmark 316 | 317 | #markers = markers.astype('uint8') 318 | markers[markers == cwmark] = 0 # in markers image, 0 is background and 1 is the largest blob (i.e. chest wall & mediastinum) 319 | markers = markers > 0 320 | markers = markers.astype('uint8') 321 | 322 | myelem = custom_square(cr) 323 | opened = ndimage.morphology.binary_opening(markers, myelem) 324 | opened = opened.astype('uint8') 325 | 326 | markers, nummarks = ndimage.label(opened) 327 | opened = opened * 255 328 | 329 | bins = np.bincount(markers.flatten()) 330 | 331 | for i in range(1, nummarks+1): 332 | if bins[i] > 10: 333 | com = ndimage.measurements.center_of_mass(markers == i) 334 | print com 335 | tmpimg_orig = np.array(original[:,:,int(com[2])]) 336 | tmpimg_open = np.array(opened[:,:,int(com[2])]) 337 | cv2.circle(tmpimg_orig,(int(com[1]),int(com[0])),50,[255,255,255],10) 338 | cv2.circle(tmpimg_open,(int(com[1]),int(com[0])),50,[255,255,255],10) 339 | original[:,:,com[2]] = tmpimg_orig 340 | opened[:,:,com[2]] = tmpimg_open 341 | 342 | return original, eroded, markers, opened 343 | 344 | def printMatrix(p1, p2, p3, p4, pfx, nfls): 345 | 346 | BUFF = 40 347 | 348 | arr = [] 349 | dataenc = None 350 | 351 | conn = S3Connection(ACCESS_KEY, SECRET_KEY) 352 | bkt = conn.get_bucket('chestcad') 353 | k = Key(bkt) 354 | 355 | numslices = p1.shape[2] 356 | 357 | if numslices != nfls: 358 | print 'The number of matrix slices (Z-dim) is not equal to the saved number of files.' 359 | 360 | for z in xrange(nfls): 361 | 362 | mykey = pfx+'-'+str(nfls)+'-'+str(z) 363 | k.key = mykey 364 | 365 | # Paste 4x4 Plot 366 | '''pil_panel1 = Image.fromarray(p1[:,:,z]) 367 | pil_panel2 = Image.fromarray(p2[:,:,z]) 368 | pil_panel3 = Image.fromarray(p3[:,:,z]) 369 | pil_panel4 = Image.fromarray(p4[:,:,z]) 370 | 371 | pil_panel1 = pil_panel1.convert('RGB') 372 | pil_panel2 = pil_panel2.convert('RGB') 373 | pil_panel3 = pil_panel3.convert('RGB') 374 | pil_panel4 = pil_panel4.convert('RGB') 375 | 376 | pw, ph = pil_panel1.size 377 | pil_backdrop = Image.new('RGB', (pw*2+BUFF*3,ph*2+BUFF*3), "white") 378 | 379 | pil_backdrop.paste(pil_panel1,(BUFF,BUFF)) 380 | pil_backdrop.paste(pil_panel2,(pw+BUFF*2,BUFF)) 381 | pil_backdrop.paste(pil_panel3,(BUFF,BUFF*2+ph)) 382 | pil_backdrop.paste(pil_panel4,(pw+BUFF*2,BUFF*2+ph)) 383 | 384 | pil_backdrop.save(outdir+pre+str(z)+'.png')''' 385 | 386 | copyarr = p1[:,:,z].copy() 387 | pil_panel1 = Image.fromarray(copyarr) 388 | pil_panel1 = pil_panel1.convert('RGB') 389 | 390 | imgout = cStringIO.StringIO() 391 | pil_panel1.save(imgout, format='png') 392 | k.set_contents_from_string(imgout.getvalue()) 393 | imgout.close() 394 | 395 | data = k.get_contents_as_string() 396 | #k.delete() # putting the delete here causes premature loss of the image; need to find somewhere else to do it probably performed via outside function when called from javascript 397 | dataenc = data.encode("base64") 398 | arr.append(k.generate_url(3600)) 399 | 400 | return arr, dataenc 401 | 402 | def get_LUT_value(data, window, level): 403 | return np.piecewise(data, 404 | [data <= (level - 0.5 - (window - 1) / 2), 405 | data > (level - 0.5 + (window - 1) / 2)], 406 | [0, 255, lambda data: ((data - (level - 0.5)) / (window - 1) + 0.5) * (255 - 0)]) 407 | 408 | # Display an image using the Python Imaging Library (PIL) 409 | def get_dicom_PIL(dataset): 410 | if ('PixelData' not in dataset): 411 | raise TypeError("Cannot show image -- DICOM dataset does not have pixel data") 412 | if ('WindowWidth' not in dataset) or ('WindowCenter' not in dataset): # can only apply LUT if these values exist 413 | bits = dataset.BitsAllocated 414 | samples = dataset.SamplesPerPixel 415 | if bits == 8 and samples == 1: 416 | mode = "L" 417 | elif bits == 8 and samples == 3: 418 | mode = "RGB" 419 | elif bits == 16: 420 | mode = "I;16" # not sure about this -- PIL source says is 'experimental' and no documentation. Also, should bytes swap depending on endian of file and system?? 421 | else: 422 | raise TypeError("Don't know PIL mode for %d BitsAllocated and %d SamplesPerPixel" % (bits, samples)) 423 | 424 | # PIL size = (width, height) 425 | size = (dataset.Columns, dataset.Rows) 426 | 427 | im = Image.frombuffer(mode, size, dataset.PixelData, "raw", mode, 0, 1) # Recommended from the original code to specify all details by http://www.pythonware.com/library/pil/handbook/image.html; this is experimental... 428 | else: 429 | image = get_LUT_value(dataset.pixel_array, dataset.WindowWidth, dataset.WindowCenter) 430 | im = Image.fromarray(np.uint8(image)).convert('L') # Convert mode to L since LUT has only 256 values: http://www.pythonware.com/library/pil/handbook/image.htm 431 | 432 | return im 433 | 434 | if __name__ == '__main__': 435 | app.run(debug=True) 436 | -------------------------------------------------------------------------------- /static/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 16 | } 17 | 18 | .btn-default:active, 19 | .btn-primary:active, 20 | .btn-success:active, 21 | .btn-info:active, 22 | .btn-warning:active, 23 | .btn-danger:active, 24 | .btn-default.active, 25 | .btn-primary.active, 26 | .btn-success.active, 27 | .btn-info.active, 28 | .btn-warning.active, 29 | .btn-danger.active { 30 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 31 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 32 | } 33 | 34 | .btn:active, 35 | .btn.active { 36 | background-image: none; 37 | } 38 | 39 | .btn-default { 40 | text-shadow: 0 1px 0 #fff; 41 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); 42 | background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%); 43 | background-repeat: repeat-x; 44 | border-color: #dbdbdb; 45 | border-color: #ccc; 46 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 47 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 48 | } 49 | 50 | .btn-default:hover, 51 | .btn-default:focus { 52 | background-color: #e0e0e0; 53 | background-position: 0 -15px; 54 | } 55 | 56 | .btn-default:active, 57 | .btn-default.active { 58 | background-color: #e0e0e0; 59 | border-color: #dbdbdb; 60 | } 61 | 62 | .btn-primary { 63 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 64 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); 65 | background-repeat: repeat-x; 66 | border-color: #2b669a; 67 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); 68 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 69 | } 70 | 71 | .btn-primary:hover, 72 | .btn-primary:focus { 73 | background-color: #2d6ca2; 74 | background-position: 0 -15px; 75 | } 76 | 77 | .btn-primary:active, 78 | .btn-primary.active { 79 | background-color: #2d6ca2; 80 | border-color: #2b669a; 81 | } 82 | 83 | .btn-success { 84 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 85 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 86 | background-repeat: repeat-x; 87 | border-color: #3e8f3e; 88 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 89 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 90 | } 91 | 92 | .btn-success:hover, 93 | .btn-success:focus { 94 | background-color: #419641; 95 | background-position: 0 -15px; 96 | } 97 | 98 | .btn-success:active, 99 | .btn-success.active { 100 | background-color: #419641; 101 | border-color: #3e8f3e; 102 | } 103 | 104 | .btn-warning { 105 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 106 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 107 | background-repeat: repeat-x; 108 | border-color: #e38d13; 109 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 110 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 111 | } 112 | 113 | .btn-warning:hover, 114 | .btn-warning:focus { 115 | background-color: #eb9316; 116 | background-position: 0 -15px; 117 | } 118 | 119 | .btn-warning:active, 120 | .btn-warning.active { 121 | background-color: #eb9316; 122 | border-color: #e38d13; 123 | } 124 | 125 | .btn-danger { 126 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 127 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 128 | background-repeat: repeat-x; 129 | border-color: #b92c28; 130 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 131 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 132 | } 133 | 134 | .btn-danger:hover, 135 | .btn-danger:focus { 136 | background-color: #c12e2a; 137 | background-position: 0 -15px; 138 | } 139 | 140 | .btn-danger:active, 141 | .btn-danger.active { 142 | background-color: #c12e2a; 143 | border-color: #b92c28; 144 | } 145 | 146 | .btn-info { 147 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 148 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 149 | background-repeat: repeat-x; 150 | border-color: #28a4c9; 151 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 152 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 153 | } 154 | 155 | .btn-info:hover, 156 | .btn-info:focus { 157 | background-color: #2aabd2; 158 | background-position: 0 -15px; 159 | } 160 | 161 | .btn-info:active, 162 | .btn-info.active { 163 | background-color: #2aabd2; 164 | border-color: #28a4c9; 165 | } 166 | 167 | .thumbnail, 168 | .img-thumbnail { 169 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 170 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 171 | } 172 | 173 | .dropdown-menu > li > a:hover, 174 | .dropdown-menu > li > a:focus { 175 | background-color: #e8e8e8; 176 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 177 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 178 | background-repeat: repeat-x; 179 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 180 | } 181 | 182 | .dropdown-menu > .active > a, 183 | .dropdown-menu > .active > a:hover, 184 | .dropdown-menu > .active > a:focus { 185 | background-color: #357ebd; 186 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 187 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 188 | background-repeat: repeat-x; 189 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 190 | } 191 | 192 | .navbar-default { 193 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); 194 | background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); 195 | background-repeat: repeat-x; 196 | border-radius: 4px; 197 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 198 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 199 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 200 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 201 | } 202 | 203 | .navbar-default .navbar-nav > .active > a { 204 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 205 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); 206 | background-repeat: repeat-x; 207 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); 208 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); 209 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); 210 | } 211 | 212 | .navbar-brand, 213 | .navbar-nav > li > a { 214 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); 215 | } 216 | 217 | .navbar-inverse { 218 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%); 219 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%); 220 | background-repeat: repeat-x; 221 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 222 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 223 | } 224 | 225 | .navbar-inverse .navbar-nav > .active > a { 226 | background-image: -webkit-linear-gradient(top, #222222 0%, #282828 100%); 227 | background-image: linear-gradient(to bottom, #222222 0%, #282828 100%); 228 | background-repeat: repeat-x; 229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); 230 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); 231 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); 232 | } 233 | 234 | .navbar-inverse .navbar-brand, 235 | .navbar-inverse .navbar-nav > li > a { 236 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 237 | } 238 | 239 | .navbar-static-top, 240 | .navbar-fixed-top, 241 | .navbar-fixed-bottom { 242 | border-radius: 0; 243 | } 244 | 245 | .alert { 246 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); 247 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 248 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 249 | } 250 | 251 | .alert-success { 252 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 253 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 254 | background-repeat: repeat-x; 255 | border-color: #b2dba1; 256 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 257 | } 258 | 259 | .alert-info { 260 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 261 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 262 | background-repeat: repeat-x; 263 | border-color: #9acfea; 264 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 265 | } 266 | 267 | .alert-warning { 268 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 269 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 270 | background-repeat: repeat-x; 271 | border-color: #f5e79e; 272 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 273 | } 274 | 275 | .alert-danger { 276 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 277 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 278 | background-repeat: repeat-x; 279 | border-color: #dca7a7; 280 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 281 | } 282 | 283 | .progress { 284 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 285 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 286 | background-repeat: repeat-x; 287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 288 | } 289 | 290 | .progress-bar { 291 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); 292 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 293 | background-repeat: repeat-x; 294 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 295 | } 296 | 297 | .progress-bar-success { 298 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 299 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 300 | background-repeat: repeat-x; 301 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 302 | } 303 | 304 | .progress-bar-info { 305 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 306 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 307 | background-repeat: repeat-x; 308 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 309 | } 310 | 311 | .progress-bar-warning { 312 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 313 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 314 | background-repeat: repeat-x; 315 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 316 | } 317 | 318 | .progress-bar-danger { 319 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 320 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 321 | background-repeat: repeat-x; 322 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 323 | } 324 | 325 | .list-group { 326 | border-radius: 4px; 327 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 328 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 329 | } 330 | 331 | .list-group-item.active, 332 | .list-group-item.active:hover, 333 | .list-group-item.active:focus { 334 | text-shadow: 0 -1px 0 #3071a9; 335 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); 336 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 337 | background-repeat: repeat-x; 338 | border-color: #3278b3; 339 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 340 | } 341 | 342 | .panel { 343 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 344 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 345 | } 346 | 347 | .panel-default > .panel-heading { 348 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 349 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 350 | background-repeat: repeat-x; 351 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 352 | } 353 | 354 | .panel-primary > .panel-heading { 355 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 356 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 357 | background-repeat: repeat-x; 358 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 359 | } 360 | 361 | .panel-success > .panel-heading { 362 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 363 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 364 | background-repeat: repeat-x; 365 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 366 | } 367 | 368 | .panel-info > .panel-heading { 369 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 370 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 371 | background-repeat: repeat-x; 372 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 373 | } 374 | 375 | .panel-warning > .panel-heading { 376 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 377 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 378 | background-repeat: repeat-x; 379 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 380 | } 381 | 382 | .panel-danger > .panel-heading { 383 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 384 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 385 | background-repeat: repeat-x; 386 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 387 | } 388 | 389 | .well { 390 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 391 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 392 | background-repeat: repeat-x; 393 | border-color: #dcdcdc; 394 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 395 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 396 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 397 | } -------------------------------------------------------------------------------- /static/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,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,0.125);box-shadow:inset 0 3px 5px rgba(0,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:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.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-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);background-repeat:repeat-x;border-color:#2b669a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff2d6ca2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.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-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);background-repeat:repeat-x;border-color:#3e8f3e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff419641',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.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-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);background-repeat:repeat-x;border-color:#e38d13;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffeb9316',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.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-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);background-repeat:repeat-x;border-color:#b92c28;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc12e2a',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.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-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);background-repeat:repeat-x;border-color:#28a4c9;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2aabd2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.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}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,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:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.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:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff3f3f3',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff282828',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,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,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,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:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /static/css/carousel.css: -------------------------------------------------------------------------------- 1 | /* GLOBAL STYLES 2 | -------------------------------------------------- */ 3 | /* Padding below the footer and lighter body text */ 4 | 5 | body { 6 | padding-bottom: 40px; 7 | color: #5a5a5a; 8 | } 9 | 10 | 11 | 12 | /* CUSTOMIZE THE NAVBAR 13 | -------------------------------------------------- */ 14 | 15 | /* Special class on .container surrounding .navbar, used for positioning it into place. */ 16 | .navbar-wrapper { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | right: 0; 21 | z-index: 20; 22 | } 23 | 24 | /* Flip around the padding for proper display in narrow viewports */ 25 | .navbar-wrapper .container { 26 | padding-left: 0; 27 | padding-right: 0; 28 | } 29 | .navbar-wrapper .navbar { 30 | padding-left: 15px; 31 | padding-right: 15px; 32 | } 33 | 34 | 35 | /* CUSTOMIZE THE CAROUSEL 36 | -------------------------------------------------- */ 37 | 38 | /* Carousel base class */ 39 | .carousel { 40 | height: 500px; 41 | margin-bottom: 60px; 42 | } 43 | /* Since positioning the image, we need to help out the caption */ 44 | .carousel-caption { 45 | z-index: 10; 46 | } 47 | 48 | /* Declare heights because of positioning of img element */ 49 | .carousel .item { 50 | height: 500px; 51 | background-color: #777; 52 | } 53 | .carousel-inner > .item > img { 54 | position: absolute; 55 | top: 0; 56 | left: 0; 57 | min-width: 100%; 58 | height: 500px; 59 | } 60 | 61 | 62 | 63 | /* MARKETING CONTENT 64 | -------------------------------------------------- */ 65 | 66 | /* Pad the edges of the mobile views a bit */ 67 | .marketing { 68 | padding-left: 15px; 69 | padding-right: 15px; 70 | } 71 | 72 | /* Center align the text within the three columns below the carousel */ 73 | .marketing .col-lg-4 { 74 | text-align: center; 75 | margin-bottom: 20px; 76 | } 77 | .marketing h2 { 78 | font-weight: normal; 79 | } 80 | .marketing .col-lg-4 p { 81 | margin-left: 10px; 82 | margin-right: 10px; 83 | } 84 | 85 | 86 | /* Featurettes 87 | ------------------------- */ 88 | 89 | .featurette-divider { 90 | margin: 80px 0; /* Space out the Bootstrap
more */ 91 | } 92 | 93 | /* Thin out the marketing headings */ 94 | .featurette-heading { 95 | font-weight: 300; 96 | line-height: 1; 97 | letter-spacing: -1px; 98 | } 99 | 100 | 101 | 102 | /* RESPONSIVE CSS 103 | -------------------------------------------------- */ 104 | 105 | @media (min-width: 768px) { 106 | 107 | /* Remove the edge padding needed for mobile */ 108 | .marketing { 109 | padding-left: 0; 110 | padding-right: 0; 111 | } 112 | 113 | /* Navbar positioning foo */ 114 | .navbar-wrapper { 115 | margin-top: 20px; 116 | } 117 | .navbar-wrapper .container { 118 | padding-left: 15px; 119 | padding-right: 15px; 120 | } 121 | .navbar-wrapper .navbar { 122 | padding-left: 0; 123 | padding-right: 0; 124 | } 125 | 126 | /* The navbar becomes detached from the top, so we round the corners */ 127 | .navbar-wrapper .navbar { 128 | border-radius: 4px; 129 | } 130 | 131 | /* Bump up size of carousel content */ 132 | .carousel-caption p { 133 | margin-bottom: 20px; 134 | font-size: 21px; 135 | line-height: 1.4; 136 | } 137 | 138 | .featurette-heading { 139 | font-size: 50px; 140 | } 141 | 142 | } 143 | 144 | @media (min-width: 992px) { 145 | .featurette-heading { 146 | margin-top: 120px; 147 | } 148 | } -------------------------------------------------------------------------------- /static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* AJAX STYLES 2 | -------------------------------------------------- */ 3 | 4 | #loading-indicator { 5 | /* transition: opacity 2s linear, margin-top 2s step-start, z-index 2s step-start; 6 | display: block; 7 | position: absolute; 8 | margin-left: auto; 9 | margin-right: auto; 10 | left: 10px; 11 | top: 10px;*/ 12 | } 13 | 14 | #canvas-container { 15 | /*border: 1px solid grey; 16 | border-radius: 3px; 17 | position: relative; 18 | margin: 80px auto 90px; 19 | min-height: 290px; 20 | overflow: hidden; 21 | padding-bottom: 40px; 22 | width: 990px;*/ 23 | } 24 | 25 | canvas { 26 | display: block; 27 | margin: 0px; 28 | } 29 | 30 | /*#my-canvas { 31 | margin: 40px; 32 | border: 1px solid grey; 33 | border-radius: 3px; 34 | position: relative; 35 | margin: 80px auto 90px; 36 | min-height: 290px; 37 | overflow: hidden; 38 | padding-bottom: 40px; 39 | width: 990px; 40 | }*/ 41 | 42 | -------------------------------------------------------------------------------- /static/css/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | *{ 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | html{ 8 | min-height: 100%; 9 | position: relative; 10 | 11 | } 12 | 13 | body{ 14 | color: grey; 15 | background-color:#333; 16 | min-height: 600px; 17 | font: 14px/1.3 'Segoe UI',Arial, sans-serif; 18 | }*/ 19 | 20 | #dropbox{ 21 | border: 1px solid grey; 22 | border-radius: 3px; 23 | position: relative; 24 | margin: 80px auto 90px; 25 | min-height: 290px; 26 | overflow: hidden; 27 | padding-bottom: 40px; 28 | width: 990px; 29 | } 30 | 31 | #dropbox .message{ 32 | font-size: 11px; 33 | text-align: center; 34 | padding-top: 160px; 35 | display: block; 36 | } 37 | 38 | #dropbox:before{ 39 | border-radius: 3px 3px 0 0; 40 | } 41 | 42 | #dropbox .preview{ 43 | width: 245px; 44 | height: 215px; 45 | float: left; 46 | margin: 55px 0 0 60px; 47 | position: relative; 48 | text-align: center; 49 | } 50 | 51 | #dropbox .preview img{ 52 | max-width: 240px; 53 | max-height: 180px; 54 | border: 3px solid #fff; 55 | display: block; 56 | box-shadow: 0 0 2px #000; 57 | } 58 | 59 | #dropbox .imageHolder{ 60 | display: inline-block; 61 | position: relative; 62 | } 63 | 64 | #dropbox .uploaded{ 65 | position: absolute; 66 | top: 0; 67 | left: 0; 68 | height: 100%; 69 | width: 100%; 70 | background: url('../img/done.png') no-repeat center center rgba(255,255,255,0.5); 71 | display: none; 72 | } 73 | 74 | #dropbox .preview.done .uploaded{ 75 | display: block; 76 | } 77 | 78 | #dropbox .progressHolder{ 79 | position: absolute; 80 | background-color:#252f38; 81 | height: 12px; 82 | width: 100%; 83 | left: 0; 84 | bottom: 0; 85 | box-shadow: 0 0 2px #000; 86 | } 87 | 88 | #dropbox .progress{ 89 | background-color:#2586d0; 90 | position: absolute; 91 | height: 100%; 92 | left: 0; 93 | width: 0; 94 | box-shadow: 0 0 1px rgba(255, 255, 255, 0.4) inset; 95 | -moz-transition: 0.25s; 96 | -webkit-transition: 0.25s; 97 | -o-transition: 0.25s; 98 | transition: 0.25s; 99 | } 100 | 101 | #dropbox .preview.done .progress{ 102 | width: 100% !important; 103 | } 104 | 105 | #predownload{ 106 | position:absolute; 107 | border:3px solid #ccc; 108 | background:#333; 109 | padding:5px; 110 | display:none; 111 | color:#fff; 112 | box-shadow: 4px 4px 3px rgba(103, 115, 130, 1); 113 | } 114 | 115 | 116 | img { 117 | border:0px; 118 | box-shadow: 2px 2px 3px #555562; 119 | } -------------------------------------------------------------------------------- /static/fonts/Arial Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/fonts/Arial Black.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/img/Fig1 copy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Fig1 copy.jpg -------------------------------------------------------------------------------- /static/img/Fig1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Fig1.jpg -------------------------------------------------------------------------------- /static/img/Fig2 copy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Fig2 copy.jpg -------------------------------------------------------------------------------- /static/img/Fig2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Fig2.jpg -------------------------------------------------------------------------------- /static/img/Fig3 copy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Fig3 copy.jpg -------------------------------------------------------------------------------- /static/img/Fig3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Fig3.jpg -------------------------------------------------------------------------------- /static/img/Preview1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Preview1.jpg -------------------------------------------------------------------------------- /static/img/Preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/Preview2.jpg -------------------------------------------------------------------------------- /static/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/ajax-loader.gif -------------------------------------------------------------------------------- /static/img/ajaxloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/ajaxloader.gif -------------------------------------------------------------------------------- /static/img/done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalkman/chestdetect/faf1928aea952ef28a3d3af2706012b7f56553cd/static/img/done.png -------------------------------------------------------------------------------- /static/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | if("undefined"==typeof jQuery)throw new Error("Bootstrap 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]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,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()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.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(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),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",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.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},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();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]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.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]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.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?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /static/js/bootstrap.min.js.old: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | if("undefined"==typeof jQuery)throw new Error("Bootstrap 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]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,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()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.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(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),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",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.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},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();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]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.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]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.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?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /static/js/display.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | 3 | // Globals 4 | 5 | var curr_file = "uploads/IM-0001-4035.tif"; 6 | 7 | function display_main(msg) { 8 | alert("Called Display script:"+msg); 9 | } 10 | 11 | }); -------------------------------------------------------------------------------- /static/js/download.js: -------------------------------------------------------------------------------- 1 | //the below uses jQuery "on" http://api.jquery.com/on/ (jQuery 1.7 + required, otherwise use "delegate" or "live") so that any 2 | // that is ever loaded into an Ajax site will automatically use jquery.fileDownload.js 3 | //if you are using "on": 4 | //you should generally be able to reduce the scope of the selector below "document" but it is used in this example so it 5 | //works for possible dynamic manipulation in the entire DOM 6 | 7 | // 8 | //Custom rich user experience - jquery.fileDownload.js & jQuery UI Dialog 9 | //uses the optional "options" argument 10 | // 11 | $(function() { 12 | $(document).on("click", "a.fileDownloadCustomRichExperience", function() { 13 | 14 | alert("File Downloading..."); 15 | 16 | var $preparingFileModal = $("#preparing-file-modal"); 17 | 18 | $preparingFileModal.dialog({ modal: true }); 19 | 20 | $.fileDownload($(this).attr('href'), { 21 | successCallback: function(url) { 22 | 23 | $preparingFileModal.dialog('close'); 24 | }, 25 | failCallback: function(responseHtml, url) { 26 | 27 | $preparingFileModal.dialog('close'); 28 | $("#error-modal").dialog({ modal: true }); 29 | } 30 | }); 31 | return false; //this is critical to stop the click event which will trigger a normal file download! 32 | }); 33 | }); -------------------------------------------------------------------------------- /static/js/hover.js: -------------------------------------------------------------------------------- 1 | 2 | // Kick off the jQuery with the document ready function on page load 3 | /*$(document).ready(function(){ 4 | alert("Trigger Image Preview.."); 5 | imagePreview(); 6 | });*/ 7 | 8 | $(function() { 9 | // Configuration of the x and y offsets 10 | xOffset = -20; 11 | yOffset = 40; 12 | 13 | $("a.predownload").hover(function(e){ 14 | this.t = this.title; 15 | this.title = ""; 16 | var c = (this.t != "") ? "
" + this.t : ""; 17 | $("body").append("

Image preview"+ c +"

"); 18 | $("#predownload").css("top",(e.pageY - xOffset) + "px").css("left",(e.pageX + yOffset) + "px").fadeIn("slow"); 19 | }, 20 | 21 | function(){ 22 | this.title = this.t; 23 | $("#predownload").remove(); 24 | 25 | }); 26 | 27 | $("a.predownload").mousemove(function(e){ 28 | $("#predownload").css("top",(e.pageY - xOffset) + "px").css("left",(e.pageX + yOffset) + "px"); 29 | }); 30 | }); -------------------------------------------------------------------------------- /static/js/jquery.fileDownload.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery File Download Plugin v1.4.2 3 | * 4 | * http://www.johnculviner.com 5 | * 6 | * Copyright (c) 2013 - John Culviner 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/mit-license.php 10 | * 11 | * !!!!NOTE!!!! 12 | * You must also write a cookie in conjunction with using this plugin as mentioned in the orignal post: 13 | * http://johnculviner.com/jquery-file-download-plugin-for-ajax-like-feature-rich-file-downloads/ 14 | * !!!!NOTE!!!! 15 | */ 16 | 17 | (function($, window){ 18 | // i'll just put them here to get evaluated on script load 19 | var htmlSpecialCharsRegEx = /[<>&\r\n"']/gm; 20 | var htmlSpecialCharsPlaceHolders = { 21 | '<': 'lt;', 22 | '>': 'gt;', 23 | '&': 'amp;', 24 | '\r': "#13;", 25 | '\n': "#10;", 26 | '"': 'quot;', 27 | "'": '#39;' /*single quotes just to be safe, IE8 doesn't support ', so use ' instead */ 28 | }; 29 | 30 | $.extend({ 31 | // 32 | //$.fileDownload('/path/to/url/', options) 33 | // see directly below for possible 'options' 34 | fileDownload: function (fileUrl, options) { 35 | 36 | //provide some reasonable defaults to any unspecified options below 37 | var settings = $.extend({ 38 | 39 | // 40 | //Requires jQuery UI: provide a message to display to the user when the file download is being prepared before the browser's dialog appears 41 | // 42 | preparingMessageHtml: null, 43 | 44 | // 45 | //Requires jQuery UI: provide a message to display to the user when a file download fails 46 | // 47 | failMessageHtml: null, 48 | 49 | // 50 | //the stock android browser straight up doesn't support file downloads initiated by a non GET: http://code.google.com/p/android/issues/detail?id=1780 51 | //specify a message here to display if a user tries with an android browser 52 | //if jQuery UI is installed this will be a dialog, otherwise it will be an alert 53 | // 54 | androidPostUnsupportedMessageHtml: "Unfortunately your Android browser doesn't support this type of file download. Please try again with a different browser.", 55 | 56 | // 57 | //Requires jQuery UI: options to pass into jQuery UI Dialog 58 | // 59 | dialogOptions: { modal: true }, 60 | 61 | // 62 | //a function to call while the dowload is being prepared before the browser's dialog appears 63 | //Args: 64 | // url - the original url attempted 65 | // 66 | prepareCallback: function (url) { }, 67 | 68 | // 69 | //a function to call after a file download dialog/ribbon has appeared 70 | //Args: 71 | // url - the original url attempted 72 | // 73 | successCallback: function (url) { }, 74 | 75 | // 76 | //a function to call after a file download dialog/ribbon has appeared 77 | //Args: 78 | // responseHtml - the html that came back in response to the file download. this won't necessarily come back depending on the browser. 79 | // in less than IE9 a cross domain error occurs because 500+ errors cause a cross domain issue due to IE subbing out the 80 | // server's error message with a "helpful" IE built in message 81 | // url - the original url attempted 82 | // 83 | failCallback: function (responseHtml, url) { }, 84 | 85 | // 86 | // the HTTP method to use. Defaults to "GET". 87 | // 88 | httpMethod: "GET", 89 | 90 | // 91 | // if specified will perform a "httpMethod" request to the specified 'fileUrl' using the specified data. 92 | // data must be an object (which will be $.param serialized) or already a key=value param string 93 | // 94 | data: null, 95 | 96 | // 97 | //a period in milliseconds to poll to determine if a successful file download has occured or not 98 | // 99 | checkInterval: 100, 100 | 101 | // 102 | //the cookie name to indicate if a file download has occured 103 | // 104 | cookieName: "fileDownload", 105 | 106 | // 107 | //the cookie value for the above name to indicate that a file download has occured 108 | // 109 | cookieValue: "true", 110 | 111 | // 112 | //the cookie path for above name value pair 113 | // 114 | cookiePath: "/", 115 | 116 | // 117 | //if specified it will be used when attempting to clear the above name value pair 118 | //useful for when downloads are being served on a subdomain (e.g. downloads.example.com) 119 | // 120 | cookieDomain: null, 121 | 122 | // 123 | //the title for the popup second window as a download is processing in the case of a mobile browser 124 | // 125 | popupWindowTitle: "Initiating file download...", 126 | 127 | // 128 | //Functionality to encode HTML entities for a POST, need this if data is an object with properties whose values contains strings with quotation marks. 129 | //HTML entity encoding is done by replacing all &,<,>,',",\r,\n characters. 130 | //Note that some browsers will POST the string htmlentity-encoded whilst others will decode it before POSTing. 131 | //It is recommended that on the server, htmlentity decoding is done irrespective. 132 | // 133 | encodeHTMLEntities: true 134 | 135 | }, options); 136 | 137 | var deferred = new $.Deferred(); 138 | 139 | //Setup mobile browser detection: Partial credit: http://detectmobilebrowser.com/ 140 | var userAgent = (navigator.userAgent || navigator.vendor || window.opera).toLowerCase(); 141 | 142 | var isIos; //has full support of features in iOS 4.0+, uses a new window to accomplish this. 143 | var isAndroid; //has full support of GET features in 4.0+ by using a new window. Non-GET is completely unsupported by the browser. See above for specifying a message. 144 | var isOtherMobileBrowser; //there is no way to reliably guess here so all other mobile devices will GET and POST to the current window. 145 | 146 | if (/ip(ad|hone|od)/.test(userAgent)) { 147 | 148 | isIos = true; 149 | 150 | } else if (userAgent.indexOf('android') !== -1) { 151 | 152 | isAndroid = true; 153 | 154 | } else { 155 | 156 | isOtherMobileBrowser = /avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|playbook|silk|iemobile|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(userAgent.substr(0, 4)); 157 | 158 | } 159 | 160 | var httpMethodUpper = settings.httpMethod.toUpperCase(); 161 | 162 | if (isAndroid && httpMethodUpper !== "GET") { 163 | //the stock android browser straight up doesn't support file downloads initiated by non GET requests: http://code.google.com/p/android/issues/detail?id=1780 164 | 165 | if ($().dialog) { 166 | $("
").html(settings.androidPostUnsupportedMessageHtml).dialog(settings.dialogOptions); 167 | } else { 168 | alert(settings.androidPostUnsupportedMessageHtml); 169 | } 170 | 171 | return deferred.reject(); 172 | } 173 | 174 | var $preparingDialog = null; 175 | 176 | var internalCallbacks = { 177 | 178 | onPrepare: function (url) { 179 | 180 | //wire up a jquery dialog to display the preparing message if specified 181 | if (settings.preparingMessageHtml) { 182 | 183 | $preparingDialog = $("
").html(settings.preparingMessageHtml).dialog(settings.dialogOptions); 184 | 185 | } else if (settings.prepareCallback) { 186 | 187 | settings.prepareCallback(url); 188 | 189 | } 190 | 191 | }, 192 | 193 | onSuccess: function (url) { 194 | 195 | //remove the perparing message if it was specified 196 | if ($preparingDialog) { 197 | $preparingDialog.dialog('close'); 198 | }; 199 | 200 | settings.successCallback(url); 201 | 202 | deferred.resolve(url); 203 | }, 204 | 205 | onFail: function (responseHtml, url) { 206 | 207 | //remove the perparing message if it was specified 208 | if ($preparingDialog) { 209 | $preparingDialog.dialog('close'); 210 | }; 211 | 212 | //wire up a jquery dialog to display the fail message if specified 213 | if (settings.failMessageHtml) { 214 | $("
").html(settings.failMessageHtml).dialog(settings.dialogOptions); 215 | } 216 | 217 | settings.failCallback(responseHtml, url); 218 | 219 | deferred.reject(responseHtml, url); 220 | } 221 | }; 222 | 223 | internalCallbacks.onPrepare(fileUrl); 224 | 225 | //make settings.data a param string if it exists and isn't already 226 | if (settings.data !== null && typeof settings.data !== "string") { 227 | settings.data = $.param(settings.data); 228 | } 229 | 230 | 231 | var $iframe, 232 | downloadWindow, 233 | formDoc, 234 | $form; 235 | 236 | if (httpMethodUpper === "GET") { 237 | 238 | if (settings.data !== null) { 239 | //need to merge any fileUrl params with the data object 240 | 241 | var qsStart = fileUrl.indexOf('?'); 242 | 243 | if (qsStart !== -1) { 244 | //we have a querystring in the url 245 | 246 | if (fileUrl.substring(fileUrl.length - 1) !== "&") { 247 | fileUrl = fileUrl + "&"; 248 | } 249 | } else { 250 | 251 | fileUrl = fileUrl + "?"; 252 | } 253 | 254 | fileUrl = fileUrl + settings.data; 255 | } 256 | 257 | if (isIos || isAndroid) { 258 | 259 | downloadWindow = window.open(fileUrl); 260 | downloadWindow.document.title = settings.popupWindowTitle; 261 | window.focus(); 262 | 263 | } else if (isOtherMobileBrowser) { 264 | 265 | window.location(fileUrl); 266 | 267 | } else { 268 | 269 | //create a temporary iframe that is used to request the fileUrl as a GET request 270 | $iframe = $("").appendTo("body"); 314 | formDoc = getiframeDocument($iframe); 315 | } 316 | 317 | formDoc.write("
" + formInnerHtml + "
" + settings.popupWindowTitle + ""); 318 | $form = $(formDoc).find('form'); 319 | } 320 | 321 | $form.submit(); 322 | } 323 | 324 | 325 | //check if the file download has completed every checkInterval ms 326 | setTimeout(checkFileDownloadComplete, settings.checkInterval); 327 | 328 | 329 | function checkFileDownloadComplete() { 330 | //has the cookie been written due to a file download occuring? 331 | if (document.cookie.indexOf(settings.cookieName + "=" + settings.cookieValue) != -1) { 332 | 333 | //execute specified callback 334 | internalCallbacks.onSuccess(fileUrl); 335 | 336 | //remove cookie 337 | var cookieData = settings.cookieName + "=; path=" + settings.cookiePath + "; expires=" + new Date(0).toUTCString() + ";"; 338 | if (settings.cookieDomain) cookieData += " domain=" + settings.cookieDomain + ";"; 339 | document.cookie = cookieData; 340 | 341 | //remove iframe 342 | cleanUp(false); 343 | 344 | return; 345 | } 346 | 347 | //has an error occured? 348 | //if neither containers exist below then the file download is occuring on the current window 349 | if (downloadWindow || $iframe) { 350 | 351 | //has an error occured? 352 | try { 353 | 354 | var formDoc = downloadWindow ? downloadWindow.document : getiframeDocument($iframe); 355 | 356 | if (formDoc && formDoc.body != null && formDoc.body.innerHTML.length) { 357 | 358 | var isFailure = true; 359 | 360 | if ($form && $form.length) { 361 | var $contents = $(formDoc.body).contents().first(); 362 | 363 | try { 364 | if ($contents.length && $contents[0] === $form[0]) { 365 | isFailure = false; 366 | } 367 | } catch (e) { 368 | if (e && e.number == -2146828218) { 369 | // IE 8-10 throw a permission denied after the form reloads on the "$contents[0] === $form[0]" comparison 370 | isFailure = true; 371 | } else { 372 | throw e; 373 | } 374 | } 375 | } 376 | 377 | if (isFailure) { 378 | // IE 8-10 don't always have the full content available right away, they need a litle bit to finish 379 | setTimeout(function () { 380 | internalCallbacks.onFail(formDoc.body.innerHTML, fileUrl); 381 | cleanUp(true); 382 | }, 100); 383 | 384 | return; 385 | } 386 | } 387 | } 388 | catch (err) { 389 | 390 | //500 error less than IE9 391 | internalCallbacks.onFail('', fileUrl); 392 | 393 | cleanUp(true); 394 | 395 | return; 396 | } 397 | } 398 | 399 | 400 | //keep checking... 401 | setTimeout(checkFileDownloadComplete, settings.checkInterval); 402 | } 403 | 404 | //gets an iframes document in a cross browser compatible manner 405 | function getiframeDocument($iframe) { 406 | var iframeDoc = $iframe[0].contentWindow || $iframe[0].contentDocument; 407 | if (iframeDoc.document) { 408 | iframeDoc = iframeDoc.document; 409 | } 410 | return iframeDoc; 411 | } 412 | 413 | function cleanUp(isFailure) { 414 | 415 | setTimeout(function() { 416 | 417 | if (downloadWindow) { 418 | 419 | if (isAndroid) { 420 | downloadWindow.close(); 421 | } 422 | 423 | if (isIos) { 424 | if (downloadWindow.focus) { 425 | downloadWindow.focus(); //ios safari bug doesn't allow a window to be closed unless it is focused 426 | if (isFailure) { 427 | downloadWindow.close(); 428 | } 429 | } 430 | } 431 | } 432 | 433 | //iframe cleanup appears to randomly cause the download to fail 434 | //not doing it seems better than failure... 435 | //if ($iframe) { 436 | // $iframe.remove(); 437 | //} 438 | 439 | }, 0); 440 | } 441 | 442 | 443 | function htmlSpecialCharsEntityEncode(str) { 444 | return str.replace(htmlSpecialCharsRegEx, function(match) { 445 | return '&' + htmlSpecialCharsPlaceHolders[match]; 446 | }); 447 | } 448 | 449 | return deferred.promise(); 450 | } 451 | }); 452 | 453 | })(jQuery, this); -------------------------------------------------------------------------------- /static/js/jquery.filedrop.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Default text - jQuery plugin for html5 dragging files from desktop to browser 3 | * 4 | * Author: Weixi Yen 5 | * 6 | * Email: [Firstname][Lastname]@gmail.com 7 | * 8 | * Copyright (c) 2010 Resopollution 9 | * 10 | * Licensed under the MIT license: 11 | * http://www.opensource.org/licenses/mit-license.php 12 | * 13 | * Project home: 14 | * http://www.github.com/weixiyen/jquery-filedrop 15 | * 16 | * Version: 0.1.0 17 | * 18 | * Features: 19 | * Allows sending of extra parameters with file. 20 | * Works with Firefox 3.6+ 21 | * Future-compliant with HTML5 spec (will work with Webkit browsers and IE9) 22 | * Usage: 23 | * See README at project homepage 24 | * 25 | */ 26 | (function($){ 27 | 28 | jQuery.event.props.push("dataTransfer"); 29 | var opts = {}, 30 | default_opts = { 31 | url: '', 32 | refresh: 1000, 33 | paramname: 'userfile', 34 | maxfiles: 25, 35 | maxfilesize: 1, // MBs 36 | data: {}, 37 | drop: empty, 38 | dragEnter: empty, 39 | dragOver: empty, 40 | dragLeave: empty, 41 | docEnter: empty, 42 | docOver: empty, 43 | docLeave: empty, 44 | beforeEach: empty, 45 | afterAll: empty, 46 | rename: empty, 47 | error: function(err, file, i){alert(err);}, 48 | uploadStarted: empty, 49 | uploadFinished: empty, 50 | progressUpdated: empty, 51 | speedUpdated: empty 52 | }, 53 | errors = ["BrowserNotSupported", "TooManyFiles", "FileTooLarge"], 54 | doc_leave_timer, 55 | stop_loop = false, 56 | files_count = 0, 57 | file_prefix = '', 58 | files; 59 | 60 | $.fn.filedrop = function(options) { 61 | opts = $.extend( {}, default_opts, options ); 62 | 63 | this.bind('drop', drop).bind('dragenter', dragEnter).bind('dragover', dragOver).bind('dragleave', dragLeave); 64 | $(document).bind('drop', docDrop).bind('dragenter', docEnter).bind('dragover', docOver).bind('dragleave', docLeave); 65 | }; 66 | 67 | function drop(e) { 68 | opts.drop(e); 69 | files = e.dataTransfer.files; 70 | if (files === null || files === undefined) { 71 | opts.error(errors[0]); 72 | return false; 73 | } 74 | 75 | 76 | files_count = files.length; 77 | upload(); 78 | e.preventDefault(); 79 | return false; 80 | } 81 | 82 | function getBuilder(filename, filedata, boundary) { 83 | var dashdash = '--', 84 | crlf = '\r\n', 85 | builder = ''; 86 | 87 | $.each(opts.data, function(i, val) { 88 | if (typeof val === 'function') val = val(); 89 | builder += dashdash; 90 | builder += boundary; 91 | builder += crlf; 92 | builder += 'Content-Disposition: form-data; name="'+i+'"'; 93 | builder += crlf; 94 | builder += crlf; 95 | builder += val; 96 | builder += crlf; 97 | }); 98 | 99 | builder += dashdash; 100 | builder += boundary; 101 | builder += crlf; 102 | builder += 'Content-Disposition: form-data; name="'+opts.paramname+'"'; 103 | builder += '; filename="' + filename + '"'; 104 | builder += crlf; 105 | 106 | builder += 'Content-Type: application/octet-stream'; 107 | builder += crlf; 108 | builder += crlf; 109 | 110 | builder += filedata; 111 | builder += crlf; 112 | 113 | builder += dashdash; 114 | builder += boundary; 115 | builder += dashdash; 116 | builder += crlf; 117 | return builder; 118 | } 119 | 120 | function progress(e) { 121 | if (e.lengthComputable) { 122 | var percentage = Math.round((e.loaded * 100) / e.total); 123 | if (this.currentProgress != percentage) { 124 | 125 | this.currentProgress = percentage; 126 | opts.progressUpdated(this.index, this.file, this.currentProgress); 127 | 128 | var elapsed = new Date().getTime(); 129 | var diffTime = elapsed - this.currentStart; 130 | if (diffTime >= opts.refresh) { 131 | var diffData = e.loaded - this.startData; 132 | var speed = diffData / diffTime; // KB per second 133 | opts.speedUpdated(this.index, this.file, speed); 134 | this.startData = e.loaded; 135 | this.currentStart = elapsed; 136 | } 137 | } 138 | } 139 | } 140 | 141 | function upload() { 142 | stop_loop = false; 143 | if (!files) { 144 | opts.error(errors[0]); 145 | return false; 146 | } 147 | var filesDone = 0, 148 | filesRejected = 0; 149 | 150 | if (files_count > opts.maxfiles) { 151 | opts.error(errors[1]); 152 | return false; 153 | } 154 | 155 | // file_prefix is determined by time and represents a unique prefix to a stack; should be redetermined on each drag & drop 156 | file_prefix = new Date().getTime(); 157 | 158 | for (var i=0; i max_file_size) { 168 | opts.error(errors[2], files[i], i); 169 | filesRejected++; 170 | continue; 171 | } 172 | 173 | reader.onloadend = send; 174 | reader.readAsBinaryString(files[i]); 175 | } else { 176 | filesRejected++; 177 | } 178 | } catch(err) { 179 | opts.error(errors[0]); 180 | return false; 181 | } 182 | } 183 | 184 | function send(e) { 185 | // Sometimes the index is not attached to the 186 | // event object. Find it by size. Hack for sure. 187 | if (e.target.index == undefined) { 188 | e.target.index = getIndexBySize(e.total); 189 | } 190 | 191 | var xhr = new XMLHttpRequest(), 192 | upload = xhr.upload, 193 | file = files[e.target.index], 194 | index = e.target.index, 195 | start_time = new Date().getTime(), 196 | boundary = '------multipartformboundary' + (new Date).getTime(), 197 | builder; 198 | 199 | newName = rename(file.name, e.target.index); 200 | //alert("Index: "+e.target.index); 201 | //alert("Result: "+e.target.result); 202 | if (typeof newName === "string") { 203 | builder = getBuilder(newName, e.target.result, boundary); 204 | //alert("New Name: "+newName); 205 | } else { 206 | builder = getBuilder(file.name, e.target.result, boundary); 207 | //alert("File Name: "+file.name); 208 | } 209 | 210 | upload.index = index; 211 | upload.file = file; 212 | upload.downloadStartTime = start_time; 213 | upload.currentStart = start_time; 214 | upload.currentProgress = 0; 215 | upload.startData = 0; 216 | upload.addEventListener("progress", progress, false); 217 | 218 | xhr.open("POST", opts.url, true); 219 | xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' 220 | + boundary); 221 | 222 | xhr.sendAsBinary(builder); 223 | 224 | opts.uploadStarted(index, file, files_count); 225 | 226 | xhr.onload = function() { 227 | if (xhr.responseText) { 228 | var now = new Date().getTime(), 229 | timeDiff = now - start_time, 230 | result = opts.uploadFinished(index, file, jQuery.parseJSON(xhr.responseText), timeDiff); 231 | filesDone++; 232 | if (filesDone == files_count - filesRejected) { 233 | afterAll(); 234 | } 235 | if (result === false) stop_loop = true; 236 | } 237 | }; 238 | } 239 | } 240 | 241 | function getIndexBySize(size) { 242 | for (var i=0; i < files_count; i++) { 243 | if (files[i].size == size) { 244 | return i; 245 | } 246 | } 247 | 248 | return undefined; 249 | } 250 | 251 | // Override this function to encode the number of files and file index into the filename while preserving the extension 252 | function rename(name, idx) { 253 | var nn; 254 | 255 | // Replace file name with current time + file count + index; keep the extension the same 256 | fnamesplit = name.split("."); 257 | nn = file_prefix+"-"+files_count+"-"+idx+"."+fnamesplit[fnamesplit.length - 1]; 258 | 259 | // Keeps the original file name 260 | /*fnamesplit = name.split("."); 261 | if (fnamesplit.length > 2) { 262 | nn = fnamesplit.slice(0,fnamesplit.length - 2).concat("-"+file_prefix+"-"+files_count+"-"+idx+"."+fnamesplit[fnamesplit.length - 1]); 263 | } else { 264 | nn = fnamesplit[0].concat("-"+file_prefix+"-"+files_count+"-"+idx+"."+fnamesplit[fnamesplit.length - 1]); 265 | }*/ 266 | 267 | // Implementation passing file_count and index and burden on the server side to create unique file name; didn't work b/c server couldn't remember base name between requests 268 | /*fnamesplit = name.split("."); 269 | if (fnamesplit.length > 2) { 270 | nn = fnamesplit.slice(0,fnamesplit.length - 2).concat("/"+files_count+"/"+idx+"."+fnamesplit[fnamesplit.length - 1]); 271 | } else { 272 | nn = fnamesplit[0].concat("/"+files_count+"/"+idx+"."+fnamesplit[fnamesplit.length - 1]); 273 | }*/ 274 | 275 | return nn; 276 | //return opts.rename(name); // this is the old function 277 | } 278 | 279 | 280 | 281 | function beforeEach(file) { 282 | return opts.beforeEach(file); 283 | } 284 | 285 | function afterAll() { 286 | return opts.afterAll(); 287 | } 288 | 289 | function dragEnter(e) { 290 | clearTimeout(doc_leave_timer); 291 | e.preventDefault(); 292 | opts.dragEnter(e); 293 | } 294 | 295 | function dragOver(e) { 296 | clearTimeout(doc_leave_timer); 297 | e.preventDefault(); 298 | opts.docOver(e); 299 | opts.dragOver(e); 300 | } 301 | 302 | function dragLeave(e) { 303 | clearTimeout(doc_leave_timer); 304 | opts.dragLeave(e); 305 | e.stopPropagation(); 306 | } 307 | 308 | function docDrop(e) { 309 | e.preventDefault(); 310 | opts.docLeave(e); 311 | return false; 312 | } 313 | 314 | function docEnter(e) { 315 | clearTimeout(doc_leave_timer); 316 | e.preventDefault(); 317 | opts.docEnter(e); 318 | return false; 319 | } 320 | 321 | function docOver(e) { 322 | clearTimeout(doc_leave_timer); 323 | e.preventDefault(); 324 | opts.docOver(e); 325 | return false; 326 | } 327 | 328 | function docLeave(e) { 329 | doc_leave_timer = setTimeout(function(){ 330 | opts.docLeave(e); 331 | }, 200); 332 | } 333 | 334 | function empty(){} 335 | 336 | try { 337 | if (XMLHttpRequest.prototype.sendAsBinary) return; 338 | XMLHttpRequest.prototype.sendAsBinary = function(datastr) { 339 | function byteValue(x) { 340 | return x.charCodeAt(0) & 0xff; 341 | } 342 | var ords = Array.prototype.map.call(datastr, byteValue); 343 | var ui8a = new Uint8Array(ords); 344 | this.send(ui8a.buffer); 345 | } 346 | } catch(e) {} 347 | 348 | })(jQuery); -------------------------------------------------------------------------------- /static/js/jquery.mooscan.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | var methods = { 4 | 5 | init : function(options) { 6 | 7 | return this.each(function() { 8 | var cache = []; 9 | var $this = $(this), data = $this.data("globals"); 10 | 11 | $.each(options.images, function(index, record) { 12 | var cacheImage = document.createElement('img'); 13 | cacheImage.src = record; 14 | cache.push(cacheImage); 15 | }); 16 | 17 | options.images = []; 18 | 19 | $this.data("globals", {cache : cache, ix : options.imgx, iy : options.imgy, niw : options.niw, nih : options.nih, winwidth : options.winwidth, winheight : options.winheight, oiw : options.oiw, oih : options.oih, nextIndex : 0, ctx : this.getContext("2d")}); 20 | 21 | $(this).data("globals").ctx.font="15px Verdana"; 22 | 23 | $(document).keydown({t:$(this)}, methods.handleMouseZoomPan); 24 | $(document).keyup({t:$(this)},methods.handleResetKey); 25 | $(this).bind("click", methods.handleMouseClick); 26 | $(this).bind("mousedown", methods.handleMouseDown); 27 | $(this).bind("mouseup", methods.handleMouseUp); 28 | $(this).bind("touchstart", methods.handleStart); 29 | $(this).bind("touchend", methods.handleEnd); 30 | $(this).bind("touchmove", methods.handleMove); 31 | $(this).bind("gesturestart", methods.handleGestureStart); 32 | $(this).bind("gesturechange", methods.handleGestureChange); 33 | $(this).bind("gestureend", methods.handleGestureEnd); 34 | $(this).bind("mousewheel", methods.handleMouseWheel); 35 | 36 | window.addEventListener('resize', methods.resizeMooCanvas, false); // keep resize control in submit for now 37 | }); 38 | }, 39 | 40 | handleMouseWheel : function(e) { 41 | e.originalEvent.preventDefault(); 42 | 43 | if (!$(this).data('flag')) { 44 | var self = this; 45 | 46 | $(this).data('timeout', window.setTimeout(function() { $(self).data('flag', false); }, 25)); 47 | 48 | var $this = $(this), data = $this.data("globals"); 49 | var delta = Math.max(-1, Math.min(1, (e.originalEvent.wheelDelta || -e.originalEvent.detail))); 50 | 51 | if (delta > 0) { 52 | data.nextIndex = data.nextIndex - 1; 53 | } else { 54 | data.nextIndex = data.nextIndex + 1; 55 | } 56 | 57 | //nextIndex = nextIndex + 1; 58 | if (data.nextIndex < 0) { 59 | data.nextIndex = data.cache.length - 1;// - Math.abs(nextIndex); 60 | } else { 61 | data.nextIndex = data.nextIndex % data.cache.length; 62 | } 63 | 64 | methods.drawStuff($(this),this); // for some reason direct call of methods like this don't have the global element $(this), need to pass that as well as the DOM element canvas 65 | 66 | $(this).data('flag', true); 67 | } 68 | }, 69 | 70 | handleMouseZoomPan : function(e) { 71 | e.preventDefault(); 72 | 73 | var $this = e.data.t, data = $this.data("globals"); 74 | 75 | keyPressed = 1; 76 | 77 | if (e.which == 90) { 78 | $this.bind("mousemove", methods.handleMouseZoom); 79 | } else if (e.which == 32) { 80 | $this.bind("mousemove", methods.handleMousePan); 81 | } 82 | }, 83 | 84 | handleMousePan : function(e) { 85 | e.preventDefault(); 86 | var $this = $(this), data = $this.data("globals"); 87 | 88 | var x = e.pageX; 89 | var y = e.pageY; 90 | 91 | var dx = x - startX; 92 | var dy = y - startY; 93 | 94 | if ((Math.abs(dy) >= 150) || (Math.abs(dx) >= 150)) { // this is a substitute for getting the initial mouse position 95 | 96 | startX = x; 97 | startY = y; 98 | 99 | } else if ((Math.abs(dy) >= 1) || (Math.abs(dx) >= 1)) { 100 | 101 | startX = x; 102 | startY = y; 103 | 104 | data.ix = data.ix+dx; 105 | data.iy = data.iy+dy; 106 | 107 | var picture = data.cache[data.nextIndex]; 108 | 109 | var chk_iw = picture.width; 110 | var chk_ih = picture.height; 111 | 112 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 113 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 114 | $(this).data("globals").ctx.fillRect(0,0,this.width,this.height); 115 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 116 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 117 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 118 | $(this).data("globals").ctx.fillStyle="#000000"; 119 | } 120 | } 121 | }, 122 | 123 | handleResetKey : function(e) { 124 | e.preventDefault(); 125 | 126 | var $this = e.data.t, data = $this.data("globals"); 127 | 128 | keyPressed = 0; 129 | 130 | if (e.which == 90) { 131 | $this.unbind("mousemove", methods.handleMouseZoom); 132 | } else if (e.which == 32) { 133 | $this.unbind("mousemove", methods.handleMousePan); 134 | } 135 | }, 136 | 137 | handleMouseDown : function(e) { 138 | if (keyPressed == 0) { 139 | e.preventDefault(); 140 | 141 | startX = e.pageX; 142 | startY = e.pageY; 143 | 144 | $(this).bind("mousemove", methods.handleDrag); 145 | cancelClick = 0; 146 | } 147 | }, 148 | 149 | handleMouseUp : function(e) { 150 | e.preventDefault(); 151 | 152 | if (keyPressed == 0) { 153 | $(this).unbind("mousemove", methods.handleDrag); 154 | } 155 | }, 156 | 157 | handleMouseClick : function(e) { 158 | if (keyPressed == 0) { 159 | e.preventDefault(); 160 | 161 | var $this = $(this), data = $this.data("globals"); 162 | 163 | var click_x = e.pageX; 164 | var click_y = e.pageY; 165 | 166 | var leftright = click_x/data.winwidth; 167 | 168 | now = new Date().getTime(); 169 | 170 | var delta = now - clickendtime; 171 | 172 | if (delta > 50) { // lock out a ghost touch end event by only executing if this was a human touch 173 | if(!cancelClick) { 174 | if(zoomSearchMode == 1) { 175 | methods.handleZoomSearch($(this)); 176 | } else { 177 | if(leftright > 0.5) { 178 | data.nextIndex = data.nextIndex + 1; 179 | } else { 180 | data.nextIndex = data.nextIndex - 1; 181 | } 182 | 183 | if (data.nextIndex < 0) { 184 | data.nextIndex = data.cache.length - 1; 185 | } else { 186 | data.nextIndex = data.nextIndex % data.cache.length; 187 | } 188 | var picture = data.cache[data.nextIndex]; 189 | 190 | var chk_iw = picture.width; 191 | var chk_ih = picture.height; 192 | 193 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 194 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 195 | $(this).data("globals").ctx.fillRect(0,0,this.width,this.height); 196 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 197 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 198 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 199 | $(this).data("globals").ctx.fillStyle="#000000"; 200 | } 201 | } 202 | } 203 | } 204 | clickendtime = new Date().getTime(); 205 | } 206 | }, 207 | 208 | handleMouseZoom : function(e) { 209 | e.preventDefault(); 210 | 211 | var $this = $(this), data = $this.data("globals"); 212 | 213 | var drag_x = e.pageX; 214 | var drag_y = e.pageY; 215 | 216 | var drag_dx = startX - drag_x; 217 | var drag_dy = startY - drag_y; 218 | 219 | var scaled = 1; 220 | 221 | if (Math.abs(drag_dy) >= 1) { 222 | 223 | if (drag_dy < 0) { 224 | scaled = 0.98; 225 | } else { 226 | scaled = 1.02; 227 | } 228 | 229 | startY = drag_y; 230 | startX = drag_x; 231 | 232 | // New 233 | 234 | data.niw = (data.niw)*scaled; 235 | data.nih = (data.nih)*scaled; 236 | 237 | data.ix = data.ix + (data.oiw - data.niw)/2; 238 | data.iy = data.iy + (data.oih - data.nih)/2; 239 | 240 | data.oih = data.nih; 241 | data.oiw = data.niw; 242 | 243 | // Old 244 | /*data.ix = data.ix + (startX - data.ix)*(1-scaled); 245 | data.iy = data.iy + (startY - data.iy)*(1-scaled); 246 | 247 | data.niw = (data.niw)*scaled; 248 | data.nih = (data.nih)*scaled; 249 | 250 | data.oih = data.nih; 251 | data.oiw = data.niw;*/ 252 | 253 | var picture = data.cache[data.nextIndex]; 254 | 255 | var chk_iw = picture.width; 256 | var chk_ih = picture.height; 257 | 258 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 259 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 260 | $(this).data("globals").ctx.fillRect(0,0,this.width,this.height); 261 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 262 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 263 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 264 | $(this).data("globals").ctx.fillStyle="#000000"; 265 | } 266 | } 267 | }, 268 | 269 | handleDrag : function(e) { 270 | e.preventDefault(); 271 | 272 | var $this = $(this), data = $this.data("globals"); 273 | cancelClick = 1; 274 | 275 | var drag_x = e.pageX; 276 | var drag_y = e.pageY; 277 | 278 | var drag_dx = startX - drag_x; 279 | var drag_dy = startY - drag_y; 280 | 281 | if (Math.abs(drag_dy) >= 1) { 282 | 283 | startY = drag_y; 284 | startX = drag_x; 285 | 286 | if (drag_dy < 0) { 287 | data.nextIndex = data.nextIndex + 1; 288 | } else { 289 | data.nextIndex = data.nextIndex - 1; 290 | } 291 | 292 | if (data.nextIndex < 0) { 293 | data.nextIndex = data.cache.length - 1;// - Math.abs(nextIndex); 294 | } else { 295 | data.nextIndex = data.nextIndex % data.cache.length; 296 | } 297 | 298 | var picture = data.cache[data.nextIndex]; 299 | 300 | var chk_iw = picture.width; 301 | var chk_ih = picture.height; 302 | 303 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 304 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 305 | $(this).data("globals").ctx.fillRect(0,0,this.width,this.height); 306 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 307 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 308 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 309 | $(this).data("globals").ctx.fillStyle="#000000"; 310 | } 311 | } 312 | }, 313 | 314 | handleStart : function(e) { 315 | e.preventDefault(); 316 | 317 | startX = e.originalEvent.touches[0].pageX; 318 | startY = e.originalEvent.touches[0].pageY; 319 | cancelTap = 0; 320 | }, 321 | 322 | handleMove : function(e) { 323 | e.preventDefault(); 324 | 325 | var $this = $(this), data = $this.data("globals"); 326 | 327 | now = new Date().getTime(); 328 | 329 | var delta = now - touchendtime; 330 | 331 | if (delta > 50) { // lockout a recent gesture end 332 | 333 | cancelTap = 1; 334 | 335 | var x = e.originalEvent.touches[0].pageX; 336 | var y = e.originalEvent.touches[0].pageY; 337 | 338 | var dx = x - startX; 339 | var dy = y - startY; 340 | 341 | //alert("x: "+x+" y: "+y+" dx: "+dx+" dy: "+dy); 342 | 343 | //alert("Handle Move"); 344 | 345 | if (e.originalEvent.touches.length == 2) { 346 | 347 | touchX1 = e.originalEvent.touches[0].pageX; 348 | touchY1 = e.originalEvent.touches[0].pageY; 349 | touchX2 = e.originalEvent.touches[1].pageX; 350 | touchY2 = e.originalEvent.touches[1].pageY; 351 | 352 | if ((Math.abs(dy) >= 1) || (Math.abs(dx) >= 1)) { 353 | 354 | //alert("Handle two touches move"); 355 | 356 | startX = x; 357 | startY = y; 358 | 359 | data.ix = data.ix+dx; 360 | data.iy = data.iy+dy; 361 | 362 | var picture = data.cache[data.nextIndex]; 363 | 364 | var chk_iw = picture.width; 365 | var chk_ih = picture.height; 366 | 367 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 368 | //alert("context"); 369 | //alert("OIH: "+$(this).data("globals").oih); 370 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 371 | $(this).data("globals").ctx.fillRect(0,0,this.width,this.height); 372 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 373 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 374 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 375 | $(this).data("globals").ctx.fillStyle="#000000"; 376 | } 377 | //draw(cache[nextIndex]); 378 | } 379 | } 380 | 381 | if (e.originalEvent.touches.length == 1) { 382 | 383 | if (Math.abs(dy) >= 1) { //can try for dy > 10 to get slower scaling 384 | //alert("Start X:"+startX+" Start Y: "+startY+" x: "+x+" y: "+y+" dx: "+dx+" dy: "+dy); 385 | //alert("Next Index: "+data.nextIndex); 386 | startY = y; 387 | startX = x; 388 | 389 | if (dy < 0) { 390 | data.nextIndex = data.nextIndex + 1; 391 | } else { 392 | data.nextIndex = data.nextIndex - 1; 393 | } 394 | //nextIndex = nextIndex + 1; 395 | if (data.nextIndex < 0) { 396 | data.nextIndex = data.cache.length - 1;// - Math.abs(nextIndex); 397 | } else { 398 | data.nextIndex = data.nextIndex % data.cache.length; 399 | } 400 | 401 | var picture = data.cache[data.nextIndex]; 402 | 403 | var chk_iw = picture.width; 404 | var chk_ih = picture.height; 405 | 406 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 407 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 408 | $(this).data("globals").ctx.fillRect(0,0,this.width,this.height); 409 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 410 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 411 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 412 | $(this).data("globals").ctx.fillStyle="#000000"; 413 | } 414 | } 415 | } 416 | } 417 | }, 418 | 419 | handleZoomSearch : function(th) { // pass in 'this' globals object; o/w we have no access to it 420 | 421 | var $this = th, data = $this.data("globals"); 422 | var applicable = false; 423 | //alert("NIW: "+data.niw+" NIH: "+data.nih+" WinWidth: "+data.winwidth+" WinHeight: "+data.winheight+" IX: "+data.ix+" IY: "+data.iy); 424 | if (data.niw > data.winwidth) { 425 | applicable = true; 426 | if (data.ix <= (data.winwidth - data.niw)) { // check for last X-view 427 | data.ix = 0; 428 | if (data.iy <= (data.winheight - data.nih)) { // check for last Y-view 429 | data.iy = 0; 430 | } else if ((data.iy + data.nih - data.winheight) <= (data.winheight - 100)) { // check for penultimate Y-view 431 | data.iy = data.winheight - data.nih; 432 | } else { 433 | data.iy = data.iy - (data.winheight - 100); 434 | } 435 | } else if ((data.ix + data.niw - data.winwidth) <= (data.winwidth - 100)) { // check for penultimate X-view 436 | data.ix = data.winwidth - data.niw; 437 | } else { 438 | data.ix = data.ix - (data.winwidth - 100); 439 | } 440 | } else if (data.nih > data.winheight) { 441 | applicable = true; 442 | if (data.iy <= (data.winheight - data.nih)) { // check for last Y-view 443 | data.iy = 0; 444 | if (data.ix <= (data.winwidth - data.niw)) { // check for last X-view 445 | data.ix = 0; 446 | } else if ((data.ix + data.niw - data.winwidth) <= (data.winwidth - 100)) { // check for penultimate X-view 447 | data.ix = data.winwidth - data.niw; 448 | } else { 449 | data.ix = data.ix - (data.winwidth - 100); 450 | } 451 | } else if ((data.iy + data.nih - data.winheight) <= (data.winheight - 100)) { // check for penultimate Y-view 452 | data.iy = data.winheight - data.nih; 453 | } else { 454 | data.iy = data.iy - (data.winheight - 100); 455 | } 456 | } 457 | 458 | if (applicable) { 459 | var picture = data.cache[data.nextIndex]; 460 | var chk_iw = picture.width; 461 | var chk_ih = picture.height; 462 | 463 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 464 | var shrinkby = 6; 465 | var lw = 3; 466 | var km_xo = data.winwidth-(data.winwidth/shrinkby + lw*2); 467 | var km_yo = 10; 468 | var km_w = data.winwidth/shrinkby; 469 | var km_h = km_w*data.nih/data.niw; 470 | var km_sqw; 471 | var km_sqh; 472 | 473 | if (data.winwidth > data.niw) { 474 | km_sqw = km_w-lw*2; 475 | } else { 476 | km_sqw = km_w*(data.winwidth/data.niw)-lw*2; 477 | } 478 | 479 | if (data.winheight > data.nih) { 480 | km_sqh = km_h-lw*2; 481 | } else { 482 | km_sqh = km_h*data.winheight/data.nih-lw*2; 483 | } 484 | 485 | th.data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 486 | th.data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 487 | th.data("globals").ctx.drawImage(picture,km_xo,km_yo,km_w,km_h); 488 | th.data("globals").ctx.beginPath(); 489 | th.data("globals").ctx.rect(km_xo,km_yo,km_w,km_h); 490 | th.data("globals").ctx.strokeStyle = 'white'; 491 | th.data("globals").ctx.lineWidth = lw; 492 | th.data("globals").ctx.stroke(); 493 | th.data("globals").ctx.beginPath(); 494 | th.data("globals").ctx.rect(km_xo-(data.ix/data.niw)*km_w+lw,km_yo-(data.iy/data.nih)*km_h+lw,km_sqw,km_sqh); 495 | th.data("globals").ctx.strokeStyle = 'orange'; 496 | th.data("globals").ctx.lineWidth = 1; 497 | th.data("globals").ctx.stroke(); 498 | th.data("globals").ctx.fillStyle="#FFFFFF"; 499 | th.data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 500 | th.data("globals").ctx.fillStyle="#000000"; 501 | } 502 | } else { 503 | alert("You must be zoomed to make use of the Map tool."); 504 | } 505 | }, 506 | 507 | handleEnd : function(e) { 508 | e.preventDefault(); 509 | 510 | var $this = $(this), data = $this.data("globals"); 511 | 512 | var leftright = startX/data.winwidth; 513 | now = new Date().getTime(); 514 | 515 | var delta = now - touchendtime; 516 | 517 | if (delta > 50) { // lock out a ghost touch end event by only executing if this was a human touch 518 | if(!cancelTap) { 519 | if(zoomSearchMode == 1) { 520 | methods.handleZoomSearch($(this)); 521 | } else { 522 | if(leftright > 0.5) { 523 | //currScroll += 50; 524 | data.nextIndex = data.nextIndex + 1; 525 | } else { 526 | //currScroll -= 50; 527 | data.nextIndex = data.nextIndex - 1; 528 | } 529 | 530 | if (data.nextIndex < 0) { 531 | data.nextIndex = data.cache.length - 1; 532 | } else { 533 | data.nextIndex = data.nextIndex % data.cache.length; 534 | } 535 | var picture = data.cache[data.nextIndex]; 536 | 537 | var chk_iw = picture.width; 538 | var chk_ih = picture.height; 539 | 540 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 541 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 542 | $(this).data("globals").ctx.fillRect(0,0,this.width,this.height); 543 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 544 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 545 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 546 | $(this).data("globals").ctx.fillStyle="#000000"; 547 | } 548 | } 549 | } 550 | } 551 | touchendtime = new Date().getTime(); 552 | }, 553 | 554 | handleGestureStart : function(e) { 555 | //alert("Gesture Start..."); 556 | }, 557 | 558 | handleGestureChange : function(e) { 559 | e.preventDefault(); 560 | 561 | var $this = $(this), data = $this.data("globals"); 562 | 563 | //var x1 = e.originalEvent.touches[0].pageX; 564 | //var y1 = e.originalEvent.touches[0].pageY; 565 | //var x2 = e.originalEvent.touches[1].pageX; 566 | //var y2 = e.originalEvent.touches[1].pageY; 567 | var xMid = 0; 568 | var yMid = 0; 569 | 570 | if (touchX1 > touchX2) { 571 | xMid = touchX2 + (touchX1-touchX2)/2; 572 | } else { 573 | xMid = touchX1 + (touchX2-touchX1)/2; 574 | } 575 | 576 | if (touchY1 > touchY2) { 577 | yMid = touchY2 + (touchY1-touchY2)/2; 578 | } else { 579 | yMid = touchY1 + (touchY2-touchY1)/2; 580 | } 581 | 582 | var scaled = 1; 583 | 584 | if (e.originalEvent.scale > 1) { 585 | scaled = (1 - e.originalEvent.scale)/50; 586 | } else { 587 | scaled = (1 - e.originalEvent.scale)/15; 588 | } 589 | 590 | //data.ix = data.ix + data.niw*scaled/2; 591 | //data.iy = data.iy + data.nih*scaled/2; 592 | 593 | data.ix = data.ix + (xMid - data.ix)*scaled; 594 | data.iy = data.iy + (yMid - data.iy)*scaled; 595 | //alert("Scaled: "+scaled); 596 | if (e.originalEvent.scale > 1) { 597 | scaled = 1 + Math.abs(scaled); 598 | } else { 599 | scaled = 1 - Math.abs(scaled); 600 | } 601 | 602 | data.niw = (data.niw)*scaled; 603 | data.nih = (data.nih)*scaled; 604 | data.oih = data.nih; 605 | data.oiw = data.niw; 606 | //alert("Scaled Abs: "+scaled+" New IX: "+data.ix+" New IY "+data.iy+" New Width "+data.niw+" New Height "+data.nih); 607 | //ctx.fillRect(0,0,data.winwidth,data.winheight); 608 | 609 | var picture = data.cache[data.nextIndex]; 610 | 611 | var chk_iw = picture.width; 612 | var chk_ih = picture.height; 613 | 614 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 615 | $(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 616 | $(this).data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 617 | $(this).data("globals").ctx.fillStyle="#FFFFFF"; 618 | $(this).data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 619 | $(this).data("globals").ctx.fillStyle="#000000"; 620 | } 621 | 622 | //ctx.drawImage(cache[nextIndex],ix,iy,niw,nih); 623 | //ctx.fillStyle="#FFFFFF"; 624 | //ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 625 | //ctx.fillStyle="#000000"; 626 | }, 627 | 628 | handleGestureEnd : function(e) { 629 | touchendtime = new Date().getTime(); 630 | }, 631 | 632 | testFunction : function() { 633 | alert("Test Function..."); 634 | var $this = $(this), data = $this.data("globals"); 635 | alert("Test Function Data Cache: "+data.cache); 636 | }, 637 | 638 | destroy : function() { 639 | var $this = $(this), data = $this.data("globals"); 640 | if (typeof data != "undefined") { 641 | data.cache = []; 642 | //alert("Data cache after: "+data.cache.length); 643 | data.remove(); 644 | $this.removeData("globals"); 645 | } 646 | }, 647 | 648 | resizeMooCanvas : function () { 649 | var prev_width = this.width; 650 | //var new_width = window.innerWidth; 651 | var new_width = window.innerWidth*0.6; 652 | 653 | this.width = new_width; 654 | this.height = this.width; // height = width (square) for now 655 | //canvas.height = canvas.height * (new_width/prev_width); 656 | 657 | methods.drawStuff($(this),this); 658 | }, 659 | 660 | drawStuff : function(th,domelem) { 661 | var $this = th, data = $this.data("globals"); 662 | var picture = data.cache[data.nextIndex]; 663 | var chk_iw = picture.width; 664 | var chk_ih = picture.height; 665 | 666 | if (chk_iw > 0 && chk_ih > 0) { //only process if the image is loaded 667 | //$(this).data("globals").ctx.fillRect(0,0,data.winwidth,data.winheight); 668 | $this.data("globals").ctx.fillRect(0,0,domelem.width,domelem.height); 669 | $this.data("globals").ctx.drawImage(picture,data.ix,data.iy,data.niw,data.nih); 670 | $this.data("globals").ctx.fillStyle="#FFFFFF"; 671 | $this.data("globals").ctx.fillText(data.nextIndex+1+"/"+data.cache.length,10,20); 672 | $this.data("globals").ctx.fillStyle="#000000"; 673 | } 674 | } 675 | }; 676 | 677 | $.fn.mooscan = function( method ) { 678 | 679 | // Create some globals in the name space for touch actions 680 | startX = 0; 681 | startY = 0; 682 | startGX = 0; 683 | startGY = 0; 684 | touchX1 = 0; 685 | touchY1 = 0; 686 | touchX2 = 0; 687 | touchY2 = 0; 688 | touchendtime = 0; 689 | clickendtime = 0; 690 | now = 0; 691 | cancelTap = 0; 692 | cancelClick = 0; 693 | keyPressed = 0; 694 | 695 | // Method calling logic 696 | if ( methods[method] ) { 697 | return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); 698 | } else if ( typeof method === 'object' || ! method ) { 699 | return methods.init.apply( this, arguments ); 700 | } else { 701 | $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' ); 702 | } 703 | 704 | return this.each(function() { 705 | }); 706 | }; 707 | 708 | }) (jQuery); -------------------------------------------------------------------------------- /static/js/submit.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | // Globals 4 | var img = null; 5 | var imgarr = [] 6 | var canvas = document.getElementById('my-canvas'); 7 | var dropbox = document.getElementById('dropbox'); 8 | var context = canvas.getContext('2d'); 9 | var winwidth = $(window).width(); 10 | var winheight = $(window).height(); 11 | var IH = 1; // image height resized; arbitrarily init 12 | var IW = 1; // image height resized; arbitrarily init 13 | var MARGIN = 50; 14 | 15 | function drawStuff() { 16 | IW = canvas.width - MARGIN; 17 | var s = IW/img.width; 18 | 19 | IH = img.height*s; 20 | canvas.height = IH; 21 | 22 | context.drawImage(img,MARGIN/2,MARGIN/2,IW,IH); 23 | } 24 | 25 | function getImagesCT() { 26 | 27 | $.ajax({ 28 | type: "GET", 29 | url: "/process_serve_chestct?imgfile="+curr_file, 30 | data: {'message':'message'}, 31 | 32 | success:function(resp) { 33 | if (resp.success > 0) { 34 | 35 | // Experimental Implementation 36 | img = new Image(); 37 | img.src = "data:image/png;base64," + resp.imagefile; 38 | img.onload = function () { 39 | resizeCanvas(); 40 | imgarr = resp.imgarr; 41 | initCanvas(); 42 | } 43 | } else { 44 | alert("Sorry, there was an error processing one of the submitted MRI slices."); 45 | } 46 | 47 | $('#loading-indicator').hide(); 48 | $('#dropbox-container').show(); 49 | $('#results-container').show(); 50 | $('#download-container').show(); 51 | removeImages(); 52 | } 53 | }); 54 | } 55 | 56 | $("#processButton").on("click",function(e) { 57 | e.preventDefault(); 58 | 59 | if (curr_file.length < 1) { 60 | alert("No CT stack was found. Please drag and drop a CT stack file into the web page drop container."); 61 | } else { 62 | $('#loading-indicator').show(); 63 | $('#dropbox-container').hide(); 64 | $('#results-container').hide(); 65 | $('#download-container').hide(); 66 | getImagesCT(); 67 | } 68 | }); 69 | 70 | function removeImages () { 71 | var dropbox = $('#dropbox'), 72 | message = $('.message', dropbox); 73 | $('.preview').remove(); 74 | message.show(); 75 | curr_file = []; 76 | } 77 | 78 | function resizeCanvas () { 79 | var prev_width = canvas.width; 80 | //var new_width = window.innerWidth; 81 | var new_width = window.innerWidth*0.6; 82 | 83 | canvas.width = new_width + 25; //arbitrary buffer 84 | //canvas.height = IH; 85 | canvas.height = canvas.width; // make it square 86 | //canvas.height = canvas.height * (new_width/prev_width); 87 | drawStuff(); 88 | } 89 | 90 | function initCanvas() { 91 | 92 | // Display the first image 93 | myimg = document.createElement('img'); 94 | myimg.src = imgarr[0]; 95 | 96 | // We need to wait until the image is loaded before we can get its width and height 97 | myimg.onload = function() { 98 | //var IW = myimg.width; 99 | //var IH = myimg.height; 100 | 101 | $("#my-canvas").mooscan({ 102 | images:imgarr, 103 | imgx:MARGIN/2, 104 | imgy:MARGIN/2, 105 | niw:IW, 106 | nih:IH, 107 | winwidth:canvas.width, 108 | winheight:canvas.height, 109 | oiw:IW, 110 | oih:IH 111 | }); 112 | } 113 | } 114 | 115 | // resize the canvas to fill browser window dynamically 116 | //window.addEventListener('resize', resizeCanvas, false); 117 | 118 | }); -------------------------------------------------------------------------------- /static/js/upload.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var dropbox = $('#dropbox'), 3 | message = $('.message', dropbox); 4 | curr_file = []; // set global variable to null and flag error if process button is pressed prior to uploading a mammogram 5 | 6 | dropbox.filedrop({ 7 | paramname: 'file', 8 | maxfiles: 200, 9 | maxfilesize: 30, 10 | url: '/upload', 11 | uploadFinished:function(i, file, response) { // i = index (from filedrop), which I think represents the file # out of a stack 12 | $.data(file).addClass('done'); 13 | /*alert("Upload finished: "+response.file); 14 | var img = new Image(); 15 | img.src = "/serve_img?file="+response.file; 16 | img.onload = function () { 17 | alert("Image source: "+img.src); 18 | context.drawImage(img, 0, 0, 100, 100); 19 | alert("Context: "+context); 20 | }*/ 21 | 22 | curr_file = response.file; 23 | 24 | //alert("My curr file = "+curr_file); 25 | }, 26 | 27 | error: function(err, file) { 28 | switch(err) { 29 | case 'BrowserNotSupported': 30 | showMessage('Your browser does not support HTML5 file uploads!'); 31 | break; 32 | case 'TooManyFiles': 33 | alert('Too many files! Please select ' + this.maxfiles + ' at most!'); 34 | break; 35 | case 'FileTooLarge': 36 | alert(file.name + ' is too large! The size is limited to ' + this.maxfilesize + 'MB.'); 37 | break; 38 | default: 39 | break; 40 | } 41 | }, 42 | 43 | beforeEach: function(file){ 44 | if(!(file.type.match(/^image\//) || file.type.match(/dicom/))){ 45 | alert('Only images are allowed!'); 46 | return false; 47 | } 48 | }, 49 | 50 | // len = is the total number of files 51 | // file = the object file 52 | // i = the current file index, starting from (len - 1) and counting down to zero 53 | uploadStarted:function(i, file, len){ // consider i > 1, then don't create a new image, or just change the picture of the current preview 54 | //alert("Index: "+i+" File: "+file+" Len: "+len); 55 | if (i < len-1) { 56 | appendImage(file); // used for a stack, in which case don't create another preview image 57 | } else { 58 | createImage(file); 59 | } 60 | }, 61 | 62 | progressUpdated: function(i, file, progress) { 63 | $.data(file).find('.progress').width(progress); 64 | } 65 | }); 66 | 67 | var template = '
'+ 68 | ''+ 69 | ''+ 70 | ''+ 71 | ''+ 72 | '
'+ 73 | '
'+ 74 | '
'+ 75 | '
'; 76 | 77 | function createImage(file){ 78 | 79 | var preview = $(template), 80 | image = $('img', preview); 81 | 82 | var reader = new FileReader(); 83 | 84 | image.width = 100; 85 | image.height = 100; 86 | 87 | reader.onload = function(e){ 88 | image.attr('src',e.target.result); 89 | }; 90 | 91 | reader.readAsDataURL(file); 92 | 93 | message.hide(); 94 | preview.appendTo(dropbox); 95 | 96 | $.data(file,preview); 97 | } 98 | 99 | function appendImage(file){ 100 | 101 | image = $('img', dropbox); // fix later to make more specific as this could refer to any image within the dropbox 102 | //alert("Image object: "+image); 103 | var reader = new FileReader(); 104 | 105 | reader.onload = function(e){ 106 | image.attr('src',e.target.result); 107 | }; 108 | 109 | reader.readAsDataURL(file); 110 | 111 | message.hide(); 112 | } 113 | 114 | function showMessage(msg){ 115 | message.html(msg); 116 | } 117 | 118 | }); -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Chest CAD 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
74 | 75 | 77 | 122 | 123 | 125 | 126 | 127 |
128 |

Downloadable Test Files (download, then drag and drop into the container below)

129 |
130 |

Test Patient 1

131 |

Test Patient 2

132 |
133 |
134 |
135 | 136 | gallery thumbnail 137 |
138 |
139 | 140 | gallery thumbnail 141 |
142 |
143 |
144 | 145 | 178 | 179 |
180 |
181 |
182 | 188 | 189 |
190 |

Drop any TIFF Chest CT below and click the Analyze button.

191 |
192 | Drop images here to upload. 193 |
194 |

ANALYZE CHEST CT

195 |
196 |
197 |
198 | 199 | 200 | 201 | 202 |
203 |
204 |
205 | 209 |
210 |
211 | 212 | 213 | --------------------------------------------------------------------------------