├── public ├── favicon.ico ├── images │ ├── favicon_32.png │ └── logo.svg ├── css │ ├── player.css │ ├── index.css │ ├── common.css │ └── dropzone.min.css ├── js │ ├── common.js │ ├── index.js │ ├── player.js │ ├── cash.min.js │ └── dropzone.min.js ├── 451.html ├── player.html └── index.html ├── Dockerfile ├── README.md ├── docker-compose.yaml ├── LICENSE └── nginx.conf /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/download13/ipfstube/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/images/favicon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/download13/ipfstube/HEAD/public/images/favicon_32.png -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.10.3-alpine 2 | 3 | RUN mkdir /etc/nginx/certs 4 | COPY nginx.conf /etc/nginx/nginx.conf 5 | COPY public/ /etc/nginx/public/ 6 | -------------------------------------------------------------------------------- /public/css/player.css: -------------------------------------------------------------------------------- 1 | #player__holder { 2 | text-align: center; 3 | } 4 | 5 | #player__holder > p { 6 | margin: 200px; 7 | } 8 | 9 | #player { 10 | width: 100%; 11 | max-width: 900px; 12 | } 13 | -------------------------------------------------------------------------------- /public/css/index.css: -------------------------------------------------------------------------------- 1 | #uploader { 2 | max-width: 600px; 3 | border: 2px dashed #0087F7; 4 | border-radius: 5px; 5 | background: white; 6 | padding: 50px; 7 | margin: 100px auto 100px auto; 8 | text-align: center; 9 | cursor: pointer; 10 | font-size: 24px; 11 | color: #888; 12 | } 13 | 14 | #sitedescription { 15 | text-align: center; 16 | font-size: 22px; 17 | } 18 | -------------------------------------------------------------------------------- /public/js/common.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // Goto bar 3 | var input = $('#hashnav__input'); 4 | var btn = $('#hashnav__btn'); 5 | 6 | input.on('keyup', function(e) { 7 | if(e.keyCode === 13) { 8 | navigateFilehash(); 9 | } 10 | }); 11 | btn.on('click', navigateFilehash); 12 | 13 | function navigateFilehash() { 14 | var hash = input.val(); 15 | if(hash) { 16 | location.assign('/v/' + hash); 17 | } 18 | } 19 | })(); 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipfstube 2 | 3 | Watch videos stored on IPFS 4 | 5 | Live at [ipfstube.erindachtler.me](http://ipfstube.erindachtler.me) 6 | 7 | [Demo video](https://ipfstube.erindachtler.me/v/QmU1GSqu4w29Pt7EEM57Lhte8Lce6e7kuhRHo6rSNb2UaC) 8 | 9 | 10 | ## Setup 11 | 12 | Run certbot to pull in letsencrypt certificates for your domain. 13 | 14 | Edit `nginx.conf` to use your domain and the path to your certs. 15 | 16 | Run `docker-compose build && docker-compose up -d` 17 | -------------------------------------------------------------------------------- /public/451.html: -------------------------------------------------------------------------------- 1 | 2 |
This request may not be serviced in the Roman Province 6 | of Judea due to the Lex Julia Majestatis, which disallows 7 | access to resources hosted on servers deemed to be 8 | operated by the People's Front of Judea.
9 |Back to Home
10 | 11 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | ipfsnode: 5 | image: ipfs/go-ipfs:v0.4.18 6 | restart: always 7 | volumes: 8 | - ~/volumes/ipfs:/data/ipfs 9 | ports: 10 | - "4001:4001" 11 | frontend: 12 | build: . 13 | restart: always 14 | volumes: 15 | - /etc/letsencrypt:/etc/letsencrypt:ro 16 | - /tmp/letsencrypt-auto:/tmp/letsencrypt-auto 17 | ports: 18 | - "80:80" 19 | - "443:443" 20 | -------------------------------------------------------------------------------- /public/js/index.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | Dropzone.options.uploader = false; 3 | 4 | var dropzone = new Dropzone('#uploader', { 5 | url: '/upload', 6 | uploadMultiple: false, 7 | parallelUploads: 1, 8 | dictDefaultMessage: 'Drop video here or click to upload', 9 | acceptedFiles: 'video/*' 10 | }); 11 | 12 | dropzone.on('error', function() { 13 | console.log('upload error', arguments); 14 | }); 15 | 16 | dropzone.on('success', function(file, response) { 17 | console.log('upload success', response); 18 | setTimeout(function() { 19 | location.assign('/v/' + response.Hash); 20 | }, 500); 21 | }); 22 | })(); 23 | -------------------------------------------------------------------------------- /public/css/common.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | 5 | font-family: Arial, sans-serif; 6 | } 7 | 8 | body { 9 | display: flex; 10 | min-height: 100vh; 11 | flex-direction: column; 12 | } 13 | 14 | code { 15 | background-color: rgba(150, 150, 150, 0.4); 16 | border-radius: 3px; 17 | padding: 3px; 18 | font-family: monospace; 19 | } 20 | 21 | 22 | #sitemain { 23 | flex: 1; 24 | } 25 | 26 | #sitefooter { 27 | font-size: 12px; 28 | background-color: #ddd; 29 | color: #888; 30 | text-align: center; 31 | padding: 10px; 32 | display: flex; 33 | justify-content: space-around; 34 | } 35 | 36 | 37 | #hashnav { 38 | display: flex; 39 | max-width: 600px; 40 | margin: 30px auto 100px auto; 41 | } 42 | 43 | #hashnav__input { 44 | flex: 1 1 auto; 45 | border-radius: 3px 0 0 3px; 46 | border: 2px solid #888; 47 | font-size: 19px; 48 | padding: 4px 6px; 49 | } 50 | 51 | #hashnav__btn { 52 | flex: 0 0 auto; 53 | border-radius: 0 3px 3px 0; 54 | background-color: #888; 55 | color: #efefef; 56 | border: 0; 57 | font-size: 22px; 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Erin Dachtler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /public/player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |IPFSTube is a player for videos stored in IPFS.
20 |You can upload videos here or on your own computer using ipfs add.
Unable to load video
')); 68 | } 69 | } 70 | 71 | function hashToPath(hash) { 72 | if( 73 | hash.indexOf('/ipfs/') === -1 && 74 | hash.indexOf('/ipns/') === -1 75 | ) { 76 | return '/ipfs/' + hash; 77 | } else { 78 | return hash; 79 | } 80 | } 81 | })(); 82 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | 5 | http { 6 | include mime.types; 7 | default_type application/octet-stream; 8 | 9 | access_log off; 10 | error_log /dev/stdout; 11 | error_page 451 /451.html; 12 | gzip on; 13 | etag on; 14 | 15 | # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; 16 | 17 | server { 18 | listen 80 default_server; 19 | 20 | location /.well-known/acme-challenge { 21 | default_type "text/plain"; 22 | root /tmp/letsencrypt-auto; 23 | try_files $uri =404; 24 | } 25 | 26 | location / { 27 | return 302 https://$host$request_uri; 28 | } 29 | } 30 | 31 | server { 32 | listen 443 ssl default_server; 33 | server_name ipfstube.erindachtler.me; 34 | 35 | ssl_certificate /etc/letsencrypt/live/ipfstube.erindachtler.me/fullchain.pem; 36 | ssl_certificate_key /etc/letsencrypt/live/ipfstube.erindachtler.me/privkey.pem; 37 | 38 | root public; 39 | index index.html; 40 | 41 | add_header Cache-Control "public"; 42 | 43 | location = /v/QmXKXJs9fZrSLUqyayPQ5dSXP8biQ11zUwwaj68XzNBjvw { 44 | return 451; 45 | } 46 | 47 | location = /v/QmTuHQ9jJY7uwE1nDESEyuoQWLgAnPS4z4ArNKs8YRwYUT { 48 | return 451; 49 | } 50 | 51 | location = /v/Qmcqf4e95CK9dNTrjJcQyiRnrALLfQ1dhXbZTHMrcyEY9z { 52 | return 451; 53 | } 54 | 55 | location = /v/QmNsXR7fcet16T69TgZyEmbTbpJxePbwCk77frxGNXZBTe { 56 | return 451; 57 | } 58 | 59 | location = /v/QmRnxAorwSUzmTCz13gkv9KtCKcijzRXQ7aqVyhMeBJYTQ { 60 | return 451; 61 | } 62 | 63 | location = /v/QmNpxtsHFscNqBWxA1i1aoUu53RDCj7PdZkzPK3H3xCBnR { 64 | return 451; 65 | } 66 | 67 | location = /v/QmfHjGWgvByZMpZQgYBuLxs4Kw2LdtEY9dV8Ag1zDMetXh { 68 | return 451; 69 | } 70 | 71 | location = /v/QmW1mX4PvKhrZfaCqRmaXUyYDSeu1WAQGgB9uzrzUukvoB { 72 | return 451; 73 | } 74 | 75 | location = /v/QmatBG7pqYP1kdDZ2tbKW3reZ9gqpdj7XzDqGpRWFxj7Ds { 76 | return 451; 77 | } 78 | 79 | location = /v/QmRpLbtJswudjTa2WA5UD5QjkVL6AqJMZgYMQKYR12VTvg { 80 | return 451; 81 | } 82 | 83 | location = /v/QmPqnLWzPTZ2bnRpghut1jf7iU3Vi1QzoszBkyZ9V5LJPy { 84 | return 451; 85 | } 86 | 87 | location = /v/QmRfLJq4bJHAz2LQjKRzZ4y7n6CN98FNPWPZvviXRN9ejw { 88 | return 451; 89 | } 90 | 91 | location /v/ { 92 | try_files /player.html =404; 93 | } 94 | 95 | location = /upload { 96 | proxy_set_header Origin ""; 97 | proxy_set_header Referer ""; 98 | proxy_set_header X-Requested-With ""; 99 | proxy_pass http://ipfsnode:5001/api/v0/add; 100 | client_max_body_size 200M; 101 | } 102 | 103 | location /ipfs/ { 104 | proxy_pass http://ipfsnode:8080/ipfs/; 105 | } 106 | 107 | location /ipns/ { 108 | proxy_pass http://ipfsnode:8080/ipns/; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /public/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 143 | -------------------------------------------------------------------------------- /public/css/dropzone.min.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-moz-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-webkit-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}}@-moz-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}}@keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);-o-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes pulse{0%,20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}}@-moz-keyframes pulse{0%,20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}}@keyframes pulse{0%,20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:2px solid rgba(0,0,0,.3);background:#fff;padding:20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:.5}.dropzone .dz-preview.dz-file-preview .dz-details,.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-message{text-align:center;margin:2em 0}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom,#eee,#ddd)}.dropzone .dz-preview.dz-image-preview{background:#fff}.dropzone .dz-preview.dz-image-preview .dz-details{-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-ms-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,.8);background-color:rgba(255,255,255,.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid transparent}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:rgba(255,255,255,.4);padding:0 .4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{-webkit-transform:scale(1.05,1.05);-moz-transform:scale(1.05,1.05);-ms-transform:scale(1.05,1.05);-o-transform:scale(1.05,1.05);transform:scale(1.05,1.05);-webkit-filter:blur(8px);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{-webkit-animation:passing-through 3s cubic-bezier(.77,0,.175,1);-moz-animation:passing-through 3s cubic-bezier(.77,0,.175,1);-ms-animation:passing-through 3s cubic-bezier(.77,0,.175,1);-o-animation:passing-through 3s cubic-bezier(.77,0,.175,1);animation:passing-through 3s cubic-bezier(.77,0,.175,1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;-webkit-animation:slide-in 3s cubic-bezier(.77,0,.175,1);-moz-animation:slide-in 3s cubic-bezier(.77,0,.175,1);-ms-animation:slide-in 3s cubic-bezier(.77,0,.175,1);-o-animation:slide-in 3s cubic-bezier(.77,0,.175,1);animation:slide-in 3s cubic-bezier(.77,0,.175,1)}.dropzone .dz-preview .dz-error-mark,.dropzone .dz-preview .dz-success-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px}.dropzone .dz-preview .dz-error-mark svg,.dropzone .dz-preview .dz-success-mark svg{display:block;width:54px;height:54px}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;-webkit-transition:all .2s linear;-moz-transition:all .2s linear;-ms-transition:all .2s linear;-o-transition:all .2s linear;transition:all .2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;-webkit-transition:opacity .4s ease-in;-moz-transition:opacity .4s ease-in;-ms-transition:opacity .4s ease-in;-o-transition:opacity .4s ease-in;transition:opacity .4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{-webkit-animation:pulse 6s ease infinite;-moz-animation:pulse 6s ease infinite;-ms-animation:pulse 6s ease infinite;-o-animation:pulse 6s ease infinite;animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:16px;left:50%;top:50%;margin-top:-8px;width:80px;margin-left:-40px;background:rgba(255,255,255,.9);-webkit-transform:scale(1);border-radius:8px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#333;background:linear-gradient(to bottom,#666,#444);position:absolute;top:0;left:0;bottom:0;width:0;-webkit-transition:width .3s ease-in-out;-moz-transition:width .3s ease-in-out;-ms-transition:width .3s ease-in-out;-o-transition:width .3s ease-in-out;transition:width .3s ease-in-out}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;-webkit-transition:opacity .3s ease;-moz-transition:opacity .3s ease;-ms-transition:opacity .3s ease;-o-transition:opacity .3s ease;transition:opacity .3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#be2626;background:linear-gradient(to bottom,#be2626,#a92222);padding:.5em 1.2em;color:#fff}.dropzone .dz-preview .dz-error-message:after{content:'';position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #be2626} 2 | -------------------------------------------------------------------------------- /public/js/cash.min.js: -------------------------------------------------------------------------------- 1 | "use strict";/*! cash-dom 1.2.0, https://github.com/kenwheeler/cash @license MIT */ 2 | !function(t,n){"function"==typeof define&&define.amd?define(n):"undefined"!=typeof exports?module.exports=n():t.cash=t.$=n()}(this,function(){function t(t,n){n=n||A;var e=$.test(t)?n.getElementsByClassName(t.slice(1)):D.test(t)?n.getElementsByTagName(t):n.querySelectorAll(t);return e}function n(t){return b=b||A.createDocumentFragment(),E=E||b.appendChild(A.createElement("div")),E.innerHTML=t,E.childNodes}function e(t){"loading"!==A.readyState?t():A.addEventListener("DOMContentLoaded",t)}function r(r,i){if(!r)return this;if(r.cash&&r!==w)return r;var o,u=r,s=0;if(R(r))u=q.test(r)?A.getElementById(r.slice(1)):k.test(r)?n(r):t(r,i);else if(O(r))return e(r),this;if(!u)return this;if(u.nodeType||u===w)this[0]=u,this.length=1;else for(o=this.length=u.length;o>s;s++)this[s]=u[s];return this}function i(t,n){return new r(t,n)}function o(t,n){for(var e=t.length,r=0;e>r&&n.call(t[r],t[r],r,t)!==!1;r++);}function u(t,n){return(t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.msMatchesSelector||t.oMatchesSelector).call(t,n)}function s(t){return i(S.call(t).filter(function(t,n,e){return e.indexOf(t)===n}))}function c(t){return t[I]=t[I]||{}}function a(t,n,e){return c(t)[n]=e}function f(t,n){var e=c(t);return void 0===e[n]&&(e[n]=t.dataset?t.dataset[n]:i(t).attr("data-"+n)),e[n]}function h(t,n){var e=c(t);e?delete e[n]:t.dataset?delete t.dataset[n]:i(t).removeAttr("data-"+name)}function l(t,n){return t.classList?t.classList.contains(n):new RegExp("(^| )"+n+"( |$)","gi").test(t.className)}function d(t,n,e){t.classList?t.classList.add(n):e.indexOf(" "+n+" ")&&(t.className+=" "+n)}function p(t,n){t.classList?t.classList.remove(n):t.className=t.className.replace(n,"")}function v(t,n){return parseInt(w.getComputedStyle(t[0],null)[n],10)||0}function m(t,n,e){var r=f(t,"_cashEvents")||a(t,"_cashEvents",{});r[n]=r[n]||[],r[n].push(e),t.addEventListener(n,e)}function g(t,n,e){var r=f(t,"_cashEvents")[n];e?t.removeEventListener(n,e):(o(r,function(e){t.removeEventListener(n,e)}),r=[])}function y(t,n){return"&"+encodeURIComponent(t)+"="+encodeURIComponent(n).replace(/%20/g,"+")}function x(t){return"radio"===t.type||"checkbox"===t.type}function C(t,n,e){if(e){var r=t.childNodes[0];t.insertBefore(n,r)}else t.appendChild(n)}function L(t,n,e){var r=R(n);return!r&&n.length?void o(n,function(n){return L(t,n,e)}):void o(t,r?function(t){return t.insertAdjacentHTML(e?"afterbegin":"beforeend",n)}:function(t,r){return C(t,0===r?n:n.cloneNode(!0),e)})}function N(t,n){return t===n}var b,E,A=document,w=window,T=Array.prototype,S=T.slice,M=T.filter,B=T.push,H=function(){},O=function(t){return typeof t==typeof H},R=function(t){return"string"==typeof t},q=/^#[\w-]*$/,$=/^\.[\w-]*$/,k=/<.+>/,D=/^\w+$/,F=i.fn=i.prototype=r.prototype={constructor:i,cash:!0,length:0,push:B,splice:T.splice,map:T.map,init:r};i.parseHTML=n,i.noop=H,i.isFunction=O,i.isString=R,i.extend=F.extend=function(t){t=t||{};var n=S.call(arguments),e=n.length,r=1;for(1===n.length&&(t=this,r=0);e>r;r++)if(n[r])for(var i in n[r])n[r].hasOwnProperty(i)&&(t[i]=n[r][i]);return t},i.extend({merge:function(t,n){for(var e=+n.length,r=t.length,i=0;e>i;r++,i++)t[r]=n[i];return t.length=r,t},each:o,matches:u,unique:s,isArray:Array.isArray,isNumeric:function(t){return!isNaN(parseFloat(t))&&isFinite(t)}});var I=i.uid="_cash"+Date.now();F.extend({data:function(t,n){return n?this.each(function(e){return a(e,t,n)}):f(this[0],t)},removeData:function(t){return this.each(function(n){return h(n,t)})}});var P=/\S+/g;F.extend({addClass:function(t){var n=t.match(P);return this.each(function(t){var e=" "+t.className+" ";o(n,function(n){d(t,n,e)})})},attr:function(t,n){if(R(t))return void 0===n?this[0].getAttribute?this[0].getAttribute(t):this[0][t]:this.each(function(e){e.setAttribute?e.setAttribute(t,n):e[t]=n});for(var e in t)this.attr(e,t[e]);return this},hasClass:function(t){var n=!1;return this.each(function(e){return n=l(e,t),!n}),n},prop:function(t,n){if(R(t))return void 0===n?this[0][t]:this.each(function(e){e[t]=n});for(var e in t)this.prop(e,t[e]);return this},removeAttr:function(t){return this.each(function(n){n.removeAttribute?n.removeAttribute(t):delete n[t]})},removeClass:function(t){var n=t.match(P);return this.each(function(t){o(n,function(n){p(t,n)})})},removeProp:function(t){return this.each(function(n){delete n[t]})},toggleClass:function(t,n){if(void 0!==n)return this[n?"addClass":"removeClass"](t);var e=t.match(P);return this.each(function(t){var n=" "+t.className+" ";o(e,function(e){l(t,e)?p(t,e):d(t,e,n)})})}}),F.extend({add:function(t,n){return s(i.merge(this,i(t,n)))},each:function(t){return o(this,t),this},eq:function(t){return i(this.get(t))},filter:function(t){return M.call(this,R(t)?function(n){return u(n,t)}:t)},first:function(){return this.eq(0)},get:function(t){return void 0===t?S.call(this):0>t?this[t+this.length]:this[t]},index:function(t){var n=this[0];return S.call(t?i(t):i(n).parent().children()).indexOf(n)},last:function(){return this.eq(-1)}});var U=function(){function t(t){return t.replace(i,function(t,n){return t[0===n?"toLowerCase":"toUpperCase"]()}).replace(u,"")}var n={},e=A.createElement("div"),r=e.style,i=/(?:^\w|[A-Z]|\b\w)/g,u=/\s+/g;return function(e){if(e=t(e),n[e])return n[e];var i=e.charAt(0).toUpperCase()+e.slice(1),u=["webkit","moz","ms","o"],s=(e+" "+u.join(i+" ")+i).split(" ");return o(s,function(t){return t in r?(n[t]=e=n[e]=t,!1):void 0}),n[e]}}();F.extend({css:function(t,n){if(R(t))return t=U(t),n?this.each(function(e){return e.style[t]=n}):w.getComputedStyle(this[0])[t];for(var e in t)this.css(e,t[e]);return this}}),o(["Width","Height"],function(t){var n=t.toLowerCase();F[n]=function(){return this[0].getBoundingClientRect()[n]},F["inner"+t]=function(){return this[0]["client"+t]},F["outer"+t]=function(n){return this[0]["offset"+t]+(n?v(this,"margin"+("Width"===t?"Left":"Top"))+v(this,"margin"+("Width"===t?"Right":"Bottom")):0)}}),F.extend({off:function(t,n){return this.each(function(e){return g(e,t,n)})},on:function(t,n,r,i){var o;if(!R(t)){for(var s in t)this.on(s,n,t[s]);return this}return O(n)&&(r=n,n=null),"ready"===t?(e(r),this):(n&&(o=r,r=function(t){var e=t.target;if(u(e,n))o.call(e);else{for(;!u(e,n);){if(e===this)return e=!1;e=e.parentNode}e&&o.call(e)}}),this.each(function(n){var e=r;i&&(e=function(){r.apply(this,arguments),g(n,t,e)}),m(n,t,e)}))},one:function(t,n,e){return this.on(t,n,e,!0)},ready:e,trigger:function(t){var n=A.createEvent("HTMLEvents");return n.initEvent(t,!0,!1),this.each(function(t){return t.dispatchEvent(n)})}});var _=["file","reset","submit","button"];F.extend({serialize:function(){var t=this[0].elements,n="";return o(t,function(t){t.name&&_.indexOf(t.type)<0&&("select-multiple"===t.type?o(t.options,function(e){e.selected&&(n+=y(t.name,e.value))}):(!x(t)||x(t)&&t.checked)&&(n+=y(t.name,t.value)))}),n.substr(1)},val:function(t){return void 0===t?this[0].value:this.each(function(n){return n.value=t})}}),F.extend({after:function(t){return i(t).insertAfter(this),this},append:function(t){return L(this,t),this},appendTo:function(t){return L(i(t),this),this},before:function(t){return i(t).insertBefore(this),this},clone:function(){return i(this.map(function(t){return t.cloneNode(!0)}))},empty:function(){return this.html(""),this},html:function(t){if(void 0===t)return this[0].innerHTML;var n=t.nodeType?t[0].outerHTML:t;return this.each(function(t){return t.innerHTML=n})},insertAfter:function(t){var n=this;return i(t).each(function(t,e){var r=t.parentNode,i=t.nextSibling;n.each(function(t){r.insertBefore(0===e?t:t.cloneNode(!0),i)})}),this},insertBefore:function(t){var n=this;return i(t).each(function(t,e){var r=t.parentNode;n.each(function(n){r.insertBefore(0===e?n:n.cloneNode(!0),t)})}),this},prepend:function(t){return L(this,t,!0),this},prependTo:function(t){return L(i(t),this,!0),this},remove:function(){return this.each(function(t){return t.parentNode.removeChild(t)})},text:function(t){return t?this.each(function(n){return n.textContent=t}):this[0].textContent}});var z=A.documentElement;return F.extend({position:function(){var t=this[0];return{left:t.offsetLeft,top:t.offsetTop}},offset:function(){var t=this[0].getBoundingClientRect();return{top:t.top+w.pageYOffset-z.clientTop,left:t.left+w.pageXOffset-z.clientLeft}},offsetParent:function(){return i(this[0].offsetParent)}}),F.extend({children:function(t){var n=[];return this.each(function(t){B.apply(n,t.children)}),n=s(n),t?n.filter(function(n){return u(n,t)}):n},closest:function(t){return!t||u(this[0],t)?this:this.parent().closest(t)},is:function(t){if(!t)return!1;var n=!1,e=R(t)?u:t.cash?function(n){return t.is(n)}:N;return this.each(function(r,i){return n=e(r,t,i),!n}),n},find:function(n){if(!n)return i();var e=[];return this.each(function(r){B.apply(e,t(n,r))}),s(e)},has:function(t){return M.call(this,function(n){return 0!==i(n).find(t).length})},next:function(){return i(this[0].nextElementSibling)},not:function(t){return M.call(this,function(n){return!u(n,t)})},parent:function(){var t=this.map(function(t){return t.parentElement||A.body.parentNode});return s(t)},parents:function(t){var n,e=[];return this.each(function(r){for(n=r;n!==A.body.parentNode;)n=n.parentElement,(!t||t&&u(n,t))&&e.push(n)}),s(e)},prev:function(){return i(this[0].previousElementSibling)},siblings:function(){var t=this.parent().children(),n=this[0];return M.call(t,function(t){return t!==n})}}),i}); -------------------------------------------------------------------------------- /public/js/dropzone.min.js: -------------------------------------------------------------------------------- 1 | (function(){var e,t,i,n,r,o,s,l,a=[].slice,u={}.hasOwnProperty,p=function(e,t){function i(){this.constructor=e}for(var n in t)u.call(t,n)&&(e[n]=t[n]);return i.prototype=t.prototype,e.prototype=new i,e.__super__=t.prototype,e};s=function(){},t=function(){function e(){}return e.prototype.addEventListener=e.prototype.on,e.prototype.on=function(e,t){return this._callbacks=this._callbacks||{},this._callbacks[e]||(this._callbacks[e]=[]),this._callbacks[e].push(t),this},e.prototype.emit=function(){var e,t,i,n,r,o;if(n=arguments[0],e=2>arguments.length?[]:a.call(arguments,1),this._callbacks=this._callbacks||{},i=this._callbacks[n])for(r=0,o=i.length;o>r;r++)t=i[r],t.apply(this,e);return this},e.prototype.removeListener=e.prototype.off,e.prototype.removeAllListeners=e.prototype.off,e.prototype.removeEventListener=e.prototype.off,e.prototype.off=function(e,t){var i,n,r,o,s;if(!this._callbacks||0===arguments.length)return this._callbacks={},this;if(n=this._callbacks[e],!n)return this;if(1===arguments.length)return delete this._callbacks[e],this;for(r=o=0,s=n.length;s>o;r=++o)if(i=n[r],i===t){n.splice(r,1);break}return this},e}(),e=function(e){function i(e,t){var r,o,s;if(this.element=e,this.version=i.version,this.defaultOptions.previewTemplate=this.defaultOptions.previewTemplate.replace(/\n*/g,""),this.clickableElements=[],this.listeners=[],this.files=[],"string"==typeof this.element&&(this.element=document.querySelector(this.element)),!this.element||null==this.element.nodeType)throw Error("Invalid dropzone element.");if(this.element.dropzone)throw Error("Dropzone already attached.");if(i.instances.push(this),this.element.dropzone=this,r=null!=(s=i.optionsForElement(this.element))?s:{},this.options=n({},this.defaultOptions,r,null!=t?t:{}),this.options.forceFallback||!i.isBrowserSupported())return this.options.fallback.call(this);if(null==this.options.url&&(this.options.url=this.element.getAttribute("action")),!this.options.url)throw Error("No URL provided.");if(this.options.acceptedFiles&&this.options.acceptedMimeTypes)throw Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");this.options.acceptedMimeTypes&&(this.options.acceptedFiles=this.options.acceptedMimeTypes,delete this.options.acceptedMimeTypes),this.options.method=this.options.method.toUpperCase(),(o=this.getExistingFallback())&&o.parentNode&&o.parentNode.removeChild(o),this.options.previewsContainer!==!1&&(this.previewsContainer=this.options.previewsContainer?i.getElement(this.options.previewsContainer,"previewsContainer"):this.element),this.options.clickable&&(this.clickableElements=this.options.clickable===!0?[this.element]:i.getElements(this.options.clickable,"clickable")),this.init()}var n,r;return p(i,e),i.prototype.Emitter=t,i.prototype.events=["drop","dragstart","dragend","dragenter","dragover","dragleave","addedfile","addedfiles","removedfile","thumbnail","error","errormultiple","processing","processingmultiple","uploadprogress","totaluploadprogress","sending","sendingmultiple","success","successmultiple","canceled","canceledmultiple","complete","completemultiple","reset","maxfilesexceeded","maxfilesreached","queuecomplete"],i.prototype.defaultOptions={url:null,method:"post",withCredentials:!1,parallelUploads:2,uploadMultiple:!1,maxFilesize:256,paramName:"file",createImageThumbnails:!0,maxThumbnailFilesize:10,thumbnailWidth:120,thumbnailHeight:120,filesizeBase:1e3,maxFiles:null,params:{},clickable:!0,ignoreHiddenFiles:!0,acceptedFiles:null,acceptedMimeTypes:null,autoProcessQueue:!0,autoQueue:!0,addRemoveLinks:!1,previewsContainer:null,hiddenInputContainer:"body",capture:null,renameFilename:null,dictDefaultMessage:"Drop files here to upload",dictFallbackMessage:"Your browser does not support drag'n'drop file uploads.",dictFallbackText:"Please use the fallback form below to upload your files like in the olden days.",dictFileTooBig:"File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",dictInvalidFileType:"You can't upload files of this type.",dictResponseError:"Server responded with {{statusCode}} code.",dictCancelUpload:"Cancel upload",dictCancelUploadConfirmation:"Are you sure you want to cancel this upload?",dictRemoveFile:"Remove file",dictRemoveFileConfirmation:null,dictMaxFilesExceeded:"You can not upload any more files.",accept:function(e,t){return t()},init:function(){return s},forceFallback:!1,fallback:function(){var e,t,n,r,o,s;for(this.element.className=""+this.element.className+" dz-browser-not-supported",s=this.element.getElementsByTagName("div"),r=0,o=s.length;o>r;r++)e=s[r],/(^| )dz-message($| )/.test(e.className)&&(t=e,e.className="dz-message");return t||(t=i.createElement(''),this.element.appendChild(t)),n=t.getElementsByTagName("span")[0],n&&(null!=n.textContent?n.textContent=this.options.dictFallbackMessage:null!=n.innerText&&(n.innerText=this.options.dictFallbackMessage)),this.element.appendChild(this.getFallbackForm())},resize:function(e){var t,i,n;return t={srcX:0,srcY:0,srcWidth:e.width,srcHeight:e.height},i=e.width/e.height,t.optWidth=this.options.thumbnailWidth,t.optHeight=this.options.thumbnailHeight,null==t.optWidth&&null==t.optHeight?(t.optWidth=t.srcWidth,t.optHeight=t.srcHeight):null==t.optWidth?t.optWidth=i*t.optHeight:null==t.optHeight&&(t.optHeight=1/i*t.optWidth),n=t.optWidth/t.optHeight,e.height"+this.options.dictFallbackText+"
"),n+='