├── .DS_Store ├── .gitignore ├── README.md ├── app.js ├── bin └── www ├── package.json ├── public ├── .DS_Store ├── images │ ├── loading.gif │ └── progressbar.gif ├── javascripts │ ├── cors │ │ ├── jquery.postmessage-transport.js │ │ └── jquery.xdr-transport.js │ └── uploader │ │ ├── canvas-to-blob.min.js │ │ ├── jquery.blueimp-gallery.min.js │ │ ├── jquery.fileupload-audio.js │ │ ├── jquery.fileupload-image.js │ │ ├── jquery.fileupload-process.js │ │ ├── jquery.fileupload-ui.js │ │ ├── jquery.fileupload-validate.js │ │ ├── jquery.fileupload-video.js │ │ ├── jquery.fileupload.js │ │ ├── jquery.iframe-transport.js │ │ ├── jquery.ui.widget.js │ │ ├── load-image.min.js │ │ ├── main.js │ │ └── tmpl.min.js └── stylesheets │ ├── blueimp-gallery.min.css │ ├── jquery.fileupload-ui.css │ ├── jquery.fileupload.css │ └── style.css ├── routes ├── index.js ├── uploadManager.js └── users.js └── views ├── error.ejs └── index.ejs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvindr21/expressjs-fileupload/3e41039f065961665592930e26139e7bf31e05e9/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Expressjs and jQuery File Upload 2 | ================= 3 | 4 | An example consuming ```blueimp-file-upload-expressjs``` node module 5 | 6 | Demo : http://expressjs-fileupload.cloudno.de/ 7 | 8 | Tutorial: http://thejackalofjavascript.com/uploading-files-made-fun 9 | 10 | Install 11 | ------- 12 | 13 | * Download or clone this repo 14 | * run ```npm install``` 15 | * run ```node bin/www``` 16 | 17 | for debug messages 18 | * run ```DEBUG=express:* node app.js``` 19 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('static-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index'); 9 | var users = require('./routes/users'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'ejs'); 16 | 17 | app.use(favicon()); 18 | app.use(logger('dev')); 19 | app.use(bodyParser.json()); 20 | app.use(bodyParser.urlencoded()); 21 | app.use(cookieParser()); 22 | app.use(express.static(path.join(__dirname, 'public'))); 23 | 24 | app.use('/', routes); 25 | app.use('/users', users); 26 | 27 | /// catch 404 and forwarding to error handler 28 | app.use(function(req, res, next) { 29 | var err = new Error('Not Found'); 30 | err.status = 404; 31 | next(err); 32 | }); 33 | 34 | /// error handlers 35 | 36 | // development error handler 37 | // will print stacktrace 38 | if (app.get('env') === 'development') { 39 | app.use(function(err, req, res, next) { 40 | res.status(err.status || 500); 41 | res.render('error', { 42 | message: err.message, 43 | error: err 44 | }); 45 | }); 46 | } 47 | 48 | // production error handler 49 | // no stacktraces leaked to user 50 | app.use(function(err, req, res, next) { 51 | res.status(err.status || 500); 52 | res.render('error', { 53 | message: err.message, 54 | error: {} 55 | }); 56 | }); 57 | 58 | 59 | module.exports = app; 60 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var debug = require('debug')('my-application'); 3 | var app = require('../app'); 4 | 5 | app.set('port', process.env.PORT || 3000); 6 | 7 | var server = app.listen(app.get('port'), function() { 8 | debug('Express server listening on port ' + server.address().port); 9 | }); 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "application-name", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "express": "~4.0.0", 10 | "static-favicon": "~1.0.0", 11 | "morgan": "~1.0.0", 12 | "cookie-parser": "~1.0.1", 13 | "body-parser": "~1.0.0", 14 | "debug": "~0.7.4", 15 | "ejs": "~0.8.5", 16 | "blueimp-file-upload-expressjs": "latest" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /public/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvindr21/expressjs-fileupload/3e41039f065961665592930e26139e7bf31e05e9/public/.DS_Store -------------------------------------------------------------------------------- /public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvindr21/expressjs-fileupload/3e41039f065961665592930e26139e7bf31e05e9/public/images/loading.gif -------------------------------------------------------------------------------- /public/images/progressbar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvindr21/expressjs-fileupload/3e41039f065961665592930e26139e7bf31e05e9/public/images/progressbar.gif -------------------------------------------------------------------------------- /public/javascripts/cors/jquery.postmessage-transport.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery postMessage Transport Plugin 1.1.1 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2011, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /* global define, window, document */ 13 | 14 | (function (factory) { 15 | 'use strict'; 16 | if (typeof define === 'function' && define.amd) { 17 | // Register as an anonymous AMD module: 18 | define(['jquery'], factory); 19 | } else { 20 | // Browser globals: 21 | factory(window.jQuery); 22 | } 23 | }(function ($) { 24 | 'use strict'; 25 | 26 | var counter = 0, 27 | names = [ 28 | 'accepts', 29 | 'cache', 30 | 'contents', 31 | 'contentType', 32 | 'crossDomain', 33 | 'data', 34 | 'dataType', 35 | 'headers', 36 | 'ifModified', 37 | 'mimeType', 38 | 'password', 39 | 'processData', 40 | 'timeout', 41 | 'traditional', 42 | 'type', 43 | 'url', 44 | 'username' 45 | ], 46 | convert = function (p) { 47 | return p; 48 | }; 49 | 50 | $.ajaxSetup({ 51 | converters: { 52 | 'postmessage text': convert, 53 | 'postmessage json': convert, 54 | 'postmessage html': convert 55 | } 56 | }); 57 | 58 | $.ajaxTransport('postmessage', function (options) { 59 | if (options.postMessage && window.postMessage) { 60 | var iframe, 61 | loc = $('').prop('href', options.postMessage)[0], 62 | target = loc.protocol + '//' + loc.host, 63 | xhrUpload = options.xhr().upload; 64 | return { 65 | send: function (_, completeCallback) { 66 | counter += 1; 67 | var message = { 68 | id: 'postmessage-transport-' + counter 69 | }, 70 | eventName = 'message.' + message.id; 71 | iframe = $( 72 | '' 75 | ).bind('load', function () { 76 | $.each(names, function (i, name) { 77 | message[name] = options[name]; 78 | }); 79 | message.dataType = message.dataType.replace('postmessage ', ''); 80 | $(window).bind(eventName, function (e) { 81 | e = e.originalEvent; 82 | var data = e.data, 83 | ev; 84 | if (e.origin === target && data.id === message.id) { 85 | if (data.type === 'progress') { 86 | ev = document.createEvent('Event'); 87 | ev.initEvent(data.type, false, true); 88 | $.extend(ev, data); 89 | xhrUpload.dispatchEvent(ev); 90 | } else { 91 | completeCallback( 92 | data.status, 93 | data.statusText, 94 | {postmessage: data.result}, 95 | data.headers 96 | ); 97 | iframe.remove(); 98 | $(window).unbind(eventName); 99 | } 100 | } 101 | }); 102 | iframe[0].contentWindow.postMessage( 103 | message, 104 | target 105 | ); 106 | }).appendTo(document.body); 107 | }, 108 | abort: function () { 109 | if (iframe) { 110 | iframe.remove(); 111 | } 112 | } 113 | }; 114 | } 115 | }); 116 | 117 | })); 118 | -------------------------------------------------------------------------------- /public/javascripts/cors/jquery.xdr-transport.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery XDomainRequest Transport Plugin 1.1.3 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2011, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | * 11 | * Based on Julian Aubourg's ajaxHooks xdr.js: 12 | * https://github.com/jaubourg/ajaxHooks/ 13 | */ 14 | 15 | /* global define, window, XDomainRequest */ 16 | 17 | (function (factory) { 18 | 'use strict'; 19 | if (typeof define === 'function' && define.amd) { 20 | // Register as an anonymous AMD module: 21 | define(['jquery'], factory); 22 | } else { 23 | // Browser globals: 24 | factory(window.jQuery); 25 | } 26 | }(function ($) { 27 | 'use strict'; 28 | if (window.XDomainRequest && !$.support.cors) { 29 | $.ajaxTransport(function (s) { 30 | if (s.crossDomain && s.async) { 31 | if (s.timeout) { 32 | s.xdrTimeout = s.timeout; 33 | delete s.timeout; 34 | } 35 | var xdr; 36 | return { 37 | send: function (headers, completeCallback) { 38 | var addParamChar = /\?/.test(s.url) ? '&' : '?'; 39 | function callback(status, statusText, responses, responseHeaders) { 40 | xdr.onload = xdr.onerror = xdr.ontimeout = $.noop; 41 | xdr = null; 42 | completeCallback(status, statusText, responses, responseHeaders); 43 | } 44 | xdr = new XDomainRequest(); 45 | // XDomainRequest only supports GET and POST: 46 | if (s.type === 'DELETE') { 47 | s.url = s.url + addParamChar + '_method=DELETE'; 48 | s.type = 'POST'; 49 | } else if (s.type === 'PUT') { 50 | s.url = s.url + addParamChar + '_method=PUT'; 51 | s.type = 'POST'; 52 | } else if (s.type === 'PATCH') { 53 | s.url = s.url + addParamChar + '_method=PATCH'; 54 | s.type = 'POST'; 55 | } 56 | xdr.open(s.type, s.url); 57 | xdr.onload = function () { 58 | callback( 59 | 200, 60 | 'OK', 61 | {text: xdr.responseText}, 62 | 'Content-Type: ' + xdr.contentType 63 | ); 64 | }; 65 | xdr.onerror = function () { 66 | callback(404, 'Not Found'); 67 | }; 68 | if (s.xdrTimeout) { 69 | xdr.ontimeout = function () { 70 | callback(0, 'timeout'); 71 | }; 72 | xdr.timeout = s.xdrTimeout; 73 | } 74 | xdr.send((s.hasContent && s.data) || null); 75 | }, 76 | abort: function () { 77 | if (xdr) { 78 | xdr.onerror = $.noop(); 79 | xdr.abort(); 80 | } 81 | } 82 | }; 83 | } 84 | }); 85 | } 86 | })); 87 | -------------------------------------------------------------------------------- /public/javascripts/uploader/canvas-to-blob.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.Blob&&function(){try{return Boolean(new Blob)}catch(a){return!1}}(),d=c&&a.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(a){return!1}}(),e=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||a.MSBlobBuilder,f=(c||e)&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,f,g,h,i,j;for(b=a.split(",")[0].indexOf("base64")>=0?atob(a.split(",")[1]):decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f),h=0;hf?a:f)-e-1),this.slideWidth*c,0);a=this.circle(a),this.move(f,this.slideWidth*c,b),this.move(a,0,b),this.options.continuous&&this.move(this.circle(a-c),-(this.slideWidth*c),0)}else a=this.circle(a),this.animate(f*-this.slideWidth,a*-this.slideWidth,b);this.onslide(a)}},getIndex:function(){return this.index},getNumber:function(){return this.num},prev:function(){(this.options.continuous||this.index)&&this.slide(this.index-1)},next:function(){(this.options.continuous||this.index1&&(this.timeout=this.setTimeout(!this.requestAnimationFrame&&this.slide||function(a,c){b.animationFrameId=b.requestAnimationFrame.call(window,function(){b.slide(a,c)})},[this.index+1,this.options.slideshowTransitionSpeed],this.interval)),this.container.addClass(this.options.playingClass)},pause:function(){window.clearTimeout(this.timeout),this.interval=null,this.container.removeClass(this.options.playingClass)},add:function(a){var b;for(a.concat||(a=Array.prototype.slice.call(a)),this.list.concat||(this.list=Array.prototype.slice.call(this.list)),this.list=this.list.concat(a),this.num=this.list.length,this.num>2&&null===this.options.continuous&&(this.options.continuous=!0,this.container.removeClass(this.options.leftEdgeClass)),this.container.removeClass(this.options.rightEdgeClass).removeClass(this.options.singleClass),b=this.num-a.length;bc?(d.slidesContainer[0].style.left=b+"px",d.ontransitionend(),void window.clearInterval(f)):void(d.slidesContainer[0].style.left=(b-a)*(Math.floor(g/c*100)/100)+a+"px")},4)},preventDefault:function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},onresize:function(){this.initSlides(!0)},onmousedown:function(a){a.which&&1===a.which&&"VIDEO"!==a.target.nodeName&&(a.preventDefault(),(a.originalEvent||a).touches=[{pageX:a.pageX,pageY:a.pageY}],this.ontouchstart(a))},onmousemove:function(a){this.touchStart&&((a.originalEvent||a).touches=[{pageX:a.pageX,pageY:a.pageY}],this.ontouchmove(a))},onmouseup:function(a){this.touchStart&&(this.ontouchend(a),delete this.touchStart)},onmouseout:function(b){if(this.touchStart){var c=b.target,d=b.relatedTarget;(!d||d!==c&&!a.contains(c,d))&&this.onmouseup(b)}},ontouchstart:function(a){this.options.stopTouchEventsPropagation&&this.stopPropagation(a);var b=(a.originalEvent||a).touches[0];this.touchStart={x:b.pageX,y:b.pageY,time:Date.now()},this.isScrolling=void 0,this.touchDelta={}},ontouchmove:function(a){this.options.stopTouchEventsPropagation&&this.stopPropagation(a);var b,c,d=(a.originalEvent||a).touches[0],e=(a.originalEvent||a).scale,f=this.index;if(!(d.length>1||e&&1!==e))if(this.options.disableScroll&&a.preventDefault(),this.touchDelta={x:d.pageX-this.touchStart.x,y:d.pageY-this.touchStart.y},b=this.touchDelta.x,void 0===this.isScrolling&&(this.isScrolling=this.isScrolling||Math.abs(b)0||f===this.num-1&&0>b?Math.abs(b)/this.slideWidth+1:1,c=[f],f&&c.push(f-1),f20||Math.abs(this.touchDelta.x)>i/2,l=!g&&this.touchDelta.x>0||g===this.num-1&&this.touchDelta.x<0,m=!k&&this.options.closeOnSwipeUpOrDown&&(j&&Math.abs(this.touchDelta.y)>20||Math.abs(this.touchDelta.y)>this.slideHeight/2);this.options.continuous&&(l=!1),b=this.touchDelta.x<0?-1:1,this.isScrolling?m?this.close():this.translateY(g,0,h):k&&!l?(c=g+b,d=g-b,e=i*b,f=-i*b,this.options.continuous?(this.move(this.circle(c),e,0),this.move(this.circle(g-2*b),f,0)):c>=0&&cthis.container[0].clientHeight&&(d.style.maxHeight=this.container[0].clientHeight),this.interval&&this.slides[this.index]===e&&this.play(),this.setTimeout(this.options.onslidecomplete,[c,e]))},onload:function(a){this.oncomplete(a)},onerror:function(a){this.oncomplete(a)},onkeydown:function(a){switch(a.which||a.keyCode){case 13:this.options.toggleControlsOnReturn&&(this.preventDefault(a),this.toggleControls());break;case 27:this.options.closeOnEscape&&this.close();break;case 32:this.options.toggleSlideshowOnSpace&&(this.preventDefault(a),this.toggleSlideshow());break;case 37:this.options.enableKeyboardNavigation&&(this.preventDefault(a),this.prev());break;case 39:this.options.enableKeyboardNavigation&&(this.preventDefault(a),this.next())}},handleClick:function(b){var c=this.options,d=b.target||b.srcElement,e=d.parentNode,f=function(b){return a(d).hasClass(b)||a(e).hasClass(b)};f(c.toggleClass)?(this.preventDefault(b),this.toggleControls()):f(c.prevClass)?(this.preventDefault(b),this.prev()):f(c.nextClass)?(this.preventDefault(b),this.next()):f(c.closeClass)?(this.preventDefault(b),this.close()):f(c.playPauseClass)?(this.preventDefault(b),this.toggleSlideshow()):e===this.slidesContainer[0]?(this.preventDefault(b),c.closeOnSlideClick?this.close():this.toggleControls()):e.parentNode&&e.parentNode===this.slidesContainer[0]&&(this.preventDefault(b),this.toggleControls())},onclick:function(a){return this.options.emulateTouchEvents&&this.touchDelta&&(Math.abs(this.touchDelta.x)>20||Math.abs(this.touchDelta.y)>20)?void delete this.touchDelta:this.handleClick(a)},updateEdgeClasses:function(a){a?this.container.removeClass(this.options.leftEdgeClass):this.container.addClass(this.options.leftEdgeClass),a===this.num-1?this.container.addClass(this.options.rightEdgeClass):this.container.removeClass(this.options.rightEdgeClass)},handleSlide:function(a){this.options.continuous||this.updateEdgeClasses(a),this.loadElements(a),this.options.unloadElements&&this.unloadElements(a),this.setTitle(a)},onslide:function(a){this.index=a,this.handleSlide(a),this.setTimeout(this.options.onslide,[a,this.slides[a]])},setTitle:function(a){var b=this.slides[a].firstChild.title,c=this.titleElement;c.length&&(this.titleElement.empty(),b&&c[0].appendChild(document.createTextNode(b)))},setTimeout:function(a,b,c){var d=this;return a&&window.setTimeout(function(){a.apply(d,b||[])},c||0)},imageFactory:function(b,c){var d,e,f,g=this,h=this.imagePrototype.cloneNode(!1),i=b,j=this.options.stretchImages,k=function(b){if(!d){if(b={type:b.type,target:e},!e.parentNode)return g.setTimeout(k,[b]);d=!0,a(h).off("load error",k),j&&"load"===b.type&&(e.style.background='url("'+i+'") center no-repeat',e.style.backgroundSize=j),c(b)}};return"string"!=typeof i&&(i=this.getItemProperty(b,this.options.urlProperty),f=this.getItemProperty(b,this.options.titleProperty)),j===!0&&(j="contain"),j=this.support.backgroundSize&&this.support.backgroundSize[j]&&j,j?e=this.elementPrototype.cloneNode(!1):(e=h,h.draggable=!1),f&&(e.title=f),a(h).on("load error",k),h.src=i,e},createElement:function(b,c){var d=b&&this.getItemProperty(b,this.options.typeProperty),e=d&&this[d.split("/")[0]+"Factory"]||this.imageFactory,f=b&&e.call(this,b,c);return f||(f=this.elementPrototype.cloneNode(!1),this.setTimeout(c,[{type:"error",target:f}])),a(f).addClass(this.options.slideContentClass),f},loadElement:function(b){this.elements[b]||(this.slides[b].firstChild?this.elements[b]=a(this.slides[b]).hasClass(this.options.slideErrorClass)?3:2:(this.elements[b]=1,a(this.slides[b]).addClass(this.options.slideLoadingClass),this.slides[b].appendChild(this.createElement(this.list[b],this.proxyListener))))},loadElements:function(a){var b,c=Math.min(this.num,2*this.options.preloadRange+1),d=a;for(b=0;c>b;b+=1)d+=b*(b%2===0?-1:1),d=this.circle(d),this.loadElement(d)},unloadElements:function(a){var b,c,d;for(b in this.elements)this.elements.hasOwnProperty(b)&&(d=Math.abs(a-b),d>this.options.preloadRange&&d+this.options.preloadRangea?-this.slideWidth:this.index options.maxFileSize) || 157 | (options.fileTypes && 158 | !options.fileTypes.test(file.type)) || 159 | !loadImage( 160 | file, 161 | function (img) { 162 | if (img.src) { 163 | data.img = img; 164 | } 165 | dfd.resolveWith(that, [data]); 166 | }, 167 | options 168 | )) { 169 | return data; 170 | } 171 | return dfd.promise(); 172 | }, 173 | 174 | // Resizes the image given as data.canvas or data.img 175 | // and updates data.canvas or data.img with the resized image. 176 | // Also stores the resized image as preview property. 177 | // Accepts the options maxWidth, maxHeight, minWidth, 178 | // minHeight, canvas and crop: 179 | resizeImage: function (data, options) { 180 | if (options.disabled || !(data.canvas || data.img)) { 181 | return data; 182 | } 183 | options = $.extend({canvas: true}, options); 184 | var that = this, 185 | dfd = $.Deferred(), 186 | img = (options.canvas && data.canvas) || data.img, 187 | resolve = function (newImg) { 188 | if (newImg && (newImg.width !== img.width || 189 | newImg.height !== img.height || 190 | options.forceResize)) { 191 | data[newImg.getContext ? 'canvas' : 'img'] = newImg; 192 | } 193 | data.preview = newImg; 194 | dfd.resolveWith(that, [data]); 195 | }, 196 | thumbnail; 197 | if (data.exif) { 198 | if (options.orientation === true) { 199 | options.orientation = data.exif.get('Orientation'); 200 | } 201 | if (options.thumbnail) { 202 | thumbnail = data.exif.get('Thumbnail'); 203 | if (thumbnail) { 204 | loadImage(thumbnail, resolve, options); 205 | return dfd.promise(); 206 | } 207 | } 208 | // Prevent orienting the same image twice: 209 | if (data.orientation) { 210 | delete options.orientation; 211 | } else { 212 | data.orientation = options.orientation; 213 | } 214 | } 215 | if (img) { 216 | resolve(loadImage.scale(img, options)); 217 | return dfd.promise(); 218 | } 219 | return data; 220 | }, 221 | 222 | // Saves the processed image given as data.canvas 223 | // inplace at data.index of data.files: 224 | saveImage: function (data, options) { 225 | if (!data.canvas || options.disabled) { 226 | return data; 227 | } 228 | var that = this, 229 | file = data.files[data.index], 230 | dfd = $.Deferred(); 231 | if (data.canvas.toBlob) { 232 | data.canvas.toBlob( 233 | function (blob) { 234 | if (!blob.name) { 235 | if (file.type === blob.type) { 236 | blob.name = file.name; 237 | } else if (file.name) { 238 | blob.name = file.name.replace( 239 | /\..+$/, 240 | '.' + blob.type.substr(6) 241 | ); 242 | } 243 | } 244 | // Don't restore invalid meta data: 245 | if (file.type !== blob.type) { 246 | delete data.imageHead; 247 | } 248 | // Store the created blob at the position 249 | // of the original file in the files list: 250 | data.files[data.index] = blob; 251 | dfd.resolveWith(that, [data]); 252 | }, 253 | options.type || file.type, 254 | options.quality 255 | ); 256 | } else { 257 | return data; 258 | } 259 | return dfd.promise(); 260 | }, 261 | 262 | loadImageMetaData: function (data, options) { 263 | if (options.disabled) { 264 | return data; 265 | } 266 | var that = this, 267 | dfd = $.Deferred(); 268 | loadImage.parseMetaData(data.files[data.index], function (result) { 269 | $.extend(data, result); 270 | dfd.resolveWith(that, [data]); 271 | }, options); 272 | return dfd.promise(); 273 | }, 274 | 275 | saveImageMetaData: function (data, options) { 276 | if (!(data.imageHead && data.canvas && 277 | data.canvas.toBlob && !options.disabled)) { 278 | return data; 279 | } 280 | var file = data.files[data.index], 281 | blob = new Blob([ 282 | data.imageHead, 283 | // Resized images always have a head size of 20 bytes, 284 | // including the JPEG marker and a minimal JFIF header: 285 | this._blobSlice.call(file, 20) 286 | ], {type: file.type}); 287 | blob.name = file.name; 288 | data.files[data.index] = blob; 289 | return data; 290 | }, 291 | 292 | // Sets the resized version of the image as a property of the 293 | // file object, must be called after "saveImage": 294 | setImage: function (data, options) { 295 | if (data.preview && !options.disabled) { 296 | data.files[data.index][options.name || 'preview'] = data.preview; 297 | } 298 | return data; 299 | }, 300 | 301 | deleteImageReferences: function (data, options) { 302 | if (!options.disabled) { 303 | delete data.img; 304 | delete data.canvas; 305 | delete data.preview; 306 | delete data.imageHead; 307 | } 308 | return data; 309 | } 310 | 311 | } 312 | 313 | }); 314 | 315 | })); 316 | -------------------------------------------------------------------------------- /public/javascripts/uploader/jquery.fileupload-process.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery File Upload Processing Plugin 1.3.0 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2012, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /* jshint nomen:false */ 13 | /* global define, window */ 14 | 15 | (function (factory) { 16 | 'use strict'; 17 | if (typeof define === 'function' && define.amd) { 18 | // Register as an anonymous AMD module: 19 | define([ 20 | 'jquery', 21 | './jquery.fileupload' 22 | ], factory); 23 | } else { 24 | // Browser globals: 25 | factory( 26 | window.jQuery 27 | ); 28 | } 29 | }(function ($) { 30 | 'use strict'; 31 | 32 | var originalAdd = $.blueimp.fileupload.prototype.options.add; 33 | 34 | // The File Upload Processing plugin extends the fileupload widget 35 | // with file processing functionality: 36 | $.widget('blueimp.fileupload', $.blueimp.fileupload, { 37 | 38 | options: { 39 | // The list of processing actions: 40 | processQueue: [ 41 | /* 42 | { 43 | action: 'log', 44 | type: 'debug' 45 | } 46 | */ 47 | ], 48 | add: function (e, data) { 49 | var $this = $(this); 50 | data.process(function () { 51 | return $this.fileupload('process', data); 52 | }); 53 | originalAdd.call(this, e, data); 54 | } 55 | }, 56 | 57 | processActions: { 58 | /* 59 | log: function (data, options) { 60 | console[options.type]( 61 | 'Processing "' + data.files[data.index].name + '"' 62 | ); 63 | } 64 | */ 65 | }, 66 | 67 | _processFile: function (data, originalData) { 68 | var that = this, 69 | dfd = $.Deferred().resolveWith(that, [data]), 70 | chain = dfd.promise(); 71 | this._trigger('process', null, data); 72 | $.each(data.processQueue, function (i, settings) { 73 | var func = function (data) { 74 | if (originalData.errorThrown) { 75 | return $.Deferred() 76 | .rejectWith(that, [originalData]).promise(); 77 | } 78 | return that.processActions[settings.action].call( 79 | that, 80 | data, 81 | settings 82 | ); 83 | }; 84 | chain = chain.pipe(func, settings.always && func); 85 | }); 86 | chain 87 | .done(function () { 88 | that._trigger('processdone', null, data); 89 | that._trigger('processalways', null, data); 90 | }) 91 | .fail(function () { 92 | that._trigger('processfail', null, data); 93 | that._trigger('processalways', null, data); 94 | }); 95 | return chain; 96 | }, 97 | 98 | // Replaces the settings of each processQueue item that 99 | // are strings starting with an "@", using the remaining 100 | // substring as key for the option map, 101 | // e.g. "@autoUpload" is replaced with options.autoUpload: 102 | _transformProcessQueue: function (options) { 103 | var processQueue = []; 104 | $.each(options.processQueue, function () { 105 | var settings = {}, 106 | action = this.action, 107 | prefix = this.prefix === true ? action : this.prefix; 108 | $.each(this, function (key, value) { 109 | if ($.type(value) === 'string' && 110 | value.charAt(0) === '@') { 111 | settings[key] = options[ 112 | value.slice(1) || (prefix ? prefix + 113 | key.charAt(0).toUpperCase() + key.slice(1) : key) 114 | ]; 115 | } else { 116 | settings[key] = value; 117 | } 118 | 119 | }); 120 | processQueue.push(settings); 121 | }); 122 | options.processQueue = processQueue; 123 | }, 124 | 125 | // Returns the number of files currently in the processsing queue: 126 | processing: function () { 127 | return this._processing; 128 | }, 129 | 130 | // Processes the files given as files property of the data parameter, 131 | // returns a Promise object that allows to bind callbacks: 132 | process: function (data) { 133 | var that = this, 134 | options = $.extend({}, this.options, data); 135 | if (options.processQueue && options.processQueue.length) { 136 | this._transformProcessQueue(options); 137 | if (this._processing === 0) { 138 | this._trigger('processstart'); 139 | } 140 | $.each(data.files, function (index) { 141 | var opts = index ? $.extend({}, options) : options, 142 | func = function () { 143 | if (data.errorThrown) { 144 | return $.Deferred() 145 | .rejectWith(that, [data]).promise(); 146 | } 147 | return that._processFile(opts, data); 148 | }; 149 | opts.index = index; 150 | that._processing += 1; 151 | that._processingQueue = that._processingQueue.pipe(func, func) 152 | .always(function () { 153 | that._processing -= 1; 154 | if (that._processing === 0) { 155 | that._trigger('processstop'); 156 | } 157 | }); 158 | }); 159 | } 160 | return this._processingQueue; 161 | }, 162 | 163 | _create: function () { 164 | this._super(); 165 | this._processing = 0; 166 | this._processingQueue = $.Deferred().resolveWith(this) 167 | .promise(); 168 | } 169 | 170 | }); 171 | 172 | })); 173 | -------------------------------------------------------------------------------- /public/javascripts/uploader/jquery.fileupload-ui.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery File Upload User Interface Plugin 9.5.2 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2010, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /* jshint nomen:false */ 13 | /* global define, window */ 14 | 15 | (function (factory) { 16 | 'use strict'; 17 | if (typeof define === 'function' && define.amd) { 18 | // Register as an anonymous AMD module: 19 | define([ 20 | 'jquery', 21 | 'tmpl', 22 | './jquery.fileupload-image', 23 | './jquery.fileupload-audio', 24 | './jquery.fileupload-video', 25 | './jquery.fileupload-validate' 26 | ], factory); 27 | } else { 28 | // Browser globals: 29 | factory( 30 | window.jQuery, 31 | window.tmpl 32 | ); 33 | } 34 | }(function ($, tmpl) { 35 | 'use strict'; 36 | 37 | $.blueimp.fileupload.prototype._specialOptions.push( 38 | 'filesContainer', 39 | 'uploadTemplateId', 40 | 'downloadTemplateId' 41 | ); 42 | 43 | // The UI version extends the file upload widget 44 | // and adds complete user interface interaction: 45 | $.widget('blueimp.fileupload', $.blueimp.fileupload, { 46 | 47 | options: { 48 | // By default, files added to the widget are uploaded as soon 49 | // as the user clicks on the start buttons. To enable automatic 50 | // uploads, set the following option to true: 51 | autoUpload: false, 52 | // The ID of the upload template: 53 | uploadTemplateId: 'template-upload', 54 | // The ID of the download template: 55 | downloadTemplateId: 'template-download', 56 | // The container for the list of files. If undefined, it is set to 57 | // an element with class "files" inside of the widget element: 58 | filesContainer: undefined, 59 | // By default, files are appended to the files container. 60 | // Set the following option to true, to prepend files instead: 61 | prependFiles: false, 62 | // The expected data type of the upload response, sets the dataType 63 | // option of the $.ajax upload requests: 64 | dataType: 'json', 65 | 66 | // Function returning the current number of files, 67 | // used by the maxNumberOfFiles validation: 68 | getNumberOfFiles: function () { 69 | return this.filesContainer.children() 70 | .not('.processing').length; 71 | }, 72 | 73 | // Callback to retrieve the list of files from the server response: 74 | getFilesFromResponse: function (data) { 75 | if (data.result && $.isArray(data.result.files)) { 76 | return data.result.files; 77 | } 78 | return []; 79 | }, 80 | 81 | // The add callback is invoked as soon as files are added to the fileupload 82 | // widget (via file input selection, drag & drop or add API call). 83 | // See the basic file upload widget for more information: 84 | add: function (e, data) { 85 | if (e.isDefaultPrevented()) { 86 | return false; 87 | } 88 | var $this = $(this), 89 | that = $this.data('blueimp-fileupload') || 90 | $this.data('fileupload'), 91 | options = that.options; 92 | data.context = that._renderUpload(data.files) 93 | .data('data', data) 94 | .addClass('processing'); 95 | options.filesContainer[ 96 | options.prependFiles ? 'prepend' : 'append' 97 | ](data.context); 98 | that._forceReflow(data.context); 99 | that._transition(data.context); 100 | data.process(function () { 101 | return $this.fileupload('process', data); 102 | }).always(function () { 103 | data.context.each(function (index) { 104 | $(this).find('.size').text( 105 | that._formatFileSize(data.files[index].size) 106 | ); 107 | }).removeClass('processing'); 108 | that._renderPreviews(data); 109 | }).done(function () { 110 | data.context.find('.start').prop('disabled', false); 111 | if ((that._trigger('added', e, data) !== false) && 112 | (options.autoUpload || data.autoUpload) && 113 | data.autoUpload !== false) { 114 | data.submit(); 115 | } 116 | }).fail(function () { 117 | if (data.files.error) { 118 | data.context.each(function (index) { 119 | var error = data.files[index].error; 120 | if (error) { 121 | $(this).find('.error').text(error); 122 | } 123 | }); 124 | } 125 | }); 126 | }, 127 | // Callback for the start of each file upload request: 128 | send: function (e, data) { 129 | if (e.isDefaultPrevented()) { 130 | return false; 131 | } 132 | var that = $(this).data('blueimp-fileupload') || 133 | $(this).data('fileupload'); 134 | if (data.context && data.dataType && 135 | data.dataType.substr(0, 6) === 'iframe') { 136 | // Iframe Transport does not support progress events. 137 | // In lack of an indeterminate progress bar, we set 138 | // the progress to 100%, showing the full animated bar: 139 | data.context 140 | .find('.progress').addClass( 141 | !$.support.transition && 'progress-animated' 142 | ) 143 | .attr('aria-valuenow', 100) 144 | .children().first().css( 145 | 'width', 146 | '100%' 147 | ); 148 | } 149 | return that._trigger('sent', e, data); 150 | }, 151 | // Callback for successful uploads: 152 | done: function (e, data) { 153 | if (e.isDefaultPrevented()) { 154 | return false; 155 | } 156 | var that = $(this).data('blueimp-fileupload') || 157 | $(this).data('fileupload'), 158 | getFilesFromResponse = data.getFilesFromResponse || 159 | that.options.getFilesFromResponse, 160 | files = getFilesFromResponse(data), 161 | template, 162 | deferred; 163 | if (data.context) { 164 | data.context.each(function (index) { 165 | var file = files[index] || 166 | {error: 'Empty file upload result'}; 167 | deferred = that._addFinishedDeferreds(); 168 | that._transition($(this)).done( 169 | function () { 170 | var node = $(this); 171 | template = that._renderDownload([file]) 172 | .replaceAll(node); 173 | that._forceReflow(template); 174 | that._transition(template).done( 175 | function () { 176 | data.context = $(this); 177 | that._trigger('completed', e, data); 178 | that._trigger('finished', e, data); 179 | deferred.resolve(); 180 | } 181 | ); 182 | } 183 | ); 184 | }); 185 | } else { 186 | template = that._renderDownload(files)[ 187 | that.options.prependFiles ? 'prependTo' : 'appendTo' 188 | ](that.options.filesContainer); 189 | that._forceReflow(template); 190 | deferred = that._addFinishedDeferreds(); 191 | that._transition(template).done( 192 | function () { 193 | data.context = $(this); 194 | that._trigger('completed', e, data); 195 | that._trigger('finished', e, data); 196 | deferred.resolve(); 197 | } 198 | ); 199 | } 200 | }, 201 | // Callback for failed (abort or error) uploads: 202 | fail: function (e, data) { 203 | if (e.isDefaultPrevented()) { 204 | return false; 205 | } 206 | var that = $(this).data('blueimp-fileupload') || 207 | $(this).data('fileupload'), 208 | template, 209 | deferred; 210 | if (data.context) { 211 | data.context.each(function (index) { 212 | if (data.errorThrown !== 'abort') { 213 | var file = data.files[index]; 214 | file.error = file.error || data.errorThrown || 215 | true; 216 | deferred = that._addFinishedDeferreds(); 217 | that._transition($(this)).done( 218 | function () { 219 | var node = $(this); 220 | template = that._renderDownload([file]) 221 | .replaceAll(node); 222 | that._forceReflow(template); 223 | that._transition(template).done( 224 | function () { 225 | data.context = $(this); 226 | that._trigger('failed', e, data); 227 | that._trigger('finished', e, data); 228 | deferred.resolve(); 229 | } 230 | ); 231 | } 232 | ); 233 | } else { 234 | deferred = that._addFinishedDeferreds(); 235 | that._transition($(this)).done( 236 | function () { 237 | $(this).remove(); 238 | that._trigger('failed', e, data); 239 | that._trigger('finished', e, data); 240 | deferred.resolve(); 241 | } 242 | ); 243 | } 244 | }); 245 | } else if (data.errorThrown !== 'abort') { 246 | data.context = that._renderUpload(data.files)[ 247 | that.options.prependFiles ? 'prependTo' : 'appendTo' 248 | ](that.options.filesContainer) 249 | .data('data', data); 250 | that._forceReflow(data.context); 251 | deferred = that._addFinishedDeferreds(); 252 | that._transition(data.context).done( 253 | function () { 254 | data.context = $(this); 255 | that._trigger('failed', e, data); 256 | that._trigger('finished', e, data); 257 | deferred.resolve(); 258 | } 259 | ); 260 | } else { 261 | that._trigger('failed', e, data); 262 | that._trigger('finished', e, data); 263 | that._addFinishedDeferreds().resolve(); 264 | } 265 | }, 266 | // Callback for upload progress events: 267 | progress: function (e, data) { 268 | if (e.isDefaultPrevented()) { 269 | return false; 270 | } 271 | var progress = Math.floor(data.loaded / data.total * 100); 272 | if (data.context) { 273 | data.context.each(function () { 274 | $(this).find('.progress') 275 | .attr('aria-valuenow', progress) 276 | .children().first().css( 277 | 'width', 278 | progress + '%' 279 | ); 280 | }); 281 | } 282 | }, 283 | // Callback for global upload progress events: 284 | progressall: function (e, data) { 285 | if (e.isDefaultPrevented()) { 286 | return false; 287 | } 288 | var $this = $(this), 289 | progress = Math.floor(data.loaded / data.total * 100), 290 | globalProgressNode = $this.find('.fileupload-progress'), 291 | extendedProgressNode = globalProgressNode 292 | .find('.progress-extended'); 293 | if (extendedProgressNode.length) { 294 | extendedProgressNode.html( 295 | ($this.data('blueimp-fileupload') || $this.data('fileupload')) 296 | ._renderExtendedProgress(data) 297 | ); 298 | } 299 | globalProgressNode 300 | .find('.progress') 301 | .attr('aria-valuenow', progress) 302 | .children().first().css( 303 | 'width', 304 | progress + '%' 305 | ); 306 | }, 307 | // Callback for uploads start, equivalent to the global ajaxStart event: 308 | start: function (e) { 309 | if (e.isDefaultPrevented()) { 310 | return false; 311 | } 312 | var that = $(this).data('blueimp-fileupload') || 313 | $(this).data('fileupload'); 314 | that._resetFinishedDeferreds(); 315 | that._transition($(this).find('.fileupload-progress')).done( 316 | function () { 317 | that._trigger('started', e); 318 | } 319 | ); 320 | }, 321 | // Callback for uploads stop, equivalent to the global ajaxStop event: 322 | stop: function (e) { 323 | if (e.isDefaultPrevented()) { 324 | return false; 325 | } 326 | var that = $(this).data('blueimp-fileupload') || 327 | $(this).data('fileupload'), 328 | deferred = that._addFinishedDeferreds(); 329 | $.when.apply($, that._getFinishedDeferreds()) 330 | .done(function () { 331 | that._trigger('stopped', e); 332 | }); 333 | that._transition($(this).find('.fileupload-progress')).done( 334 | function () { 335 | $(this).find('.progress') 336 | .attr('aria-valuenow', '0') 337 | .children().first().css('width', '0%'); 338 | $(this).find('.progress-extended').html(' '); 339 | deferred.resolve(); 340 | } 341 | ); 342 | }, 343 | processstart: function (e) { 344 | if (e.isDefaultPrevented()) { 345 | return false; 346 | } 347 | $(this).addClass('fileupload-processing'); 348 | }, 349 | processstop: function (e) { 350 | if (e.isDefaultPrevented()) { 351 | return false; 352 | } 353 | $(this).removeClass('fileupload-processing'); 354 | }, 355 | // Callback for file deletion: 356 | destroy: function (e, data) { 357 | if (e.isDefaultPrevented()) { 358 | return false; 359 | } 360 | var that = $(this).data('blueimp-fileupload') || 361 | $(this).data('fileupload'), 362 | removeNode = function () { 363 | that._transition(data.context).done( 364 | function () { 365 | $(this).remove(); 366 | that._trigger('destroyed', e, data); 367 | } 368 | ); 369 | }; 370 | if (data.url) { 371 | data.dataType = data.dataType || that.options.dataType; 372 | $.ajax(data).done(removeNode).fail(function () { 373 | that._trigger('destroyfailed', e, data); 374 | }); 375 | } else { 376 | removeNode(); 377 | } 378 | } 379 | }, 380 | 381 | _resetFinishedDeferreds: function () { 382 | this._finishedUploads = []; 383 | }, 384 | 385 | _addFinishedDeferreds: function (deferred) { 386 | if (!deferred) { 387 | deferred = $.Deferred(); 388 | } 389 | this._finishedUploads.push(deferred); 390 | return deferred; 391 | }, 392 | 393 | _getFinishedDeferreds: function () { 394 | return this._finishedUploads; 395 | }, 396 | 397 | // Link handler, that allows to download files 398 | // by drag & drop of the links to the desktop: 399 | _enableDragToDesktop: function () { 400 | var link = $(this), 401 | url = link.prop('href'), 402 | name = link.prop('download'), 403 | type = 'application/octet-stream'; 404 | link.bind('dragstart', function (e) { 405 | try { 406 | e.originalEvent.dataTransfer.setData( 407 | 'DownloadURL', 408 | [type, name, url].join(':') 409 | ); 410 | } catch (ignore) {} 411 | }); 412 | }, 413 | 414 | _formatFileSize: function (bytes) { 415 | if (typeof bytes !== 'number') { 416 | return ''; 417 | } 418 | if (bytes >= 1000000000) { 419 | return (bytes / 1000000000).toFixed(2) + ' GB'; 420 | } 421 | if (bytes >= 1000000) { 422 | return (bytes / 1000000).toFixed(2) + ' MB'; 423 | } 424 | return (bytes / 1000).toFixed(2) + ' KB'; 425 | }, 426 | 427 | _formatBitrate: function (bits) { 428 | if (typeof bits !== 'number') { 429 | return ''; 430 | } 431 | if (bits >= 1000000000) { 432 | return (bits / 1000000000).toFixed(2) + ' Gbit/s'; 433 | } 434 | if (bits >= 1000000) { 435 | return (bits / 1000000).toFixed(2) + ' Mbit/s'; 436 | } 437 | if (bits >= 1000) { 438 | return (bits / 1000).toFixed(2) + ' kbit/s'; 439 | } 440 | return bits.toFixed(2) + ' bit/s'; 441 | }, 442 | 443 | _formatTime: function (seconds) { 444 | var date = new Date(seconds * 1000), 445 | days = Math.floor(seconds / 86400); 446 | days = days ? days + 'd ' : ''; 447 | return days + 448 | ('0' + date.getUTCHours()).slice(-2) + ':' + 449 | ('0' + date.getUTCMinutes()).slice(-2) + ':' + 450 | ('0' + date.getUTCSeconds()).slice(-2); 451 | }, 452 | 453 | _formatPercentage: function (floatValue) { 454 | return (floatValue * 100).toFixed(2) + ' %'; 455 | }, 456 | 457 | _renderExtendedProgress: function (data) { 458 | return this._formatBitrate(data.bitrate) + ' | ' + 459 | this._formatTime( 460 | (data.total - data.loaded) * 8 / data.bitrate 461 | ) + ' | ' + 462 | this._formatPercentage( 463 | data.loaded / data.total 464 | ) + ' | ' + 465 | this._formatFileSize(data.loaded) + ' / ' + 466 | this._formatFileSize(data.total); 467 | }, 468 | 469 | _renderTemplate: function (func, files) { 470 | if (!func) { 471 | return $(); 472 | } 473 | var result = func({ 474 | files: files, 475 | formatFileSize: this._formatFileSize, 476 | options: this.options 477 | }); 478 | if (result instanceof $) { 479 | return result; 480 | } 481 | return $(this.options.templatesContainer).html(result).children(); 482 | }, 483 | 484 | _renderPreviews: function (data) { 485 | data.context.find('.preview').each(function (index, elm) { 486 | $(elm).append(data.files[index].preview); 487 | }); 488 | }, 489 | 490 | _renderUpload: function (files) { 491 | return this._renderTemplate( 492 | this.options.uploadTemplate, 493 | files 494 | ); 495 | }, 496 | 497 | _renderDownload: function (files) { 498 | return this._renderTemplate( 499 | this.options.downloadTemplate, 500 | files 501 | ).find('a[download]').each(this._enableDragToDesktop).end(); 502 | }, 503 | 504 | _startHandler: function (e) { 505 | e.preventDefault(); 506 | var button = $(e.currentTarget), 507 | template = button.closest('.template-upload'), 508 | data = template.data('data'); 509 | button.prop('disabled', true); 510 | if (data && data.submit) { 511 | data.submit(); 512 | } 513 | }, 514 | 515 | _cancelHandler: function (e) { 516 | e.preventDefault(); 517 | var template = $(e.currentTarget) 518 | .closest('.template-upload,.template-download'), 519 | data = template.data('data') || {}; 520 | data.context = data.context || template; 521 | if (data.abort) { 522 | data.abort(); 523 | } else { 524 | data.errorThrown = 'abort'; 525 | this._trigger('fail', e, data); 526 | } 527 | }, 528 | 529 | _deleteHandler: function (e) { 530 | e.preventDefault(); 531 | var button = $(e.currentTarget); 532 | this._trigger('destroy', e, $.extend({ 533 | context: button.closest('.template-download'), 534 | type: 'DELETE' 535 | }, button.data())); 536 | }, 537 | 538 | _forceReflow: function (node) { 539 | return $.support.transition && node.length && 540 | node[0].offsetWidth; 541 | }, 542 | 543 | _transition: function (node) { 544 | var dfd = $.Deferred(); 545 | if ($.support.transition && node.hasClass('fade') && node.is(':visible')) { 546 | node.bind( 547 | $.support.transition.end, 548 | function (e) { 549 | // Make sure we don't respond to other transitions events 550 | // in the container element, e.g. from button elements: 551 | if (e.target === node[0]) { 552 | node.unbind($.support.transition.end); 553 | dfd.resolveWith(node); 554 | } 555 | } 556 | ).toggleClass('in'); 557 | } else { 558 | node.toggleClass('in'); 559 | dfd.resolveWith(node); 560 | } 561 | return dfd; 562 | }, 563 | 564 | _initButtonBarEventHandlers: function () { 565 | var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'), 566 | filesList = this.options.filesContainer; 567 | this._on(fileUploadButtonBar.find('.start'), { 568 | click: function (e) { 569 | e.preventDefault(); 570 | filesList.find('.start').click(); 571 | } 572 | }); 573 | this._on(fileUploadButtonBar.find('.cancel'), { 574 | click: function (e) { 575 | e.preventDefault(); 576 | filesList.find('.cancel').click(); 577 | } 578 | }); 579 | this._on(fileUploadButtonBar.find('.delete'), { 580 | click: function (e) { 581 | e.preventDefault(); 582 | filesList.find('.toggle:checked') 583 | .closest('.template-download') 584 | .find('.delete').click(); 585 | fileUploadButtonBar.find('.toggle') 586 | .prop('checked', false); 587 | } 588 | }); 589 | this._on(fileUploadButtonBar.find('.toggle'), { 590 | change: function (e) { 591 | filesList.find('.toggle').prop( 592 | 'checked', 593 | $(e.currentTarget).is(':checked') 594 | ); 595 | } 596 | }); 597 | }, 598 | 599 | _destroyButtonBarEventHandlers: function () { 600 | this._off( 601 | this.element.find('.fileupload-buttonbar') 602 | .find('.start, .cancel, .delete'), 603 | 'click' 604 | ); 605 | this._off( 606 | this.element.find('.fileupload-buttonbar .toggle'), 607 | 'change.' 608 | ); 609 | }, 610 | 611 | _initEventHandlers: function () { 612 | this._super(); 613 | this._on(this.options.filesContainer, { 614 | 'click .start': this._startHandler, 615 | 'click .cancel': this._cancelHandler, 616 | 'click .delete': this._deleteHandler 617 | }); 618 | this._initButtonBarEventHandlers(); 619 | }, 620 | 621 | _destroyEventHandlers: function () { 622 | this._destroyButtonBarEventHandlers(); 623 | this._off(this.options.filesContainer, 'click'); 624 | this._super(); 625 | }, 626 | 627 | _enableFileInputButton: function () { 628 | this.element.find('.fileinput-button input') 629 | .prop('disabled', false) 630 | .parent().removeClass('disabled'); 631 | }, 632 | 633 | _disableFileInputButton: function () { 634 | this.element.find('.fileinput-button input') 635 | .prop('disabled', true) 636 | .parent().addClass('disabled'); 637 | }, 638 | 639 | _initTemplates: function () { 640 | var options = this.options; 641 | options.templatesContainer = this.document[0].createElement( 642 | options.filesContainer.prop('nodeName') 643 | ); 644 | if (tmpl) { 645 | if (options.uploadTemplateId) { 646 | options.uploadTemplate = tmpl(options.uploadTemplateId); 647 | } 648 | if (options.downloadTemplateId) { 649 | options.downloadTemplate = tmpl(options.downloadTemplateId); 650 | } 651 | } 652 | }, 653 | 654 | _initFilesContainer: function () { 655 | var options = this.options; 656 | if (options.filesContainer === undefined) { 657 | options.filesContainer = this.element.find('.files'); 658 | } else if (!(options.filesContainer instanceof $)) { 659 | options.filesContainer = $(options.filesContainer); 660 | } 661 | }, 662 | 663 | _initSpecialOptions: function () { 664 | this._super(); 665 | this._initFilesContainer(); 666 | this._initTemplates(); 667 | }, 668 | 669 | _create: function () { 670 | this._super(); 671 | this._resetFinishedDeferreds(); 672 | if (!$.support.fileInput) { 673 | this._disableFileInputButton(); 674 | } 675 | }, 676 | 677 | enable: function () { 678 | var wasDisabled = false; 679 | if (this.options.disabled) { 680 | wasDisabled = true; 681 | } 682 | this._super(); 683 | if (wasDisabled) { 684 | this.element.find('input, button').prop('disabled', false); 685 | this._enableFileInputButton(); 686 | } 687 | }, 688 | 689 | disable: function () { 690 | if (!this.options.disabled) { 691 | this.element.find('input, button').prop('disabled', true); 692 | this._disableFileInputButton(); 693 | } 694 | this._super(); 695 | } 696 | 697 | }); 698 | 699 | })); 700 | -------------------------------------------------------------------------------- /public/javascripts/uploader/jquery.fileupload-validate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery File Upload Validation Plugin 1.1.2 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2013, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /* global define, window */ 13 | 14 | (function (factory) { 15 | 'use strict'; 16 | if (typeof define === 'function' && define.amd) { 17 | // Register as an anonymous AMD module: 18 | define([ 19 | 'jquery', 20 | './jquery.fileupload-process' 21 | ], factory); 22 | } else { 23 | // Browser globals: 24 | factory( 25 | window.jQuery 26 | ); 27 | } 28 | }(function ($) { 29 | 'use strict'; 30 | 31 | // Append to the default processQueue: 32 | $.blueimp.fileupload.prototype.options.processQueue.push( 33 | { 34 | action: 'validate', 35 | // Always trigger this action, 36 | // even if the previous action was rejected: 37 | always: true, 38 | // Options taken from the global options map: 39 | acceptFileTypes: '@', 40 | maxFileSize: '@', 41 | minFileSize: '@', 42 | maxNumberOfFiles: '@', 43 | disabled: '@disableValidation' 44 | } 45 | ); 46 | 47 | // The File Upload Validation plugin extends the fileupload widget 48 | // with file validation functionality: 49 | $.widget('blueimp.fileupload', $.blueimp.fileupload, { 50 | 51 | options: { 52 | /* 53 | // The regular expression for allowed file types, matches 54 | // against either file type or file name: 55 | acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, 56 | // The maximum allowed file size in bytes: 57 | maxFileSize: 10000000, // 10 MB 58 | // The minimum allowed file size in bytes: 59 | minFileSize: undefined, // No minimal file size 60 | // The limit of files to be uploaded: 61 | maxNumberOfFiles: 10, 62 | */ 63 | 64 | // Function returning the current number of files, 65 | // has to be overriden for maxNumberOfFiles validation: 66 | getNumberOfFiles: $.noop, 67 | 68 | // Error and info messages: 69 | messages: { 70 | maxNumberOfFiles: 'Maximum number of files exceeded', 71 | acceptFileTypes: 'File type not allowed', 72 | maxFileSize: 'File is too large', 73 | minFileSize: 'File is too small' 74 | } 75 | }, 76 | 77 | processActions: { 78 | 79 | validate: function (data, options) { 80 | if (options.disabled) { 81 | return data; 82 | } 83 | var dfd = $.Deferred(), 84 | settings = this.options, 85 | file = data.files[data.index], 86 | fileSize; 87 | if (options.minFileSize || options.maxFileSize) { 88 | fileSize = file.size; 89 | } 90 | if ($.type(options.maxNumberOfFiles) === 'number' && 91 | (settings.getNumberOfFiles() || 0) + data.files.length > 92 | options.maxNumberOfFiles) { 93 | file.error = settings.i18n('maxNumberOfFiles'); 94 | } else if (options.acceptFileTypes && 95 | !(options.acceptFileTypes.test(file.type) || 96 | options.acceptFileTypes.test(file.name))) { 97 | file.error = settings.i18n('acceptFileTypes'); 98 | } else if (fileSize > options.maxFileSize) { 99 | file.error = settings.i18n('maxFileSize'); 100 | } else if ($.type(fileSize) === 'number' && 101 | fileSize < options.minFileSize) { 102 | file.error = settings.i18n('minFileSize'); 103 | } else { 104 | delete file.error; 105 | } 106 | if (file.error || data.files.error) { 107 | data.files.error = true; 108 | dfd.rejectWith(this, [data]); 109 | } else { 110 | dfd.resolveWith(this, [data]); 111 | } 112 | return dfd.promise(); 113 | } 114 | 115 | } 116 | 117 | }); 118 | 119 | })); 120 | -------------------------------------------------------------------------------- /public/javascripts/uploader/jquery.fileupload-video.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery File Upload Video Preview Plugin 1.0.3 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2013, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /* jshint nomen:false */ 13 | /* global define, window, document */ 14 | 15 | (function (factory) { 16 | 'use strict'; 17 | if (typeof define === 'function' && define.amd) { 18 | // Register as an anonymous AMD module: 19 | define([ 20 | 'jquery', 21 | 'load-image', 22 | './jquery.fileupload-process' 23 | ], factory); 24 | } else { 25 | // Browser globals: 26 | factory( 27 | window.jQuery, 28 | window.loadImage 29 | ); 30 | } 31 | }(function ($, loadImage) { 32 | 'use strict'; 33 | 34 | // Prepend to the default processQueue: 35 | $.blueimp.fileupload.prototype.options.processQueue.unshift( 36 | { 37 | action: 'loadVideo', 38 | // Use the action as prefix for the "@" options: 39 | prefix: true, 40 | fileTypes: '@', 41 | maxFileSize: '@', 42 | disabled: '@disableVideoPreview' 43 | }, 44 | { 45 | action: 'setVideo', 46 | name: '@videoPreviewName', 47 | disabled: '@disableVideoPreview' 48 | } 49 | ); 50 | 51 | // The File Upload Video Preview plugin extends the fileupload widget 52 | // with video preview functionality: 53 | $.widget('blueimp.fileupload', $.blueimp.fileupload, { 54 | 55 | options: { 56 | // The regular expression for the types of video files to load, 57 | // matched against the file type: 58 | loadVideoFileTypes: /^video\/.*$/ 59 | }, 60 | 61 | _videoElement: document.createElement('video'), 62 | 63 | processActions: { 64 | 65 | // Loads the video file given via data.files and data.index 66 | // as video element if the browser supports playing it. 67 | // Accepts the options fileTypes (regular expression) 68 | // and maxFileSize (integer) to limit the files to load: 69 | loadVideo: function (data, options) { 70 | if (options.disabled) { 71 | return data; 72 | } 73 | var file = data.files[data.index], 74 | url, 75 | video; 76 | if (this._videoElement.canPlayType && 77 | this._videoElement.canPlayType(file.type) && 78 | ($.type(options.maxFileSize) !== 'number' || 79 | file.size <= options.maxFileSize) && 80 | (!options.fileTypes || 81 | options.fileTypes.test(file.type))) { 82 | url = loadImage.createObjectURL(file); 83 | if (url) { 84 | video = this._videoElement.cloneNode(false); 85 | video.src = url; 86 | video.controls = true; 87 | data.video = video; 88 | return data; 89 | } 90 | } 91 | return data; 92 | }, 93 | 94 | // Sets the video element as a property of the file object: 95 | setVideo: function (data, options) { 96 | if (data.video && !options.disabled) { 97 | data.files[data.index][options.name || 'preview'] = data.video; 98 | } 99 | return data; 100 | } 101 | 102 | } 103 | 104 | }); 105 | 106 | })); 107 | -------------------------------------------------------------------------------- /public/javascripts/uploader/jquery.iframe-transport.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Iframe Transport Plugin 1.8.2 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2011, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /* global define, window, document */ 13 | 14 | (function (factory) { 15 | 'use strict'; 16 | if (typeof define === 'function' && define.amd) { 17 | // Register as an anonymous AMD module: 18 | define(['jquery'], factory); 19 | } else { 20 | // Browser globals: 21 | factory(window.jQuery); 22 | } 23 | }(function ($) { 24 | 'use strict'; 25 | 26 | // Helper variable to create unique names for the transport iframes: 27 | var counter = 0; 28 | 29 | // The iframe transport accepts four additional options: 30 | // options.fileInput: a jQuery collection of file input fields 31 | // options.paramName: the parameter name for the file form data, 32 | // overrides the name property of the file input field(s), 33 | // can be a string or an array of strings. 34 | // options.formData: an array of objects with name and value properties, 35 | // equivalent to the return data of .serializeArray(), e.g.: 36 | // [{name: 'a', value: 1}, {name: 'b', value: 2}] 37 | // options.initialIframeSrc: the URL of the initial iframe src, 38 | // by default set to "javascript:false;" 39 | $.ajaxTransport('iframe', function (options) { 40 | if (options.async) { 41 | // javascript:false as initial iframe src 42 | // prevents warning popups on HTTPS in IE6: 43 | /*jshint scripturl: true */ 44 | var initialIframeSrc = options.initialIframeSrc || 'javascript:false;', 45 | /*jshint scripturl: false */ 46 | form, 47 | iframe, 48 | addParamChar; 49 | return { 50 | send: function (_, completeCallback) { 51 | form = $('
'); 52 | form.attr('accept-charset', options.formAcceptCharset); 53 | addParamChar = /\?/.test(options.url) ? '&' : '?'; 54 | // XDomainRequest only supports GET and POST: 55 | if (options.type === 'DELETE') { 56 | options.url = options.url + addParamChar + '_method=DELETE'; 57 | options.type = 'POST'; 58 | } else if (options.type === 'PUT') { 59 | options.url = options.url + addParamChar + '_method=PUT'; 60 | options.type = 'POST'; 61 | } else if (options.type === 'PATCH') { 62 | options.url = options.url + addParamChar + '_method=PATCH'; 63 | options.type = 'POST'; 64 | } 65 | // IE versions below IE8 cannot set the name property of 66 | // elements that have already been added to the DOM, 67 | // so we set the name along with the iframe HTML markup: 68 | counter += 1; 69 | iframe = $( 70 | '' 72 | ).bind('load', function () { 73 | var fileInputClones, 74 | paramNames = $.isArray(options.paramName) ? 75 | options.paramName : [options.paramName]; 76 | iframe 77 | .unbind('load') 78 | .bind('load', function () { 79 | var response; 80 | // Wrap in a try/catch block to catch exceptions thrown 81 | // when trying to access cross-domain iframe contents: 82 | try { 83 | response = iframe.contents(); 84 | // Google Chrome and Firefox do not throw an 85 | // exception when calling iframe.contents() on 86 | // cross-domain requests, so we unify the response: 87 | if (!response.length || !response[0].firstChild) { 88 | throw new Error(); 89 | } 90 | } catch (e) { 91 | response = undefined; 92 | } 93 | // The complete callback returns the 94 | // iframe content document as response object: 95 | completeCallback( 96 | 200, 97 | 'success', 98 | {'iframe': response} 99 | ); 100 | // Fix for IE endless progress bar activity bug 101 | // (happens on form submits to iframe targets): 102 | $('') 103 | .appendTo(form); 104 | window.setTimeout(function () { 105 | // Removing the form in a setTimeout call 106 | // allows Chrome's developer tools to display 107 | // the response result 108 | form.remove(); 109 | }, 0); 110 | }); 111 | form 112 | .prop('target', iframe.prop('name')) 113 | .prop('action', options.url) 114 | .prop('method', options.type); 115 | if (options.formData) { 116 | $.each(options.formData, function (index, field) { 117 | $('') 118 | .prop('name', field.name) 119 | .val(field.value) 120 | .appendTo(form); 121 | }); 122 | } 123 | if (options.fileInput && options.fileInput.length && 124 | options.type === 'POST') { 125 | fileInputClones = options.fileInput.clone(); 126 | // Insert a clone for each file input field: 127 | options.fileInput.after(function (index) { 128 | return fileInputClones[index]; 129 | }); 130 | if (options.paramName) { 131 | options.fileInput.each(function (index) { 132 | $(this).prop( 133 | 'name', 134 | paramNames[index] || options.paramName 135 | ); 136 | }); 137 | } 138 | // Appending the file input fields to the hidden form 139 | // removes them from their original location: 140 | form 141 | .append(options.fileInput) 142 | .prop('enctype', 'multipart/form-data') 143 | // enctype must be set as encoding for IE: 144 | .prop('encoding', 'multipart/form-data'); 145 | // Remove the HTML5 form attribute from the input(s): 146 | options.fileInput.removeAttr('form'); 147 | } 148 | form.submit(); 149 | // Insert the file input fields at their original location 150 | // by replacing the clones with the originals: 151 | if (fileInputClones && fileInputClones.length) { 152 | options.fileInput.each(function (index, input) { 153 | var clone = $(fileInputClones[index]); 154 | // Restore the original name and form properties: 155 | $(input) 156 | .prop('name', clone.prop('name')) 157 | .attr('form', clone.attr('form')); 158 | clone.replaceWith(input); 159 | }); 160 | } 161 | }); 162 | form.append(iframe).appendTo(document.body); 163 | }, 164 | abort: function () { 165 | if (iframe) { 166 | // javascript:false as iframe src aborts the request 167 | // and prevents warning popups on HTTPS in IE6. 168 | // concat is used to avoid the "Script URL" JSLint error: 169 | iframe 170 | .unbind('load') 171 | .prop('src', initialIframeSrc); 172 | } 173 | if (form) { 174 | form.remove(); 175 | } 176 | } 177 | }; 178 | } 179 | }); 180 | 181 | // The iframe transport returns the iframe content document as response. 182 | // The following adds converters from iframe to text, json, html, xml 183 | // and script. 184 | // Please note that the Content-Type for JSON responses has to be text/plain 185 | // or text/html, if the browser doesn't include application/json in the 186 | // Accept header, else IE will show a download dialog. 187 | // The Content-Type for XML responses on the other hand has to be always 188 | // application/xml or text/xml, so IE properly parses the XML response. 189 | // See also 190 | // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation 191 | $.ajaxSetup({ 192 | converters: { 193 | 'iframe text': function (iframe) { 194 | return iframe && $(iframe[0].body).text(); 195 | }, 196 | 'iframe json': function (iframe) { 197 | return iframe && $.parseJSON($(iframe[0].body).text()); 198 | }, 199 | 'iframe html': function (iframe) { 200 | return iframe && $(iframe[0].body).html(); 201 | }, 202 | 'iframe xml': function (iframe) { 203 | var xmlDoc = iframe && iframe[0]; 204 | return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : 205 | $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || 206 | $(xmlDoc.body).html()); 207 | }, 208 | 'iframe script': function (iframe) { 209 | return iframe && $.globalEval($(iframe[0].body).text()); 210 | } 211 | } 212 | }); 213 | 214 | })); 215 | -------------------------------------------------------------------------------- /public/javascripts/uploader/jquery.ui.widget.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Widget 1.10.4+amd 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/jQuery.widget/ 10 | */ 11 | 12 | (function (factory) { 13 | if (typeof define === "function" && define.amd) { 14 | // Register as an anonymous AMD module: 15 | define(["jquery"], factory); 16 | } else { 17 | // Browser globals: 18 | factory(jQuery); 19 | } 20 | }(function( $, undefined ) { 21 | 22 | var uuid = 0, 23 | slice = Array.prototype.slice, 24 | _cleanData = $.cleanData; 25 | $.cleanData = function( elems ) { 26 | for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { 27 | try { 28 | $( elem ).triggerHandler( "remove" ); 29 | // http://bugs.jquery.com/ticket/8235 30 | } catch( e ) {} 31 | } 32 | _cleanData( elems ); 33 | }; 34 | 35 | $.widget = function( name, base, prototype ) { 36 | var fullName, existingConstructor, constructor, basePrototype, 37 | // proxiedPrototype allows the provided prototype to remain unmodified 38 | // so that it can be used as a mixin for multiple widgets (#8876) 39 | proxiedPrototype = {}, 40 | namespace = name.split( "." )[ 0 ]; 41 | 42 | name = name.split( "." )[ 1 ]; 43 | fullName = namespace + "-" + name; 44 | 45 | if ( !prototype ) { 46 | prototype = base; 47 | base = $.Widget; 48 | } 49 | 50 | // create selector for plugin 51 | $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { 52 | return !!$.data( elem, fullName ); 53 | }; 54 | 55 | $[ namespace ] = $[ namespace ] || {}; 56 | existingConstructor = $[ namespace ][ name ]; 57 | constructor = $[ namespace ][ name ] = function( options, element ) { 58 | // allow instantiation without "new" keyword 59 | if ( !this._createWidget ) { 60 | return new constructor( options, element ); 61 | } 62 | 63 | // allow instantiation without initializing for simple inheritance 64 | // must use "new" keyword (the code above always passes args) 65 | if ( arguments.length ) { 66 | this._createWidget( options, element ); 67 | } 68 | }; 69 | // extend with the existing constructor to carry over any static properties 70 | $.extend( constructor, existingConstructor, { 71 | version: prototype.version, 72 | // copy the object used to create the prototype in case we need to 73 | // redefine the widget later 74 | _proto: $.extend( {}, prototype ), 75 | // track widgets that inherit from this widget in case this widget is 76 | // redefined after a widget inherits from it 77 | _childConstructors: [] 78 | }); 79 | 80 | basePrototype = new base(); 81 | // we need to make the options hash a property directly on the new instance 82 | // otherwise we'll modify the options hash on the prototype that we're 83 | // inheriting from 84 | basePrototype.options = $.widget.extend( {}, basePrototype.options ); 85 | $.each( prototype, function( prop, value ) { 86 | if ( !$.isFunction( value ) ) { 87 | proxiedPrototype[ prop ] = value; 88 | return; 89 | } 90 | proxiedPrototype[ prop ] = (function() { 91 | var _super = function() { 92 | return base.prototype[ prop ].apply( this, arguments ); 93 | }, 94 | _superApply = function( args ) { 95 | return base.prototype[ prop ].apply( this, args ); 96 | }; 97 | return function() { 98 | var __super = this._super, 99 | __superApply = this._superApply, 100 | returnValue; 101 | 102 | this._super = _super; 103 | this._superApply = _superApply; 104 | 105 | returnValue = value.apply( this, arguments ); 106 | 107 | this._super = __super; 108 | this._superApply = __superApply; 109 | 110 | return returnValue; 111 | }; 112 | })(); 113 | }); 114 | constructor.prototype = $.widget.extend( basePrototype, { 115 | // TODO: remove support for widgetEventPrefix 116 | // always use the name + a colon as the prefix, e.g., draggable:start 117 | // don't prefix for widgets that aren't DOM-based 118 | widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name 119 | }, proxiedPrototype, { 120 | constructor: constructor, 121 | namespace: namespace, 122 | widgetName: name, 123 | widgetFullName: fullName 124 | }); 125 | 126 | // If this widget is being redefined then we need to find all widgets that 127 | // are inheriting from it and redefine all of them so that they inherit from 128 | // the new version of this widget. We're essentially trying to replace one 129 | // level in the prototype chain. 130 | if ( existingConstructor ) { 131 | $.each( existingConstructor._childConstructors, function( i, child ) { 132 | var childPrototype = child.prototype; 133 | 134 | // redefine the child widget using the same prototype that was 135 | // originally used, but inherit from the new version of the base 136 | $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); 137 | }); 138 | // remove the list of existing child constructors from the old constructor 139 | // so the old child constructors can be garbage collected 140 | delete existingConstructor._childConstructors; 141 | } else { 142 | base._childConstructors.push( constructor ); 143 | } 144 | 145 | $.widget.bridge( name, constructor ); 146 | }; 147 | 148 | $.widget.extend = function( target ) { 149 | var input = slice.call( arguments, 1 ), 150 | inputIndex = 0, 151 | inputLength = input.length, 152 | key, 153 | value; 154 | for ( ; inputIndex < inputLength; inputIndex++ ) { 155 | for ( key in input[ inputIndex ] ) { 156 | value = input[ inputIndex ][ key ]; 157 | if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { 158 | // Clone objects 159 | if ( $.isPlainObject( value ) ) { 160 | target[ key ] = $.isPlainObject( target[ key ] ) ? 161 | $.widget.extend( {}, target[ key ], value ) : 162 | // Don't extend strings, arrays, etc. with objects 163 | $.widget.extend( {}, value ); 164 | // Copy everything else by reference 165 | } else { 166 | target[ key ] = value; 167 | } 168 | } 169 | } 170 | } 171 | return target; 172 | }; 173 | 174 | $.widget.bridge = function( name, object ) { 175 | var fullName = object.prototype.widgetFullName || name; 176 | $.fn[ name ] = function( options ) { 177 | var isMethodCall = typeof options === "string", 178 | args = slice.call( arguments, 1 ), 179 | returnValue = this; 180 | 181 | // allow multiple hashes to be passed on init 182 | options = !isMethodCall && args.length ? 183 | $.widget.extend.apply( null, [ options ].concat(args) ) : 184 | options; 185 | 186 | if ( isMethodCall ) { 187 | this.each(function() { 188 | var methodValue, 189 | instance = $.data( this, fullName ); 190 | if ( !instance ) { 191 | return $.error( "cannot call methods on " + name + " prior to initialization; " + 192 | "attempted to call method '" + options + "'" ); 193 | } 194 | if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { 195 | return $.error( "no such method '" + options + "' for " + name + " widget instance" ); 196 | } 197 | methodValue = instance[ options ].apply( instance, args ); 198 | if ( methodValue !== instance && methodValue !== undefined ) { 199 | returnValue = methodValue && methodValue.jquery ? 200 | returnValue.pushStack( methodValue.get() ) : 201 | methodValue; 202 | return false; 203 | } 204 | }); 205 | } else { 206 | this.each(function() { 207 | var instance = $.data( this, fullName ); 208 | if ( instance ) { 209 | instance.option( options || {} )._init(); 210 | } else { 211 | $.data( this, fullName, new object( options, this ) ); 212 | } 213 | }); 214 | } 215 | 216 | return returnValue; 217 | }; 218 | }; 219 | 220 | $.Widget = function( /* options, element */ ) {}; 221 | $.Widget._childConstructors = []; 222 | 223 | $.Widget.prototype = { 224 | widgetName: "widget", 225 | widgetEventPrefix: "", 226 | defaultElement: "
", 227 | options: { 228 | disabled: false, 229 | 230 | // callbacks 231 | create: null 232 | }, 233 | _createWidget: function( options, element ) { 234 | element = $( element || this.defaultElement || this )[ 0 ]; 235 | this.element = $( element ); 236 | this.uuid = uuid++; 237 | this.eventNamespace = "." + this.widgetName + this.uuid; 238 | this.options = $.widget.extend( {}, 239 | this.options, 240 | this._getCreateOptions(), 241 | options ); 242 | 243 | this.bindings = $(); 244 | this.hoverable = $(); 245 | this.focusable = $(); 246 | 247 | if ( element !== this ) { 248 | $.data( element, this.widgetFullName, this ); 249 | this._on( true, this.element, { 250 | remove: function( event ) { 251 | if ( event.target === element ) { 252 | this.destroy(); 253 | } 254 | } 255 | }); 256 | this.document = $( element.style ? 257 | // element within the document 258 | element.ownerDocument : 259 | // element is window or document 260 | element.document || element ); 261 | this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); 262 | } 263 | 264 | this._create(); 265 | this._trigger( "create", null, this._getCreateEventData() ); 266 | this._init(); 267 | }, 268 | _getCreateOptions: $.noop, 269 | _getCreateEventData: $.noop, 270 | _create: $.noop, 271 | _init: $.noop, 272 | 273 | destroy: function() { 274 | this._destroy(); 275 | // we can probably remove the unbind calls in 2.0 276 | // all event bindings should go through this._on() 277 | this.element 278 | .unbind( this.eventNamespace ) 279 | // 1.9 BC for #7810 280 | // TODO remove dual storage 281 | .removeData( this.widgetName ) 282 | .removeData( this.widgetFullName ) 283 | // support: jquery <1.6.3 284 | // http://bugs.jquery.com/ticket/9413 285 | .removeData( $.camelCase( this.widgetFullName ) ); 286 | this.widget() 287 | .unbind( this.eventNamespace ) 288 | .removeAttr( "aria-disabled" ) 289 | .removeClass( 290 | this.widgetFullName + "-disabled " + 291 | "ui-state-disabled" ); 292 | 293 | // clean up events and states 294 | this.bindings.unbind( this.eventNamespace ); 295 | this.hoverable.removeClass( "ui-state-hover" ); 296 | this.focusable.removeClass( "ui-state-focus" ); 297 | }, 298 | _destroy: $.noop, 299 | 300 | widget: function() { 301 | return this.element; 302 | }, 303 | 304 | option: function( key, value ) { 305 | var options = key, 306 | parts, 307 | curOption, 308 | i; 309 | 310 | if ( arguments.length === 0 ) { 311 | // don't return a reference to the internal hash 312 | return $.widget.extend( {}, this.options ); 313 | } 314 | 315 | if ( typeof key === "string" ) { 316 | // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } 317 | options = {}; 318 | parts = key.split( "." ); 319 | key = parts.shift(); 320 | if ( parts.length ) { 321 | curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); 322 | for ( i = 0; i < parts.length - 1; i++ ) { 323 | curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; 324 | curOption = curOption[ parts[ i ] ]; 325 | } 326 | key = parts.pop(); 327 | if ( arguments.length === 1 ) { 328 | return curOption[ key ] === undefined ? null : curOption[ key ]; 329 | } 330 | curOption[ key ] = value; 331 | } else { 332 | if ( arguments.length === 1 ) { 333 | return this.options[ key ] === undefined ? null : this.options[ key ]; 334 | } 335 | options[ key ] = value; 336 | } 337 | } 338 | 339 | this._setOptions( options ); 340 | 341 | return this; 342 | }, 343 | _setOptions: function( options ) { 344 | var key; 345 | 346 | for ( key in options ) { 347 | this._setOption( key, options[ key ] ); 348 | } 349 | 350 | return this; 351 | }, 352 | _setOption: function( key, value ) { 353 | this.options[ key ] = value; 354 | 355 | if ( key === "disabled" ) { 356 | this.widget() 357 | .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) 358 | .attr( "aria-disabled", value ); 359 | this.hoverable.removeClass( "ui-state-hover" ); 360 | this.focusable.removeClass( "ui-state-focus" ); 361 | } 362 | 363 | return this; 364 | }, 365 | 366 | enable: function() { 367 | return this._setOption( "disabled", false ); 368 | }, 369 | disable: function() { 370 | return this._setOption( "disabled", true ); 371 | }, 372 | 373 | _on: function( suppressDisabledCheck, element, handlers ) { 374 | var delegateElement, 375 | instance = this; 376 | 377 | // no suppressDisabledCheck flag, shuffle arguments 378 | if ( typeof suppressDisabledCheck !== "boolean" ) { 379 | handlers = element; 380 | element = suppressDisabledCheck; 381 | suppressDisabledCheck = false; 382 | } 383 | 384 | // no element argument, shuffle and use this.element 385 | if ( !handlers ) { 386 | handlers = element; 387 | element = this.element; 388 | delegateElement = this.widget(); 389 | } else { 390 | // accept selectors, DOM elements 391 | element = delegateElement = $( element ); 392 | this.bindings = this.bindings.add( element ); 393 | } 394 | 395 | $.each( handlers, function( event, handler ) { 396 | function handlerProxy() { 397 | // allow widgets to customize the disabled handling 398 | // - disabled as an array instead of boolean 399 | // - disabled class as method for disabling individual parts 400 | if ( !suppressDisabledCheck && 401 | ( instance.options.disabled === true || 402 | $( this ).hasClass( "ui-state-disabled" ) ) ) { 403 | return; 404 | } 405 | return ( typeof handler === "string" ? instance[ handler ] : handler ) 406 | .apply( instance, arguments ); 407 | } 408 | 409 | // copy the guid so direct unbinding works 410 | if ( typeof handler !== "string" ) { 411 | handlerProxy.guid = handler.guid = 412 | handler.guid || handlerProxy.guid || $.guid++; 413 | } 414 | 415 | var match = event.match( /^(\w+)\s*(.*)$/ ), 416 | eventName = match[1] + instance.eventNamespace, 417 | selector = match[2]; 418 | if ( selector ) { 419 | delegateElement.delegate( selector, eventName, handlerProxy ); 420 | } else { 421 | element.bind( eventName, handlerProxy ); 422 | } 423 | }); 424 | }, 425 | 426 | _off: function( element, eventName ) { 427 | eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; 428 | element.unbind( eventName ).undelegate( eventName ); 429 | }, 430 | 431 | _delay: function( handler, delay ) { 432 | function handlerProxy() { 433 | return ( typeof handler === "string" ? instance[ handler ] : handler ) 434 | .apply( instance, arguments ); 435 | } 436 | var instance = this; 437 | return setTimeout( handlerProxy, delay || 0 ); 438 | }, 439 | 440 | _hoverable: function( element ) { 441 | this.hoverable = this.hoverable.add( element ); 442 | this._on( element, { 443 | mouseenter: function( event ) { 444 | $( event.currentTarget ).addClass( "ui-state-hover" ); 445 | }, 446 | mouseleave: function( event ) { 447 | $( event.currentTarget ).removeClass( "ui-state-hover" ); 448 | } 449 | }); 450 | }, 451 | 452 | _focusable: function( element ) { 453 | this.focusable = this.focusable.add( element ); 454 | this._on( element, { 455 | focusin: function( event ) { 456 | $( event.currentTarget ).addClass( "ui-state-focus" ); 457 | }, 458 | focusout: function( event ) { 459 | $( event.currentTarget ).removeClass( "ui-state-focus" ); 460 | } 461 | }); 462 | }, 463 | 464 | _trigger: function( type, event, data ) { 465 | var prop, orig, 466 | callback = this.options[ type ]; 467 | 468 | data = data || {}; 469 | event = $.Event( event ); 470 | event.type = ( type === this.widgetEventPrefix ? 471 | type : 472 | this.widgetEventPrefix + type ).toLowerCase(); 473 | // the original event may come from any element 474 | // so we need to reset the target on the new event 475 | event.target = this.element[ 0 ]; 476 | 477 | // copy original event properties over to the new event 478 | orig = event.originalEvent; 479 | if ( orig ) { 480 | for ( prop in orig ) { 481 | if ( !( prop in event ) ) { 482 | event[ prop ] = orig[ prop ]; 483 | } 484 | } 485 | } 486 | 487 | this.element.trigger( event, data ); 488 | return !( $.isFunction( callback ) && 489 | callback.apply( this.element[0], [ event ].concat( data ) ) === false || 490 | event.isDefaultPrevented() ); 491 | } 492 | }; 493 | 494 | $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { 495 | $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { 496 | if ( typeof options === "string" ) { 497 | options = { effect: options }; 498 | } 499 | var hasOptions, 500 | effectName = !options ? 501 | method : 502 | options === true || typeof options === "number" ? 503 | defaultEffect : 504 | options.effect || defaultEffect; 505 | options = options || {}; 506 | if ( typeof options === "number" ) { 507 | options = { duration: options }; 508 | } 509 | hasOptions = !$.isEmptyObject( options ); 510 | options.complete = callback; 511 | if ( options.delay ) { 512 | element.delay( options.delay ); 513 | } 514 | if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { 515 | element[ method ]( options ); 516 | } else if ( effectName !== method && element[ effectName ] ) { 517 | element[ effectName ]( options.duration, options.easing, callback ); 518 | } else { 519 | element.queue(function( next ) { 520 | $( this )[ method ](); 521 | if ( callback ) { 522 | callback.call( element[ 0 ] ); 523 | } 524 | next(); 525 | }); 526 | } 527 | }; 528 | }); 529 | 530 | })); 531 | -------------------------------------------------------------------------------- /public/javascripts/uploader/load-image.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=function(a,c,d){var e,f,g=document.createElement("img");if(g.onerror=c,g.onload=function(){!f||d&&d.noRevoke||b.revokeObjectURL(f),c&&c(b.scale(g,d))},b.isInstanceOf("Blob",a)||b.isInstanceOf("File",a))e=f=b.createObjectURL(a),g._type=a.type;else{if("string"!=typeof a)return!1;e=a,d&&d.crossOrigin&&(g.crossOrigin=d.crossOrigin)}return e?(g.src=e,g):b.readFile(a,function(a){var b=a.target;b&&b.result?g.src=b.result:c&&c(a)})},c=window.createObjectURL&&window||window.URL&&URL.revokeObjectURL&&URL||window.webkitURL&&webkitURL;b.isInstanceOf=function(a,b){return Object.prototype.toString.call(b)==="[object "+a+"]"},b.transformCoordinates=function(){},b.getTransformedOptions=function(a){return a},b.renderImageToCanvas=function(a,b,c,d,e,f,g,h,i,j){return a.getContext("2d").drawImage(b,c,d,e,f,g,h,i,j),a},b.hasCanvasOption=function(a){return a.canvas||a.crop},b.scale=function(a,c){c=c||{};var d,e,f,g,h,i,j,k,l,m=document.createElement("canvas"),n=a.getContext||b.hasCanvasOption(c)&&m.getContext,o=a.naturalWidth||a.width,p=a.naturalHeight||a.height,q=o,r=p,s=function(){var a=Math.max((f||q)/q,(g||r)/r);a>1&&(q=Math.ceil(q*a),r=Math.ceil(r*a))},t=function(){var a=Math.min((d||q)/q,(e||r)/r);1>a&&(q=Math.ceil(q*a),r=Math.ceil(r*a))};return n&&(c=b.getTransformedOptions(c),j=c.left||0,k=c.top||0,c.sourceWidth?(h=c.sourceWidth,void 0!==c.right&&void 0===c.left&&(j=o-h-c.right)):h=o-j-(c.right||0),c.sourceHeight?(i=c.sourceHeight,void 0!==c.bottom&&void 0===c.top&&(k=p-i-c.bottom)):i=p-k-(c.bottom||0),q=h,r=i),d=c.maxWidth,e=c.maxHeight,f=c.minWidth,g=c.minHeight,n&&d&&e&&c.crop?(q=d,r=e,l=h/i-d/e,0>l?(i=e*h/d,void 0===c.top&&void 0===c.bottom&&(k=(p-i)/2)):l>0&&(h=d*i/e,void 0===c.left&&void 0===c.right&&(j=(o-h)/2))):((c.contain||c.cover)&&(f=d=d||f,g=e=e||g),c.cover?(t(),s()):(s(),t())),n?(m.width=q,m.height=r,b.transformCoordinates(m,c),b.renderImageToCanvas(m,a,j,k,h,i,0,0,q,r)):(a.width=q,a.height=r,a)},b.createObjectURL=function(a){return c?c.createObjectURL(a):!1},b.revokeObjectURL=function(a){return c?c.revokeObjectURL(a):!1},b.readFile=function(a,b,c){if(window.FileReader){var d=new FileReader;if(d.onload=d.onerror=b,c=c||"readAsDataURL",d[c])return d[c](a),d}return!1},"function"==typeof define&&define.amd?define(function(){return b}):a.loadImage=b}(this),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";if(window.navigator&&window.navigator.platform&&/iP(hone|od|ad)/.test(window.navigator.platform)){var b=a.renderImageToCanvas;a.detectSubsampling=function(a){var b,c;return a.width*a.height>1048576?(b=document.createElement("canvas"),b.width=b.height=1,c=b.getContext("2d"),c.drawImage(a,-a.width+1,0),0===c.getImageData(0,0,1,1).data[3]):!1},a.detectVerticalSquash=function(a,b){var c,d,e,f,g,h=a.naturalHeight||a.height,i=document.createElement("canvas"),j=i.getContext("2d");for(b&&(h/=2),i.width=1,i.height=h,j.drawImage(a,0,0),c=j.getImageData(0,0,1,h).data,d=0,e=h,f=h;f>d;)g=c[4*(f-1)+3],0===g?e=f:d=f,f=e+d>>1;return f/h||1},a.renderImageToCanvas=function(c,d,e,f,g,h,i,j,k,l){if("image/jpeg"===d._type){var m,n,o,p,q=c.getContext("2d"),r=document.createElement("canvas"),s=1024,t=r.getContext("2d");if(r.width=s,r.height=s,q.save(),m=a.detectSubsampling(d),m&&(e/=2,f/=2,g/=2,h/=2),n=a.detectVerticalSquash(d,m),m||1!==n){for(f*=n,k=Math.ceil(s*k/g),l=Math.ceil(s*l/h/n),j=0,p=0;h>p;){for(i=0,o=0;g>o;)t.clearRect(0,0,s,s),t.drawImage(d,e,f,g,h,-o,-p,g,h),q.drawImage(r,0,0,s,s,i,j,k,l),o+=s,i+=k;p+=s,j+=l}return q.restore(),c}}return b(c,d,e,f,g,h,i,j,k,l)}}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";var b=a.hasCanvasOption;a.hasCanvasOption=function(a){return b(a)||a.orientation},a.transformCoordinates=function(a,b){var c=a.getContext("2d"),d=a.width,e=a.height,f=b.orientation;if(f)switch(f>4&&(a.width=e,a.height=d),f){case 2:c.translate(d,0),c.scale(-1,1);break;case 3:c.translate(d,e),c.rotate(Math.PI);break;case 4:c.translate(0,e),c.scale(1,-1);break;case 5:c.rotate(.5*Math.PI),c.scale(1,-1);break;case 6:c.rotate(.5*Math.PI),c.translate(0,-e);break;case 7:c.rotate(.5*Math.PI),c.translate(d,-e),c.scale(-1,1);break;case 8:c.rotate(-.5*Math.PI),c.translate(-d,0)}},a.getTransformedOptions=function(a){if(!a.orientation||1===a.orientation)return a;var b,c={};for(b in a)a.hasOwnProperty(b)&&(c[b]=a[b]);switch(a.orientation){case 2:c.left=a.right,c.right=a.left;break;case 3:c.left=a.right,c.top=a.bottom,c.right=a.left,c.bottom=a.top;break;case 4:c.top=a.bottom,c.bottom=a.top;break;case 5:c.left=a.top,c.top=a.left,c.right=a.bottom,c.bottom=a.right;break;case 6:c.left=a.top,c.top=a.right,c.right=a.bottom,c.bottom=a.left;break;case 7:c.left=a.bottom,c.top=a.right,c.right=a.top,c.bottom=a.left;break;case 8:c.left=a.bottom,c.top=a.left,c.right=a.top,c.bottom=a.right}return a.orientation>4&&(c.maxWidth=a.maxHeight,c.maxHeight=a.maxWidth,c.minWidth=a.minHeight,c.minHeight=a.minWidth,c.sourceWidth=a.sourceHeight,c.sourceHeight=a.sourceWidth),c}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";var b=window.Blob&&(Blob.prototype.slice||Blob.prototype.webkitSlice||Blob.prototype.mozSlice);a.blobSlice=b&&function(){var a=this.slice||this.webkitSlice||this.mozSlice;return a.apply(this,arguments)},a.metaDataParsers={jpeg:{65505:[]}},a.parseMetaData=function(b,c,d){d=d||{};var e=this,f=d.maxMetaDataSize||262144,g={},h=!(window.DataView&&b&&b.size>=12&&"image/jpeg"===b.type&&a.blobSlice);(h||!a.readFile(a.blobSlice.call(b,0,f),function(b){if(b.target.error)return console.log(b.target.error),c(g),void 0;var f,h,i,j,k=b.target.result,l=new DataView(k),m=2,n=l.byteLength-4,o=m;if(65496===l.getUint16(0)){for(;n>m&&(f=l.getUint16(m),f>=65504&&65519>=f||65534===f);){if(h=l.getUint16(m+2)+2,m+h>l.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if(i=a.metaDataParsers.jpeg[f])for(j=0;j6&&(g.imageHead=k.slice?k.slice(0,o):new Uint8Array(k).subarray(0,o))}else console.log("Invalid JPEG file: Missing JPEG marker.");c(g)},"readAsArrayBuffer"))&&c(g)}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image","load-image-meta"],a):a(window.loadImage)}(function(a){"use strict";a.ExifMap=function(){return this},a.ExifMap.prototype.map={Orientation:274},a.ExifMap.prototype.get=function(a){return this[a]||this[this.map[a]]},a.getExifThumbnail=function(a,b,c){var d,e,f;if(!c||b+c>a.byteLength)return console.log("Invalid Exif data: Invalid thumbnail data."),void 0;for(d=[],e=0;c>e;e+=1)f=a.getUint8(b+e),d.push((16>f?"0":"")+f.toString(16));return"data:image/jpeg,%"+d.join("%")},a.exifTagTypes={1:{getValue:function(a,b){return a.getUint8(b)},size:1},2:{getValue:function(a,b){return String.fromCharCode(a.getUint8(b))},size:1,ascii:!0},3:{getValue:function(a,b,c){return a.getUint16(b,c)},size:2},4:{getValue:function(a,b,c){return a.getUint32(b,c)},size:4},5:{getValue:function(a,b,c){return a.getUint32(b,c)/a.getUint32(b+4,c)},size:8},9:{getValue:function(a,b,c){return a.getInt32(b,c)},size:4},10:{getValue:function(a,b,c){return a.getInt32(b,c)/a.getInt32(b+4,c)},size:8}},a.exifTagTypes[7]=a.exifTagTypes[1],a.getExifValue=function(b,c,d,e,f,g){var h,i,j,k,l,m,n=a.exifTagTypes[e];if(!n)return console.log("Invalid Exif data: Invalid tag type."),void 0;if(h=n.size*f,i=h>4?c+b.getUint32(d+8,g):d+8,i+h>b.byteLength)return console.log("Invalid Exif data: Invalid data offset."),void 0;if(1===f)return n.getValue(b,i,g);for(j=[],k=0;f>k;k+=1)j[k]=n.getValue(b,i+k*n.size,g);if(n.ascii){for(l="",k=0;ka.byteLength)return console.log("Invalid Exif data: Invalid directory offset."),void 0;if(f=a.getUint16(c,d),g=c+2+12*f,g+4>a.byteLength)return console.log("Invalid Exif data: Invalid directory size."),void 0;for(h=0;f>h;h+=1)this.parseExifTag(a,b,c+2+12*h,d,e);return a.getUint32(g,d)},a.parseExifData=function(b,c,d,e,f){if(!f.disableExif){var g,h,i,j=c+10;if(1165519206===b.getUint32(c+4)){if(j+8>b.byteLength)return console.log("Invalid Exif data: Invalid segment size."),void 0;if(0!==b.getUint16(c+8))return console.log("Invalid Exif data: Missing byte alignment offset."),void 0;switch(b.getUint16(j)){case 18761:g=!0;break;case 19789:g=!1;break;default:return console.log("Invalid Exif data: Invalid byte alignment marker."),void 0}if(42!==b.getUint16(j+2,g))return console.log("Invalid Exif data: Missing TIFF marker."),void 0;h=b.getUint32(j+4,g),e.exif=new a.ExifMap,h=a.parseExifTags(b,j,j+h,g,e),h&&!f.disableExifThumbnail&&(i={exif:{}},h=a.parseExifTags(b,j,j+h,g,i),i.exif[513]&&(e.exif.Thumbnail=a.getExifThumbnail(b,j+i.exif[513],i.exif[514]))),e.exif[34665]&&!f.disableExifSub&&a.parseExifTags(b,j,j+e.exif[34665],g,e),e.exif[34853]&&!f.disableExifGps&&a.parseExifTags(b,j,j+e.exif[34853],g,e)}}},a.metaDataParsers.jpeg[65505].push(a.parseExifData)}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image","load-image-exif"],a):a(window.loadImage)}(function(a){"use strict";a.ExifMap.prototype.tags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber",0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},a.ExifMap.prototype.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},a.ExifMap.prototype.getText=function(a){var b=this.get(a);switch(a){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[a][b];case"ExifVersion":case"FlashpixVersion":return String.fromCharCode(b[0],b[1],b[2],b[3]);case"ComponentsConfiguration":return this.stringValues[a][b[0]]+this.stringValues[a][b[1]]+this.stringValues[a][b[2]]+this.stringValues[a][b[3]];case"GPSVersionID":return b[0]+"."+b[1]+"."+b[2]+"."+b[3]}return String(b)},function(a){var b,c=a.tags,d=a.map;for(b in c)c.hasOwnProperty(b)&&(d[c[b]]=b)}(a.ExifMap.prototype),a.ExifMap.prototype.getAll=function(){var a,b,c={};for(a in this)this.hasOwnProperty(a)&&(b=this.tags[a],b&&(c[b]=this.getText(b)));return c}}); -------------------------------------------------------------------------------- /public/javascripts/uploader/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery File Upload Plugin JS Example 8.9.1 3 | * https://github.com/blueimp/jQuery-File-Upload 4 | * 5 | * Copyright 2010, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /* global $, window */ 13 | 14 | $(function () { 15 | 'use strict'; 16 | 17 | // Initialize the jQuery File Upload widget: 18 | $('#fileupload').fileupload({ 19 | // Uncomment the following to send cross-domain cookies: 20 | //xhrFields: {withCredentials: true}, 21 | url: '/upload' 22 | }); 23 | 24 | // Enable iframe cross-domain access via redirect option: 25 | $('#fileupload').fileupload( 26 | 'option', 27 | 'redirect', 28 | window.location.href.replace( 29 | /\/[^\/]*$/, 30 | '/cors/result.html?%s' 31 | ) 32 | ); 33 | 34 | if (window.location.hostname === 'blueimp.github.io') { 35 | // Demo settings: 36 | $('#fileupload').fileupload('option', { 37 | url: '//jquery-file-upload.appspot.com/', 38 | // Enable image resizing, except for Android and Opera, 39 | // which actually support image resizing, but fail to 40 | // send Blob objects via XHR requests: 41 | disableImageResize: /Android(?!.*Chrome)|Opera/ 42 | .test(window.navigator.userAgent), 43 | maxFileSize: 5000000, 44 | acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i 45 | }); 46 | // Upload server status check for browsers with CORS support: 47 | if ($.support.cors) { 48 | $.ajax({ 49 | url: '//jquery-file-upload.appspot.com/', 50 | type: 'HEAD' 51 | }).fail(function () { 52 | $('
') 53 | .text('Upload server currently unavailable - ' + 54 | new Date()) 55 | .appendTo('#fileupload'); 56 | }); 57 | } 58 | } else { 59 | // Load existing files: 60 | $('#fileupload').addClass('fileupload-processing'); 61 | $.ajax({ 62 | // Uncomment the following to send cross-domain cookies: 63 | //xhrFields: {withCredentials: true}, 64 | url: $('#fileupload').fileupload('option', 'url'), 65 | dataType: 'json', 66 | context: $('#fileupload')[0] 67 | }).always(function () { 68 | $(this).removeClass('fileupload-processing'); 69 | }).done(function (result) { 70 | $(this).fileupload('option', 'done') 71 | .call(this, $.Event('done'), {result: result}); 72 | }); 73 | } 74 | 75 | }); 76 | -------------------------------------------------------------------------------- /public/javascripts/uploader/tmpl.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=function(a,c){var d=/[^\w\-\.:]/.test(a)?new Function(b.arg+",tmpl","var _e=tmpl.encode"+b.helper+",_s='"+a.replace(b.regexp,b.func)+"';return _s;"):b.cache[a]=b.cache[a]||b(b.load(a));return c?d(c,b):function(a){return d(a,b)}};b.cache={},b.load=function(a){return document.getElementById(a).innerHTML},b.regexp=/([\s'\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,b.func=function(a,b,c,d,e,f){return b?{"\n":"\\n","\r":"\\r"," ":"\\t"," ":" "}[b]||"\\"+b:c?"="===c?"'+_e("+d+")+'":"'+("+d+"==null?'':"+d+")+'":e?"';":f?"_s+='":void 0},b.encReg=/[<>&"'\x00]/g,b.encMap={"<":"<",">":">","&":"&",'"':""","'":"'"},b.encode=function(a){return(null==a?"":""+a).replace(b.encReg,function(a){return b.encMap[a]||""})},b.arg="o",b.helper=",print=function(s,e){_s+=e?(s==null?'':s):_e(s);},include=function(s,d){_s+=tmpl(s,d);}","function"==typeof define&&define.amd?define(function(){return b}):a.tmpl=b}(this); -------------------------------------------------------------------------------- /public/stylesheets/blueimp-gallery.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{position:absolute;top:0;right:0;bottom:0;left:0;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.slide-content{margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;opacity:1}.blueimp-gallery{position:fixed;z-index:999999;overflow:hidden;background:#000;background:rgba(0,0,0,.9);opacity:0;display:none;direction:ltr;-ms-touch-action:none;touch-action:none}.blueimp-gallery-carousel{position:relative;z-index:auto;margin:1em auto;padding-bottom:56.25%;box-shadow:0 0 10px #000;-ms-touch-action:pan-y;touch-action:pan-y}.blueimp-gallery-display{display:block;opacity:1}.blueimp-gallery>.slides{position:relative;height:100%;overflow:hidden}.blueimp-gallery-carousel>.slides{position:absolute}.blueimp-gallery>.slides>.slide{position:relative;float:left;height:100%;text-align:center;-webkit-transition-timing-function:cubic-bezier(0.645,.045,.355,1);-moz-transition-timing-function:cubic-bezier(0.645,.045,.355,1);-ms-transition-timing-function:cubic-bezier(0.645,.045,.355,1);-o-transition-timing-function:cubic-bezier(0.645,.045,.355,1);transition-timing-function:cubic-bezier(0.645,.045,.355,1)}.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{-webkit-transition:opacity .5s linear;-moz-transition:opacity .5s linear;-ms-transition:opacity .5s linear;-o-transition:opacity .5s linear;transition:opacity .5s linear}.blueimp-gallery>.slides>.slide-loading{background:url(../images/loading.gif) center no-repeat;background-size:64px 64px}.blueimp-gallery>.slides>.slide-loading>.slide-content{opacity:0}.blueimp-gallery>.slides>.slide-error{background:url(../img/error.png) center no-repeat}.blueimp-gallery>.slides>.slide-error>.slide-content{display:none}.blueimp-gallery>.prev,.blueimp-gallery>.next{position:absolute;top:50%;left:15px;width:40px;height:40px;margin-top:-23px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-decoration:none;text-shadow:0 0 2px #000;text-align:center;background:#222;background:rgba(0,0,0,.5);-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;cursor:pointer;display:none}.blueimp-gallery>.next{left:auto;right:15px}.blueimp-gallery>.close,.blueimp-gallery>.title{position:absolute;top:15px;left:15px;margin:0 40px 0 0;font-size:20px;line-height:30px;color:#fff;text-shadow:0 0 2px #000;opacity:.8;display:none}.blueimp-gallery>.close{padding:15px;right:15px;left:auto;margin:-15px;font-size:30px;text-decoration:none;cursor:pointer}.blueimp-gallery>.play-pause{position:absolute;right:15px;bottom:15px;width:15px;height:15px;/*background:url(../img/play-pause.png) 0 0 no-repeat;*/cursor:pointer;opacity:.5;display:none}.blueimp-gallery-playing>.play-pause{background-position:-15px 0}.blueimp-gallery>.prev:hover,.blueimp-gallery>.next:hover,.blueimp-gallery>.close:hover,.blueimp-gallery>.title:hover,.blueimp-gallery>.play-pause:hover{color:#fff;opacity:1}.blueimp-gallery-controls>.prev,.blueimp-gallery-controls>.next,.blueimp-gallery-controls>.close,.blueimp-gallery-controls>.title,.blueimp-gallery-controls>.play-pause{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.prev,.blueimp-gallery-left>.prev,.blueimp-gallery-single>.next,.blueimp-gallery-right>.next,.blueimp-gallery-single>.play-pause{display:none}.blueimp-gallery>.slides>.slide>.slide-content,.blueimp-gallery>.prev,.blueimp-gallery>.next,.blueimp-gallery>.close,.blueimp-gallery>.play-pause{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body:last-child .blueimp-gallery>.slides>.slide-error{background-image:url(../img/error.svg)}body:last-child .blueimp-gallery>.play-pause{width:20px;height:20px;background-size:40px 20px;/*background-image:url(../img/play-pause.svg)*/}body:last-child .blueimp-gallery-playing>.play-pause{background-position:-20px 0}*+html .blueimp-gallery>.slides>.slide{min-height:300px}*+html .blueimp-gallery>.slides>.slide>.slide-content{position:relative}@charset "UTF-8";.blueimp-gallery>.indicator{position:absolute;top:auto;right:15px;bottom:15px;left:15px;margin:0 40px;padding:0;list-style:none;text-align:center;line-height:10px;display:none}.blueimp-gallery>.indicator>li{display:inline-block;width:9px;height:9px;margin:6px 3px 0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:1px solid transparent;background:#ccc;background:rgba(255,255,255,.25)center no-repeat;border-radius:5px;box-shadow:0 0 2px #000;opacity:.5;cursor:pointer}.blueimp-gallery>.indicator>li:hover,.blueimp-gallery>.indicator>.active{background-color:#fff;border-color:#fff;opacity:1}.blueimp-gallery-controls>.indicator{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.indicator{display:none}.blueimp-gallery>.indicator{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}*+html .blueimp-gallery>.indicator>li{display:inline}@charset "UTF-8";.blueimp-gallery>.slides>.slide>.video-content>img{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.video-content>video{position:absolute;top:0;left:0;width:100%;height:100%}.blueimp-gallery>.slides>.slide>.video-content>iframe{position:absolute;top:100%;left:0;width:100%;height:100%;border:none}.blueimp-gallery>.slides>.slide>.video-playing>iframe{top:0}.blueimp-gallery>.slides>.slide>.video-content>a{position:absolute;top:50%;right:0;left:0;margin:-64px auto 0;width:128px;height:128px;background:url(../img/video-play.png) center no-repeat;opacity:.8;cursor:pointer}.blueimp-gallery>.slides>.slide>.video-content>a:hover{opacity:1}.blueimp-gallery>.slides>.slide>.video-playing>a,.blueimp-gallery>.slides>.slide>.video-playing>img{display:none}.blueimp-gallery>.slides>.slide>.video-content>video{display:none}.blueimp-gallery>.slides>.slide>.video-playing>video{display:block}.blueimp-gallery>.slides>.slide>.video-loading>a{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}body:last-child .blueimp-gallery>.slides>.slide>.video-content:not(.video-loading)>a{background-image:url(../img/video-play.svg)}*+html .blueimp-gallery>.slides>.slide>.video-content{height:100%}*+html .blueimp-gallery>.slides>.slide>.video-content>a{left:50%;margin-left:-64px} -------------------------------------------------------------------------------- /public/stylesheets/jquery.fileupload-ui.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /* 3 | * jQuery File Upload UI Plugin CSS 9.0.0 4 | * https://github.com/blueimp/jQuery-File-Upload 5 | * 6 | * Copyright 2010, Sebastian Tschan 7 | * https://blueimp.net 8 | * 9 | * Licensed under the MIT license: 10 | * http://www.opensource.org/licenses/MIT 11 | */ 12 | 13 | .fileupload-buttonbar .btn, 14 | .fileupload-buttonbar .toggle { 15 | margin-bottom: 5px; 16 | } 17 | .progress-animated .progress-bar, 18 | .progress-animated .bar { 19 | background: url("../img/progressbar.gif") !important; 20 | filter: none; 21 | } 22 | .fileupload-process { 23 | float: right; 24 | display: none; 25 | } 26 | .fileupload-processing .fileupload-process, 27 | .files .processing .preview { 28 | display: block; 29 | width: 32px; 30 | height: 32px; 31 | background: url("../images/loading.gif") center no-repeat; 32 | background-size: contain; 33 | } 34 | .files audio, 35 | .files video { 36 | max-width: 300px; 37 | } 38 | 39 | @media (max-width: 767px) { 40 | .fileupload-buttonbar .toggle, 41 | .files .toggle, 42 | .files .btn span { 43 | display: none; 44 | } 45 | .files .name { 46 | width: 80px; 47 | word-wrap: break-word; 48 | } 49 | .files audio, 50 | .files video { 51 | max-width: 80px; 52 | } 53 | .files img, 54 | .files canvas { 55 | max-width: 100%; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/stylesheets/jquery.fileupload.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /* 3 | * jQuery File Upload Plugin CSS 1.3.0 4 | * https://github.com/blueimp/jQuery-File-Upload 5 | * 6 | * Copyright 2013, Sebastian Tschan 7 | * https://blueimp.net 8 | * 9 | * Licensed under the MIT license: 10 | * http://www.opensource.org/licenses/MIT 11 | */ 12 | 13 | .fileinput-button { 14 | position: relative; 15 | overflow: hidden; 16 | } 17 | .fileinput-button input { 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | margin: 0; 22 | opacity: 0; 23 | -ms-filter: 'alpha(opacity=0)'; 24 | font-size: 200px; 25 | direction: ltr; 26 | cursor: pointer; 27 | } 28 | 29 | /* Fixes for IE < 8 */ 30 | @media screen\9 { 31 | .fileinput-button input { 32 | filter: alpha(opacity=0); 33 | font-size: 100%; 34 | height: 100%; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | .preview img 10 | { 11 | width: 99px; 12 | } -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var uploadManager = require('./uploadManager')(router); 4 | 5 | /* GET home page. */ 6 | router.get('/', function(req, res) { 7 | res.render('index', { title: 'Express' }); 8 | }); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /routes/uploadManager.js: -------------------------------------------------------------------------------- 1 | // config the uploader 2 | var options = { 3 | tmpDir: __dirname + '/../public/uploaded/tmp', 4 | publicDir: __dirname + '/../public/uploaded', 5 | uploadDir: __dirname + '/../public/uploaded/files', 6 | uploadUrl: '/uploaded/files/', 7 | maxPostSize: 11000000000, // 11 GB 8 | minFileSize: 1, 9 | maxFileSize: 10000000000, // 10 GB 10 | acceptFileTypes: /.+/i, 11 | // Files not matched by this regular expression force a download dialog, 12 | // to prevent executing any scripts in the context of the service domain: 13 | inlineFileTypes: /\.(gif|jpe?g|png)$/i, 14 | imageTypes: /\.(gif|jpe?g|png)$/i, 15 | imageVersions: { 16 | maxWidth: 80, 17 | maxHeight: 80 18 | }, 19 | accessControl: { 20 | allowOrigin: '*', 21 | allowMethods: 'OPTIONS, HEAD, GET, POST, PUT, DELETE', 22 | allowHeaders: 'Content-Type, Content-Range, Content-Disposition' 23 | }, 24 | storage : { 25 | type : 'local' 26 | }, 27 | nodeStatic: { 28 | cache: 3600 // seconds to cache served files 29 | } 30 | }; 31 | 32 | var uploader = require('blueimp-file-upload-expressjs')(options); 33 | 34 | 35 | module.exports = function(router) { 36 | router.get('/upload', function(req, res) { 37 | uploader.get(req, res, function(err, obj) { 38 | res.send(JSON.stringify(obj)); 39 | }); 40 | 41 | }); 42 | 43 | router.post('/upload', function(req, res) { 44 | uploader.post(req, res, function(err, obj) { 45 | res.send(JSON.stringify(obj)); 46 | }); 47 | 48 | }); 49 | 50 | router.delete('/uploaded/files/:name', function(req, res) { 51 | uploader.delete(req, res, function(err, obj) { 52 | res.send(JSON.stringify(obj)); 53 | }); 54 | }); 55 | return router; 56 | } 57 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
30 |
31 |
32 | 33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 | 41 | Add files... 42 | 43 | 44 | 48 | 52 | 56 | 57 | 58 | 59 |
60 | 61 |
62 | 63 |
64 |
65 |
66 | 67 |
 
68 |
69 |
70 | 71 | 72 |
73 |
74 |
75 | 76 | 85 | 86 | 87 | 118 | 119 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 196 | 197 | 198 | --------------------------------------------------------------------------------