From 33926400d591bfb4431dff98949e2926f20cefe4 Mon Sep 17 00:00:00 2001 From: Rickard Lundqvist Date: Thu, 3 Feb 2022 18:27:43 +0100 Subject: [PATCH] + upload page WIP v4 --- .idea/dataSources.xml | 9 +- config.py | 11 +- skanhama/__init__.py | 7 +- skanhama/forms.py | 12 +- skanhama/models.py | 21 +- skanhama/routes.py | 20 +- skanhama/site.db | Bin 49152 -> 0 bytes skanhama/static/dropzone-min.js | 1795 +++++++++++++++++++++++++++++++ skanhama/static/dropzone.css | 265 +++++ skanhama/static/scripts.js | 201 ++++ skanhama/static/style.css | 175 ++- skanhama/templates/layout.html | 11 +- skanhama/templates/upload.html | 127 ++- 13 files changed, 2575 insertions(+), 79 deletions(-) create mode 100644 skanhama/static/dropzone-min.js create mode 100644 skanhama/static/dropzone.css diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 0a86376..744012a 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,12 +1,17 @@ - + sqlite.xerial true org.sqlite.JDBC - jdbc:sqlite:C:\Users\lunde\Desktop\skyflask\skanhama\site.db + jdbc:sqlite:C:\Users\lunde\Desktop\skyflask\site.db $ProjectFileDir$ + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.34.0/sqlite-jdbc-3.34.0.jar + + \ No newline at end of file diff --git a/config.py b/config.py index 54adb80..36951c7 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,11 @@ +import os +basedir = os.path.abspath(os.path.dirname(__file__)) + + class Config(object): - SECRET_KEY = "cloaked" - SQLALCHEMY_DATABASE_URI = "sqlite:///site.db" + SECRET_KEY = os.environ.get("SECRET_KEY") or "cloaked" + SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL") or "sqlite:///" + os.path.join(basedir, "site.db") SQLALCHEMY_TRACK_MODIFICATIONS = False + MAX_CONTENT_LENGTH = 50 * 1024 * 1024 + # MAX_DOWNLOAD_SIZE = 50 * 1024 * 1024 + # MAX_UPLOAD_SIZE = 40 * 1024 * 1024 diff --git a/skanhama/__init__.py b/skanhama/__init__.py index 9904fd4..142f77c 100644 --- a/skanhama/__init__.py +++ b/skanhama/__init__.py @@ -1,12 +1,13 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate from flask_login import LoginManager +from config import Config app = Flask(__name__) -app.config["SECRET_KEY"] = "cloaked" -app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///site.db" -app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False +app.config.from_object(Config) db = SQLAlchemy(app) +migrate = Migrate(app, db) login_manager = LoginManager(app) login_manager.login_view = "login" login_manager.login_message_category = "flash flash-fail" diff --git a/skanhama/forms.py b/skanhama/forms.py index 8dcd10b..fa4057b 100644 --- a/skanhama/forms.py +++ b/skanhama/forms.py @@ -1,7 +1,7 @@ from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileAllowed, FileSize from flask_login import current_user -from wtforms import StringField, PasswordField, SubmitField, BooleanField, TextAreaField, SelectField +from wtforms import StringField, PasswordField, SubmitField, BooleanField, TextAreaField, SelectField, MultipleFileField from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError from skanhama.models import User @@ -21,7 +21,7 @@ def validate_username(self, username): def validate_email(self, email): user = User.query.filter_by(email=email.data).first() if user: - raise ValidationError("Unable to create account. Please ensure you have submitted correct information.") + raise ValidationError("An account with that email address already exists.") class LoginForm(FlaskForm): @@ -56,6 +56,7 @@ def validate_email(self, email): class UploadPackage(FlaskForm): + # TODO: add base game as input name = StringField("Package Name", validators=[DataRequired()]) version = StringField("Current Version", validators=[DataRequired(), Length(min=1, max=30)]) author = StringField("Author(s) or Team", validators=[DataRequired()]) @@ -63,6 +64,11 @@ class UploadPackage(FlaskForm): description = TextAreaField("Description", validators=[DataRequired(), Length(min=1, max=5000)]) requirements = TextAreaField("Requirements", validators=[DataRequired()]) category = SelectField("Primary Category", choices=["Combat", "Sex"], validators=[DataRequired()]) + game = SelectField("Game Version", choices=[(1, "Skyrim"), (2, "Skyrim Special Edition"), + (3, "Skyrim Anniversary Edition")], + validators=[DataRequired()], default=2) nsfw = BooleanField("NSFW Content") - package = FileField("Select Package", validators=[FileAllowed(["zip"]), FileSize(max_size=20*1024*1024)]) + package = FileField("Select Package", validators=[FileAllowed(["zip"]), FileSize(max_size=50*1024*1024)]) + banner = FileField("Select Banner Image", validators=[FileAllowed(["jpg", "gif", "png"]), FileSize(max_size=4*1024*1024)]) + gallery = MultipleFileField("Add Gallery Images", validators=[FileAllowed(["jpg", "gif", "png"]), FileSize(max_size=10*1024*1024)]) upload = SubmitField("Upload") diff --git a/skanhama/models.py b/skanhama/models.py index 633f105..dd3e461 100644 --- a/skanhama/models.py +++ b/skanhama/models.py @@ -10,15 +10,14 @@ def load_user(user_id): class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(23), unique=True, nullable=False) + username = db.Column(db.String(64), unique=True, index=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) - password = db.Column(db.String(60), nullable=False) + password_hash = db.Column(db.String(128), nullable=False) registered_on = db.Column(db.DateTime, nullable=False) confirmed = db.Column(db.Boolean, nullable=False, default=False) confirmed_on = db.Column(db.DateTime, nullable=True) - admin = db.Column(db.Boolean, nullable=False, default=False) uploads = db.relationship("Package", backref="upload_author", lazy="dynamic") - comments = db.relationship("Comment", backref="comment_author", lazy=True) + comments = db.relationship("Comment", backref="comment_author", lazy="dynamic") def __repr__(self): return f"User ('{self.id}', '{self.username}', '{self.email}', '{self.confirmed_on}, 'Admin={self.admin}')" @@ -26,22 +25,23 @@ def __repr__(self): class Package(db.Model): id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(120), unique=True, nullable=False) + name = db.Column(db.String(120), unique=True, index=True, nullable=False) version = db.Column(db.String(30), nullable=False) - author = db.Column(db.String(100), nullable=False) + author = db.Column(db.String(100), index=True, nullable=False) nsfw = db.Column(db.Boolean, nullable=False) summary = db.Column(db.String(230), nullable=False) description = db.Column(db.Text, nullable=False) requirements = db.Column(db.Text, nullable=False) package_dir = db.Column(db.String(400), nullable=False, unique=True) date_uploaded = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) - downloads_total = db.Column(db.BigInteger, nullable=False) + downloads_total = db.Column(db.BigInteger, index=True, nullable=False) downloads_current_version = db.Column(db.BigInteger, nullable=False) - likes = db.Column(db.BigInteger, nullable=False) + likes = db.Column(db.BigInteger, index=True, nullable=False) views_total = db.Column(db.BigInteger, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) category = db.relationship("Categories", backref="category_id", lazy=True) comments = db.relationship("Comment", backref="package_comments", lazy=True) + games = db.relationship("Games", backref="games_id", lazy=True) def __repr__(self): return f"Package ('{self.date_uploaded}', {self.id}', '{self.name}', '{self.uploaderl}', '{self.synposis}')" @@ -64,6 +64,11 @@ def __repr__(self): return f"Package ('{self.id}', {self.name}')" +class Games(db.Model): + id = db.Column(db.Integer, db.ForeignKey("package.id"), primary_key=True) + name = db.Column(db.String(100), unique=True, nullable=False) + + class Comment(db.Model): id = db.Column(db.Integer, primary_key=True) comment = db.Column(db.Text, nullable=False) diff --git a/skanhama/routes.py b/skanhama/routes.py index 9ea2900..5872da1 100644 --- a/skanhama/routes.py +++ b/skanhama/routes.py @@ -1,4 +1,3 @@ -import secrets import os.path import sqlite3 import zipfile @@ -29,8 +28,8 @@ def register(): form = RegistrationForm() if form.validate_on_submit(): hashed_password = generate_password_hash(form.password.data, "sha256") - user = User(username=form.username.data, email=form.email.data, password=hashed_password, - registered_on=datetime.now(), confirmed=False, admin=False) + user = User(username=form.username.data, email=form.email.data, password_hash=hashed_password, + registered_on=datetime.now(), confirmed=False) db.session.add(user) db.session.commit() flash(f"An email has been sent to the registered email for user {form.username.data}. Please " @@ -46,7 +45,7 @@ def login(): form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() - if user and check_password_hash(user.password, form.password.data): + if user and check_password_hash(user.password_hash, form.password.data): login_user(user, remember=form.remember.data) next_page = request.args.get("next") return redirect(next_page) if next_page else redirect(url_for("home")) @@ -121,9 +120,10 @@ def browse(): # Processes the uploaded package -# +# TODO: potentially stream the uploaded file first before saving to disk? See +# https://blog.miguelgrinberg.com/post/handling-file-uploads-with-flask def process_package(form): - processor_version = 0.1 + processor_version = "0.10" f_name, f_ext = os.path.splitext(form.package.data.filename) root_dir = Path(app.root_path, "static/packages", current_user.username) extracted_dir = Path(root_dir, str(form.name.data + "_" + form.version.data)) @@ -165,6 +165,8 @@ def process_package(form): shutil.rmtree(extracted_dir) return file_dict +# def secure_validate_hkx(hkx_file): + @app.route("/upload", methods=["GET", "POST"]) @login_required @@ -177,8 +179,11 @@ def upload(): if file_dict: print(f"package_data: {file_dict['_package_data'][1]}") pack = Package(name=form.name.data, - version=form.version.data, author=current_user.username, + version=form.version.data, + category=form.category.data, + game=form.category.data, + nsfw=form.nsfw.data, summary=form.summary.data, description=form.description.data, requirements=form.requirements.data, @@ -188,7 +193,6 @@ def upload(): downloads_current_version=0, views_total=0, likes=0, - nsfw=form.nsfw.data, user_id=current_user.id) db.session.add(pack) db.session.commit() diff --git a/skanhama/site.db b/skanhama/site.db index b029f1728812480337438b050ae08cf1174c4507..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 49152 zcmeI5TW{OQ6~`qjact$HMbbcRQxp?)7qPLnAX1VgJ6LpOS;-o8>|NPuvVaS9M2;jz zyeJ&fii#G6vs)D1w?6bMv|pfKqi7%Z1MEwI_O)NG1~>GL}dr((t$jk9fHZZ!X0j;59k*{-U?(MDE~M z*E9b~TwVG>;>J;FpMzxu)QPI?hm(?SG@01+SpM1Tl1E4gyZcA=hxG>AKR9ChkN5W0 zSb8}waa4Z_E2lP?JP^&kCj()!9k6(``=~xsAe6$NYqHOc9`A^`+dgjWKB_i8Wk0EZ z%2q6M6JRFT`hKl`$W$%!N{(uoDIC{~<!i`nX(*9li2)o^9~^4BRIi+l0qLGnuW_U+_v zl1kB7FFqD#v>bVJ;dFtUXT+=*W&@ybxMh!(sGXI1pG!ILd~;l4RwM1Tko0U|&IhyW2F0z`la5CI}U1imE% zPQSm9POPq1+VJ;(qnD~ z{KdN!eWP5i=$nv^sf3dJ@zIB%6WFSJW-vsEcqw2E13i;`9@ zRm`@Y*Y)+hwytYTt8A6>TltNgo-gZV{q&t=I+1-pa!&R_zo(wgxfC9_;?dJ*2gY{k z6MwT{zuV?zZWf9gn=N6qiBZ3^>vjx_AQou9Z zyT$a}djadY-hhqEfkp0mVAN+um;2m+gg4gqJx7_7UT8Ce+g2;|86<^4#`2nS&T6xv z7e=-QXxoB6G-Wale%hYbv)m3kFyN447kJF^VEwoX8P@^3b})?@O27qof<1$bVs_XY z`j)eXF1V~Em<(ZGDABP_1cO2x4BNIhSYy}I*VDU^FT#E14VeRfo(Y~v=7dHUUoAG; z#FbBA6il~;u)zlr)!Y?Ek9lE$yZ3qE3rP6Gw#Y89lIWKWT^6kh`+d(3*fuw-QnKne zml@k{_yQ7}q2W;r;=W_QWFJRXbF3C6l-ffE$%o1zs7qDh3P=a#L0}73eGyf*6M`59 zmat7#5x}3KVJNS$Ip{i)IO>8AP9!dcQ^L+c7f9HF1&WE~BB8?bmazR`jZLTM0(1$C zu#4}3F?h*gMOuy>K}pDclmYXiY+k4u_pUx_D19G_0Loj?1*i-5;Na204g--x#;pw! zL%JYgsbpjd!!ofJEdE6BJ|G!cMO}0~l&}&d=nmA%z(%A)ZbM5(VjOT6wy>@h$iV9} z(7x+UJ}D=a^Lh>)1x2A!b>@mN@BuS;<{DyDFX(f2>_AGSYT98`>VXfU#SYBH?g;=5 z?V4LVA}i#WM4_YI>ULu--mfKsfdHA$-BI~F*0E)Gxb4p}SgNNgf1C+MQ&EQzB# z7L{*xy1|TGfHsaD9R3J^yFJAoP%58bID=(}{{n5vk;qpLAJ$e;%!gIBj$x+-<8@nf z`H2N}V;Bl;%(<^^9Y2QfAdl@3?hkOhBV-S{;8^yCZJWy;yy0#aj2{E!z%q_UX~PmW zz$LE7<7hM$9GegwXD>=E=C=x^oL<@7)HXkRf8l1Ln#n%??0ePI+9<{z4LtS{2n*uk zp~~zX4^B0~0|M3bhd63gF0jl!;kFu-ir=Il5P=xH#&#hZB4*RWUWmZ+ z7LFVX;_YbE`vTO0!Ejs)sCWQ|*{D}`84w&r9;NMA1Xu|9N3{du*%d9@GjO}|L&f#^ z+jBcZ$c(m(NpxZj(YLuF{5R~WixD>=!Fu|t=0b5x({lNIQ7db|-Adj{tl!SQ{LvSw z>S<}zT|0gV8k|sq(x4U4!%nq<&Y> zD{KeCRkT44A&DCz{;n&8c@O%qAz(t2AVV1X0;dWHMW!VMhfcePV``5>RD-Z*%AAU* zu|KAzsKSVWSwgv`YJkBzX#@nZujSsGI|l*K1dlo@>OvI6#^4cwC2`V#2BC}0YeS&N zQV|R;G0=|Z_m&N_(U8F{888GP2n^w#0jcpYs#Q=7ot6#zN0EpL6K?zwl~Qxo3AEg{ z`7{8~x>C2pdmwR~okmqph6Bbx4pS=39c@)39ClbIRse?=(R=_g9VW%mQGhA>!c{GW z(&}IXoy69G2w-wC;gF%jR-`M`1i!`xY6gMu7(xUTfstFq`3}Q#6!W30p%I`6(-vzi z-cL0@9#7*ARRIF~bZix84cQ%;2wjfp|7*{m{Pi6j` z$owPo7kHtC2oM1xKm>>Y5g-CYfCvx)B0vO)01^0x5cqDYl$>``HTA<(HF@zlyoFn- z_2sjt2h{if8-Gt^{*-wDFSHN=B0vO)01+SpM1Tko0U|&IhyW4zb`v9qEy#xMTj-qaSJUOFu;zoGYMzxt~y1MvEP_@{rg5CI}U1c(3;AOb{y z2oM1xKm>>Y5g-C@F#-JjpT7UU#T`sdBLYN#2oM1xKm>>Y5g-CYfCvx)A^-&F_kWTB z5g-CYfCvx)B0vO)01+SpM1Tkofw!Lk{r>;<_c67Q2oM1xKm>>Y5g-CYfCvx)B0vO) i0R8??8Xy8hfCvx)B0vO)01+SpM1Tko0V44B6ZkKYfhTeR diff --git a/skanhama/static/dropzone-min.js b/skanhama/static/dropzone-min.js new file mode 100644 index 0000000..99f18a7 --- /dev/null +++ b/skanhama/static/dropzone-min.js @@ -0,0 +1,1795 @@ +! function() { + function e(e) { + return e && e.__esModule ? e.default : e + } + + function t(e) { + if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return e + } + + function i(e, t) { + if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") + } + + function n(e, t) { + for (var i = 0; i < t.length; i++) { + var n = t[i]; + n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(e, n.key, n) + } + } + + function r(e, t, i) { + return t && n(e.prototype, t), i && n(e, i), e + } + + function a(e) { + return a = Object.setPrototypeOf ? Object.getPrototypeOf : function(e) { + return e.__proto__ || Object.getPrototypeOf(e) + }, a(e) + } + + function o(e, t) { + return o = Object.setPrototypeOf || function(e, t) { + return e.__proto__ = t, e + }, o(e, t) + } + + function l(e, t) { + if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function"); + e.prototype = Object.create(t && t.prototype, { + constructor: { + value: e, + writable: !0, + configurable: !0 + } + }), t && o(e, t) + } + + function s(e, i) { + return !i || "object" != ((n = i) && n.constructor === Symbol ? "symbol" : typeof n) && "function" != typeof i ? t(e) : i; + var n + } + var u; + + function c(e) { + return Array.isArray(e) || "[object Object]" == {}.toString.call(e) + } + + function d(e) { + return !e || "object" != typeof e && "function" != typeof e + } + u = function e() { + var t = [].slice.call(arguments), + i = !1; + "boolean" == typeof t[0] && (i = t.shift()); + var n = t[0]; + if (d(n)) throw new Error("extendee must be an object"); + for (var r = t.slice(1), a = r.length, o = 0; o < a; o++) { + var l = r[o]; + for (var s in l) + if (Object.prototype.hasOwnProperty.call(l, s)) { + var u = l[s]; + if (i && c(u)) { + var h = Array.isArray(u) ? [] : {}; + n[s] = e(!0, Object.prototype.hasOwnProperty.call(n, s) && !d(n[s]) ? n[s] : h, u) + } else n[s] = u + } + } + return n + }; + var h = function() { + "use strict"; + + function e() { + i(this, e) + } + return r(e, [{ + key: "on", + value: function(e, t) { + return this._callbacks = this._callbacks || {}, this._callbacks[e] || (this._callbacks[e] = []), this._callbacks[e].push(t), this + } + }, { + key: "emit", + value: function(e) { + for (var t = arguments.length, i = new Array(t > 1 ? t - 1 : 0), n = 1; n < t; n++) i[n - 1] = arguments[n]; + this._callbacks = this._callbacks || {}; + var r = this._callbacks[e], + a = !0, + o = !1, + l = void 0; + if (r) try { + for (var s, u = r[Symbol.iterator](); !(a = (s = u.next()).done); a = !0) { + var c = s.value; + c.apply(this, i) + } + } catch (e) { + o = !0, l = e + } finally { + try { + a || null == u.return || u.return() + } finally { + if (o) throw l + } + } + return this.element && this.element.dispatchEvent(this.makeEvent("dropzone:" + e, { + args: i + })), this + } + }, { + key: "makeEvent", + value: function(e, t) { + var i = { + bubbles: !0, + cancelable: !0, + detail: t + }; + if ("function" == typeof window.CustomEvent) return new CustomEvent(e, i); + var n = document.createEvent("CustomEvent"); + return n.initCustomEvent(e, i.bubbles, i.cancelable, i.detail), n + } + }, { + key: "off", + value: function(e, t) { + if (!this._callbacks || 0 === arguments.length) return this._callbacks = {}, this; + var i = this._callbacks[e]; + if (!i) return this; + if (1 === arguments.length) return delete this._callbacks[e], this; + for (var n = 0; n < i.length; n++) { + var r = i[n]; + if (r === t) { + i.splice(n, 1); + break + } + } + return this + } + }]), e + }(); + var p = { + url: null, + method: "post", + withCredentials: !1, + timeout: null, + parallelUploads: 2, + uploadMultiple: !1, + chunking: !1, + forceChunking: !1, + chunkSize: 2e6, + parallelChunkUploads: !1, + retryChunks: !1, + retryChunksLimit: 3, + maxFilesize: 256, + paramName: "file", + createImageThumbnails: !0, + maxThumbnailFilesize: 10, + thumbnailWidth: 120, + thumbnailHeight: 120, + thumbnailMethod: "crop", + resizeWidth: null, + resizeHeight: null, + resizeMimeType: null, + resizeQuality: .8, + resizeMethod: "contain", + filesizeBase: 1e3, + maxFiles: null, + headers: null, + clickable: !0, + ignoreHiddenFiles: !0, + acceptedFiles: null, + acceptedMimeTypes: null, + autoProcessQueue: !0, + autoQueue: !0, + addRemoveLinks: !1, + previewsContainer: null, + disablePreviews: !1, + hiddenInputContainer: "body", + capture: null, + renameFilename: null, + renameFile: null, + forceFallback: !1, + 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", + dictUploadCanceled: "Upload canceled.", + dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", + dictRemoveFile: "Remove file", + dictRemoveFileConfirmation: null, + dictMaxFilesExceeded: "You can not upload any more files.", + dictFileSizeUnits: { + tb: "TB", + gb: "GB", + mb: "MB", + kb: "KB", + b: "b" + }, + init: function() {}, + params: function(e, t, i) { + if (i) return { + dzuuid: i.file.upload.uuid, + dzchunkindex: i.index, + dztotalfilesize: i.file.size, + dzchunksize: this.options.chunkSize, + dztotalchunkcount: i.file.upload.totalChunkCount, + dzchunkbyteoffset: i.index * this.options.chunkSize + } + }, + accept: function(e, t) { + return t() + }, + chunksUploaded: function(e, t) { + t() + }, + fallback: function() { + var e; + this.element.className = "".concat(this.element.className, " dz-browser-not-supported"); + var t = !0, + i = !1, + n = void 0; + try { + for (var r, a = this.element.getElementsByTagName("div")[Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + var o = r.value; + if (/(^| )dz-message($| )/.test(o.className)) { + e = o, o.className = "dz-message"; + break + } + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + e || (e = f.createElement('
'), this.element.appendChild(e)); + var l = e.getElementsByTagName("span")[0]; + return l && (null != l.textContent ? l.textContent = this.options.dictFallbackMessage : null != l.innerText && (l.innerText = this.options.dictFallbackMessage)), this.element.appendChild(this.getFallbackForm()) + }, + resize: function(e, t, i, n) { + var r = { + srcX: 0, + srcY: 0, + srcWidth: e.width, + srcHeight: e.height + }, + a = e.width / e.height; + null == t && null == i ? (t = r.srcWidth, i = r.srcHeight) : null == t ? t = i * a : null == i && (i = t / a); + var o = (t = Math.min(t, r.srcWidth)) / (i = Math.min(i, r.srcHeight)); + if (r.srcWidth > t || r.srcHeight > i) + if ("crop" === n) a > o ? (r.srcHeight = e.height, r.srcWidth = r.srcHeight * o) : (r.srcWidth = e.width, r.srcHeight = r.srcWidth / o); + else { + if ("contain" !== n) throw new Error("Unknown resizeMethod '".concat(n, "'")); + a > o ? i = t / a : t = i * a + } return r.srcX = (e.width - r.srcWidth) / 2, r.srcY = (e.height - r.srcHeight) / 2, r.trgWidth = t, r.trgHeight = i, r + }, + transformFile: function(e, t) { + return (this.options.resizeWidth || this.options.resizeHeight) && e.type.match(/image.*/) ? this.resizeImage(e, this.options.resizeWidth, this.options.resizeHeight, this.options.resizeMethod, t) : t(e) + }, + previewTemplate: e('
'), + drop: function(e) { + return this.element.classList.remove("dz-drag-hover") + }, + dragstart: function(e) {}, + dragend: function(e) { + return this.element.classList.remove("dz-drag-hover") + }, + dragenter: function(e) { + return this.element.classList.add("dz-drag-hover") + }, + dragover: function(e) { + return this.element.classList.add("dz-drag-hover") + }, + dragleave: function(e) { + return this.element.classList.remove("dz-drag-hover") + }, + paste: function(e) {}, + reset: function() { + return this.element.classList.remove("dz-started") + }, + addedfile: function(e) { + if (this.element === this.previewsContainer && this.element.classList.add("dz-started"), this.previewsContainer && !this.options.disablePreviews) { + var t = this; + e.previewElement = f.createElement(this.options.previewTemplate.trim()), e.previewTemplate = e.previewElement, this.previewsContainer.appendChild(e.previewElement); + var i = !0, + n = !1, + r = void 0; + try { + for (var a, o = e.previewElement.querySelectorAll("[data-dz-name]")[Symbol.iterator](); !(i = (a = o.next()).done); i = !0) { + var l = a.value; + l.textContent = e.name + } + } catch (e) { + n = !0, r = e + } finally { + try { + i || null == o.return || o.return() + } finally { + if (n) throw r + } + } + var s = !0, + u = !1, + c = void 0; + try { + for (var d, h = e.previewElement.querySelectorAll("[data-dz-size]")[Symbol.iterator](); !(s = (d = h.next()).done); s = !0)(l = d.value).innerHTML = this.filesize(e.size) + } catch (e) { + u = !0, c = e + } finally { + try { + s || null == h.return || h.return() + } finally { + if (u) throw c + } + } + this.options.addRemoveLinks && (e._removeLink = f.createElement(''.concat(this.options.dictRemoveFile, "")), e.previewElement.appendChild(e._removeLink)); + var p = function(i) { + var n = t; + if (i.preventDefault(), i.stopPropagation(), e.status === f.UPLOADING) return f.confirm(t.options.dictCancelUploadConfirmation, (function() { + return n.removeFile(e) + })); + var r = t; + return t.options.dictRemoveFileConfirmation ? f.confirm(t.options.dictRemoveFileConfirmation, (function() { + return r.removeFile(e) + })) : t.removeFile(e) + }, + m = !0, + v = !1, + y = void 0; + try { + for (var g, b = e.previewElement.querySelectorAll("[data-dz-remove]")[Symbol.iterator](); !(m = (g = b.next()).done); m = !0) { + g.value.addEventListener("click", p) + } + } catch (e) { + v = !0, y = e + } finally { + try { + m || null == b.return || b.return() + } finally { + if (v) throw y + } + } + } + }, + removedfile: function(e) { + return null != e.previewElement && null != e.previewElement.parentNode && e.previewElement.parentNode.removeChild(e.previewElement), this._updateMaxFilesReachedClass() + }, + thumbnail: function(e, t) { + if (e.previewElement) { + e.previewElement.classList.remove("dz-file-preview"); + var i = !0, + n = !1, + r = void 0; + try { + for (var a, o = e.previewElement.querySelectorAll("[data-dz-thumbnail]")[Symbol.iterator](); !(i = (a = o.next()).done); i = !0) { + var l = a.value; + l.alt = e.name, l.src = t + } + } catch (e) { + n = !0, r = e + } finally { + try { + i || null == o.return || o.return() + } finally { + if (n) throw r + } + } + return setTimeout((function() { + return e.previewElement.classList.add("dz-image-preview") + }), 1) + } + }, + error: function(e, t) { + if (e.previewElement) { + e.previewElement.classList.add("dz-error"), "string" != typeof t && t.error && (t = t.error); + var i = !0, + n = !1, + r = void 0; + try { + for (var a, o = e.previewElement.querySelectorAll("[data-dz-errormessage]")[Symbol.iterator](); !(i = (a = o.next()).done); i = !0) { + a.value.textContent = t + } + } catch (e) { + n = !0, r = e + } finally { + try { + i || null == o.return || o.return() + } finally { + if (n) throw r + } + } + } + }, + errormultiple: function() {}, + processing: function(e) { + if (e.previewElement && (e.previewElement.classList.add("dz-processing"), e._removeLink)) return e._removeLink.innerHTML = this.options.dictCancelUpload + }, + processingmultiple: function() {}, + uploadprogress: function(e, t, i) { + var n = !0, + r = !1, + a = void 0; + if (e.previewElement) try { + for (var o, l = e.previewElement.querySelectorAll("[data-dz-uploadprogress]")[Symbol.iterator](); !(n = (o = l.next()).done); n = !0) { + var s = o.value; + "PROGRESS" === s.nodeName ? s.value = t : s.style.width = "".concat(t, "%") + } + } catch (e) { + r = !0, a = e + } finally { + try { + n || null == l.return || l.return() + } finally { + if (r) throw a + } + } + }, + totaluploadprogress: function() {}, + sending: function() {}, + sendingmultiple: function() {}, + success: function(e) { + if (e.previewElement) return e.previewElement.classList.add("dz-success") + }, + successmultiple: function() {}, + canceled: function(e) { + return this.emit("error", e, this.options.dictUploadCanceled) + }, + canceledmultiple: function() {}, + complete: function(e) { + if (e._removeLink && (e._removeLink.innerHTML = this.options.dictRemoveFile), e.previewElement) return e.previewElement.classList.add("dz-complete") + }, + completemultiple: function() {}, + maxfilesexceeded: function() {}, + maxfilesreached: function() {}, + queuecomplete: function() {}, + addedfiles: function() {} + }, + f = function(n) { + "use strict"; + + function o(n, r) { + var l, c, d, h; + if (i(this, o), (l = s(this, (c = o, a(c)).call(this))).element = n, l.clickableElements = [], l.listeners = [], l.files = [], "string" == typeof l.element && (l.element = document.querySelector(l.element)), !l.element || null == l.element.nodeType) throw new Error("Invalid dropzone element."); + if (l.element.dropzone) throw new Error("Dropzone already attached."); + o.instances.push(t(l)), l.element.dropzone = t(l); + var f = null != (h = o.optionsForElement(l.element)) ? h : {}; + if (l.options = e(u)(!0, {}, p, f, null != r ? r : {}), l.options.previewTemplate = l.options.previewTemplate.replace(/\n*/g, ""), l.options.forceFallback || !o.isBrowserSupported()) return s(l, l.options.fallback.call(t(l))); + if (null == l.options.url && (l.options.url = l.element.getAttribute("action")), !l.options.url) throw new Error("No URL provided."); + if (l.options.acceptedFiles && l.options.acceptedMimeTypes) throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); + if (l.options.uploadMultiple && l.options.chunking) throw new Error("You cannot set both: uploadMultiple and chunking."); + return l.options.acceptedMimeTypes && (l.options.acceptedFiles = l.options.acceptedMimeTypes, delete l.options.acceptedMimeTypes), null != l.options.renameFilename && (l.options.renameFile = function(e) { + return l.options.renameFilename.call(t(l), e.name, e) + }), "string" == typeof l.options.method && (l.options.method = l.options.method.toUpperCase()), (d = l.getExistingFallback()) && d.parentNode && d.parentNode.removeChild(d), !1 !== l.options.previewsContainer && (l.options.previewsContainer ? l.previewsContainer = o.getElement(l.options.previewsContainer, "previewsContainer") : l.previewsContainer = l.element), l.options.clickable && (!0 === l.options.clickable ? l.clickableElements = [l.element] : l.clickableElements = o.getElements(l.options.clickable, "clickable")), l.init(), l + } + return l(o, n), r(o, [{ + key: "getAcceptedFiles", + value: function() { + return this.files.filter((function(e) { + return e.accepted + })).map((function(e) { + return e + })) + } + }, { + key: "getRejectedFiles", + value: function() { + return this.files.filter((function(e) { + return !e.accepted + })).map((function(e) { + return e + })) + } + }, { + key: "getFilesWithStatus", + value: function(e) { + return this.files.filter((function(t) { + return t.status === e + })).map((function(e) { + return e + })) + } + }, { + key: "getQueuedFiles", + value: function() { + return this.getFilesWithStatus(o.QUEUED) + } + }, { + key: "getUploadingFiles", + value: function() { + return this.getFilesWithStatus(o.UPLOADING) + } + }, { + key: "getAddedFiles", + value: function() { + return this.getFilesWithStatus(o.ADDED) + } + }, { + key: "getActiveFiles", + value: function() { + return this.files.filter((function(e) { + return e.status === o.UPLOADING || e.status === o.QUEUED + })).map((function(e) { + return e + })) + } + }, { + key: "init", + value: function() { + var e = this, + t = this, + i = this, + n = this, + r = this, + a = this, + l = this, + s = this, + u = this, + c = this, + d = this; + if ("form" === this.element.tagName && this.element.setAttribute("enctype", "multipart/form-data"), this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message") && this.element.appendChild(o.createElement('
"))), this.clickableElements.length) { + var h = this, + p = function() { + var e = h; + h.hiddenFileInput && h.hiddenFileInput.parentNode.removeChild(h.hiddenFileInput), h.hiddenFileInput = document.createElement("input"), h.hiddenFileInput.setAttribute("type", "file"), (null === h.options.maxFiles || h.options.maxFiles > 1) && h.hiddenFileInput.setAttribute("multiple", "multiple"), h.hiddenFileInput.className = "dz-hidden-input", null !== h.options.acceptedFiles && h.hiddenFileInput.setAttribute("accept", h.options.acceptedFiles), null !== h.options.capture && h.hiddenFileInput.setAttribute("capture", h.options.capture), h.hiddenFileInput.setAttribute("tabindex", "-1"), h.hiddenFileInput.style.visibility = "hidden", h.hiddenFileInput.style.position = "absolute", h.hiddenFileInput.style.top = "0", h.hiddenFileInput.style.left = "0", h.hiddenFileInput.style.height = "0", h.hiddenFileInput.style.width = "0", o.getElement(h.options.hiddenInputContainer, "hiddenInputContainer").appendChild(h.hiddenFileInput), h.hiddenFileInput.addEventListener("change", (function() { + var t = e.hiddenFileInput.files, + i = !0, + n = !1, + r = void 0; + if (t.length) try { + for (var a, o = t[Symbol.iterator](); !(i = (a = o.next()).done); i = !0) { + var l = a.value; + e.addFile(l) + } + } catch (e) { + n = !0, r = e + } finally { + try { + i || null == o.return || o.return() + } finally { + if (n) throw r + } + } + e.emit("addedfiles", t), p() + })) + }; + p() + } + this.URL = null !== window.URL ? window.URL : window.webkitURL; + var f = !0, + m = !1, + v = void 0; + try { + for (var y, g = this.events[Symbol.iterator](); !(f = (y = g.next()).done); f = !0) { + var b = y.value; + this.on(b, this.options[b]) + } + } catch (e) { + m = !0, v = e + } finally { + try { + f || null == g.return || g.return() + } finally { + if (m) throw v + } + } + this.on("uploadprogress", (function() { + return e.updateTotalUploadProgress() + })), this.on("removedfile", (function() { + return t.updateTotalUploadProgress() + })), this.on("canceled", (function(e) { + return i.emit("complete", e) + })), this.on("complete", (function(e) { + var t = n; + if (0 === n.getAddedFiles().length && 0 === n.getUploadingFiles().length && 0 === n.getQueuedFiles().length) return setTimeout((function() { + return t.emit("queuecomplete") + }), 0) + })); + var k = function(e) { + if (function(e) { + if (e.dataTransfer.types) + for (var t = 0; t < e.dataTransfer.types.length; t++) + if ("Files" === e.dataTransfer.types[t]) return !0; + return !1 + }(e)) return e.stopPropagation(), e.preventDefault ? e.preventDefault() : e.returnValue = !1 + }; + return this.listeners = [{ + element: this.element, + events: { + dragstart: function(e) { + return r.emit("dragstart", e) + }, + dragenter: function(e) { + return k(e), a.emit("dragenter", e) + }, + dragover: function(e) { + var t; + try { + t = e.dataTransfer.effectAllowed + } catch (e) {} + return e.dataTransfer.dropEffect = "move" === t || "linkMove" === t ? "move" : "copy", k(e), l.emit("dragover", e) + }, + dragleave: function(e) { + return s.emit("dragleave", e) + }, + drop: function(e) { + return k(e), u.drop(e) + }, + dragend: function(e) { + return c.emit("dragend", e) + } + } + }], this.clickableElements.forEach((function(e) { + var t = d; + return d.listeners.push({ + element: e, + events: { + click: function(i) { + return (e !== t.element || i.target === t.element || o.elementInside(i.target, t.element.querySelector(".dz-message"))) && t.hiddenFileInput.click(), !0 + } + } + }) + })), this.enable(), this.options.init.call(this) + } + }, { + key: "destroy", + value: function() { + return this.disable(), this.removeAllFiles(!0), (null != this.hiddenFileInput ? this.hiddenFileInput.parentNode : void 0) && (this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput), this.hiddenFileInput = null), delete this.element.dropzone, o.instances.splice(o.instances.indexOf(this), 1) + } + }, { + key: "updateTotalUploadProgress", + value: function() { + var e, t = 0, + i = 0; + if (this.getActiveFiles().length) { + var n = !0, + r = !1, + a = void 0; + try { + for (var o, l = this.getActiveFiles()[Symbol.iterator](); !(n = (o = l.next()).done); n = !0) { + var s = o.value; + t += s.upload.bytesSent, i += s.upload.total + } + } catch (e) { + r = !0, a = e + } finally { + try { + n || null == l.return || l.return() + } finally { + if (r) throw a + } + } + e = 100 * t / i + } else e = 100; + return this.emit("totaluploadprogress", e, i, t) + } + }, { + key: "_getParamName", + value: function(e) { + return "function" == typeof this.options.paramName ? this.options.paramName(e) : "".concat(this.options.paramName).concat(this.options.uploadMultiple ? "[".concat(e, "]") : "") + } + }, { + key: "_renameFile", + value: function(e) { + return "function" != typeof this.options.renameFile ? e.name : this.options.renameFile(e) + } + }, { + key: "getFallbackForm", + value: function() { + var e, t; + if (e = this.getExistingFallback()) return e; + var i = '
'; + this.options.dictFallbackText && (i += "

".concat(this.options.dictFallbackText, "

")), i += '
'); + var n = o.createElement(i); + return "FORM" !== this.element.tagName ? (t = o.createElement('
'))).appendChild(n) : (this.element.setAttribute("enctype", "multipart/form-data"), this.element.setAttribute("method", this.options.method)), null != t ? t : n + } + }, { + key: "getExistingFallback", + value: function() { + var e = function(e) { + var t = !0, + i = !1, + n = void 0; + try { + for (var r, a = e[Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + var o = r.value; + if (/(^| )fallback($| )/.test(o.className)) return o + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + }, + t = !0, + i = !1, + n = void 0; + try { + for (var r, a = ["div", "form"][Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + var o, l = r.value; + if (o = e(this.element.getElementsByTagName(l))) return o + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + } + }, { + key: "setupEventListeners", + value: function() { + return this.listeners.map((function(e) { + return function() { + var t = []; + for (var i in e.events) { + var n = e.events[i]; + t.push(e.element.addEventListener(i, n, !1)) + } + return t + }() + })) + } + }, { + key: "removeEventListeners", + value: function() { + return this.listeners.map((function(e) { + return function() { + var t = []; + for (var i in e.events) { + var n = e.events[i]; + t.push(e.element.removeEventListener(i, n, !1)) + } + return t + }() + })) + } + }, { + key: "disable", + value: function() { + var e = this; + return this.clickableElements.forEach((function(e) { + return e.classList.remove("dz-clickable") + })), this.removeEventListeners(), this.disabled = !0, this.files.map((function(t) { + return e.cancelUpload(t) + })) + } + }, { + key: "enable", + value: function() { + return delete this.disabled, this.clickableElements.forEach((function(e) { + return e.classList.add("dz-clickable") + })), this.setupEventListeners() + } + }, { + key: "filesize", + value: function(e) { + var t = 0, + i = "b"; + if (e > 0) { + for (var n = ["tb", "gb", "mb", "kb", "b"], r = 0; r < n.length; r++) { + var a = n[r]; + if (e >= Math.pow(this.options.filesizeBase, 4 - r) / 10) { + t = e / Math.pow(this.options.filesizeBase, 4 - r), i = a; + break + } + } + t = Math.round(10 * t) / 10 + } + return "".concat(t, " ").concat(this.options.dictFileSizeUnits[i]) + } + }, { + key: "_updateMaxFilesReachedClass", + value: function() { + return null != this.options.maxFiles && this.getAcceptedFiles().length >= this.options.maxFiles ? (this.getAcceptedFiles().length === this.options.maxFiles && this.emit("maxfilesreached", this.files), this.element.classList.add("dz-max-files-reached")) : this.element.classList.remove("dz-max-files-reached") + } + }, { + key: "drop", + value: function(e) { + if (e.dataTransfer) { + this.emit("drop", e); + for (var t = [], i = 0; i < e.dataTransfer.files.length; i++) t[i] = e.dataTransfer.files[i]; + if (t.length) { + var n = e.dataTransfer.items; + n && n.length && null != n[0].webkitGetAsEntry ? this._addFilesFromItems(n) : this.handleFiles(t) + } + this.emit("addedfiles", t) + } + } + }, { + key: "paste", + value: function(e) { + if (null != (t = null != e ? e.clipboardData : void 0, i = function(e) { + return e.items + }, null != t ? i(t) : void 0)) { + var t, i; + this.emit("paste", e); + var n = e.clipboardData.items; + return n.length ? this._addFilesFromItems(n) : void 0 + } + } + }, { + key: "handleFiles", + value: function(e) { + var t = !0, + i = !1, + n = void 0; + try { + for (var r, a = e[Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + var o = r.value; + this.addFile(o) + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + } + }, { + key: "_addFilesFromItems", + value: function(e) { + var t = this; + return function() { + var i = [], + n = !0, + r = !1, + a = void 0; + try { + for (var o, l = e[Symbol.iterator](); !(n = (o = l.next()).done); n = !0) { + var s, u = o.value; + null != u.webkitGetAsEntry && (s = u.webkitGetAsEntry()) ? s.isFile ? i.push(t.addFile(u.getAsFile())) : s.isDirectory ? i.push(t._addFilesFromDirectory(s, s.name)) : i.push(void 0) : null != u.getAsFile && (null == u.kind || "file" === u.kind) ? i.push(t.addFile(u.getAsFile())) : i.push(void 0) + } + } catch (e) { + r = !0, a = e + } finally { + try { + n || null == l.return || l.return() + } finally { + if (r) throw a + } + } + return i + }() + } + }, { + key: "_addFilesFromDirectory", + value: function(e, t) { + var i = this, + n = e.createReader(), + r = function(e) { + return t = console, i = "log", n = function(t) { + return t.log(e) + }, null != t && "function" == typeof t[i] ? n(t, i) : void 0; + var t, i, n + }, + a = function() { + var e = i; + return n.readEntries((function(i) { + if (i.length > 0) { + var n = !0, + r = !1, + o = void 0; + try { + for (var l, s = i[Symbol.iterator](); !(n = (l = s.next()).done); n = !0) { + var u = l.value, + c = e; + u.isFile ? u.file((function(e) { + if (!c.options.ignoreHiddenFiles || "." !== e.name.substring(0, 1)) return e.fullPath = "".concat(t, "/").concat(e.name), c.addFile(e) + })) : u.isDirectory && e._addFilesFromDirectory(u, "".concat(t, "/").concat(u.name)) + } + } catch (e) { + r = !0, o = e + } finally { + try { + n || null == s.return || s.return() + } finally { + if (r) throw o + } + } + a() + } + return null + }), r) + }; + return a() + } + }, { + key: "accept", + value: function(e, t) { + this.options.maxFilesize && e.size > 1048576 * this.options.maxFilesize ? t(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(e.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize)) : o.isValidFile(e, this.options.acceptedFiles) ? null != this.options.maxFiles && this.getAcceptedFiles().length >= this.options.maxFiles ? (t(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles)), this.emit("maxfilesexceeded", e)) : this.options.accept.call(this, e, t) : t(this.options.dictInvalidFileType) + } + }, { + key: "addFile", + value: function(e) { + var t = this; + e.upload = { + uuid: o.uuidv4(), + progress: 0, + total: e.size, + bytesSent: 0, + filename: this._renameFile(e) + }, this.files.push(e), e.status = o.ADDED, this.emit("addedfile", e), this._enqueueThumbnail(e), this.accept(e, (function(i) { + i ? (e.accepted = !1, t._errorProcessing([e], i)) : (e.accepted = !0, t.options.autoQueue && t.enqueueFile(e)), t._updateMaxFilesReachedClass() + })) + } + }, { + key: "enqueueFiles", + value: function(e) { + var t = !0, + i = !1, + n = void 0; + try { + for (var r, a = e[Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + var o = r.value; + this.enqueueFile(o) + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + return null + } + }, { + key: "enqueueFile", + value: function(e) { + if (e.status !== o.ADDED || !0 !== e.accepted) throw new Error("This file can't be queued because it has already been processed or was rejected."); + var t = this; + if (e.status = o.QUEUED, this.options.autoProcessQueue) return setTimeout((function() { + return t.processQueue() + }), 0) + } + }, { + key: "_enqueueThumbnail", + value: function(e) { + if (this.options.createImageThumbnails && e.type.match(/image.*/) && e.size <= 1048576 * this.options.maxThumbnailFilesize) { + var t = this; + return this._thumbnailQueue.push(e), setTimeout((function() { + return t._processThumbnailQueue() + }), 0) + } + } + }, { + key: "_processThumbnailQueue", + value: function() { + var e = this; + if (!this._processingThumbnail && 0 !== this._thumbnailQueue.length) { + this._processingThumbnail = !0; + var t = this._thumbnailQueue.shift(); + return this.createThumbnail(t, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, !0, (function(i) { + return e.emit("thumbnail", t, i), e._processingThumbnail = !1, e._processThumbnailQueue() + })) + } + } + }, { + key: "removeFile", + value: function(e) { + if (e.status === o.UPLOADING && this.cancelUpload(e), this.files = m(this.files, e), this.emit("removedfile", e), 0 === this.files.length) return this.emit("reset") + } + }, { + key: "removeAllFiles", + value: function(e) { + null == e && (e = !1); + var t = !0, + i = !1, + n = void 0; + try { + for (var r, a = this.files.slice()[Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + var l = r.value; + (l.status !== o.UPLOADING || e) && this.removeFile(l) + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + return null + } + }, { + key: "resizeImage", + value: function(e, t, i, n, r) { + var a = this; + return this.createThumbnail(e, t, i, n, !0, (function(t, i) { + if (null == i) return r(e); + var n = a.options.resizeMimeType; + null == n && (n = e.type); + var l = i.toDataURL(n, a.options.resizeQuality); + return "image/jpeg" !== n && "image/jpg" !== n || (l = g.restore(e.dataURL, l)), r(o.dataURItoBlob(l)) + })) + } + }, { + key: "createThumbnail", + value: function(e, t, i, n, r, a) { + var o = this, + l = new FileReader; + l.onload = function() { + e.dataURL = l.result, "image/svg+xml" !== e.type ? o.createThumbnailFromUrl(e, t, i, n, r, a) : null != a && a(l.result) + }, l.readAsDataURL(e) + } + }, { + key: "displayExistingFile", + value: function(e, t, i, n, r) { + var a = void 0 === r || r; + if (this.emit("addedfile", e), this.emit("complete", e), a) { + var o = this; + e.dataURL = t, this.createThumbnailFromUrl(e, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, this.options.fixOrientation, (function(t) { + o.emit("thumbnail", e, t), i && i() + }), n) + } else this.emit("thumbnail", e, t), i && i() + } + }, { + key: "createThumbnailFromUrl", + value: function(e, t, i, n, r, a, o) { + var l = this, + s = document.createElement("img"); + return o && (s.crossOrigin = o), r = "from-image" != getComputedStyle(document.body).imageOrientation && r, s.onload = function() { + var o = l, + u = function(e) { + return e(1) + }; + return "undefined" != typeof EXIF && null !== EXIF && r && (u = function(e) { + return EXIF.getData(s, (function() { + return e(EXIF.getTag(this, "Orientation")) + })) + }), u((function(r) { + e.width = s.width, e.height = s.height; + var l = o.options.resize.call(o, e, t, i, n), + u = document.createElement("canvas"), + c = u.getContext("2d"); + switch (u.width = l.trgWidth, u.height = l.trgHeight, r > 4 && (u.width = l.trgHeight, u.height = l.trgWidth), r) { + case 2: + c.translate(u.width, 0), c.scale(-1, 1); + break; + case 3: + c.translate(u.width, u.height), c.rotate(Math.PI); + break; + case 4: + c.translate(0, u.height), 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, -u.width); + break; + case 7: + c.rotate(.5 * Math.PI), c.translate(u.height, -u.width), c.scale(-1, 1); + break; + case 8: + c.rotate(-.5 * Math.PI), c.translate(-u.height, 0) + } + y(c, s, null != l.srcX ? l.srcX : 0, null != l.srcY ? l.srcY : 0, l.srcWidth, l.srcHeight, null != l.trgX ? l.trgX : 0, null != l.trgY ? l.trgY : 0, l.trgWidth, l.trgHeight); + var d = u.toDataURL("image/png"); + if (null != a) return a(d, u) + })) + }, null != a && (s.onerror = a), s.src = e.dataURL + } + }, { + key: "processQueue", + value: function() { + var e = this.options.parallelUploads, + t = this.getUploadingFiles().length, + i = t; + if (!(t >= e)) { + var n = this.getQueuedFiles(); + if (n.length > 0) { + if (this.options.uploadMultiple) return this.processFiles(n.slice(0, e - t)); + for (; i < e;) { + if (!n.length) return; + this.processFile(n.shift()), i++ + } + } + } + } + }, { + key: "processFile", + value: function(e) { + return this.processFiles([e]) + } + }, { + key: "processFiles", + value: function(e) { + var t = !0, + i = !1, + n = void 0; + try { + for (var r, a = e[Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + var l = r.value; + l.processing = !0, l.status = o.UPLOADING, this.emit("processing", l) + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + return this.options.uploadMultiple && this.emit("processingmultiple", e), this.uploadFiles(e) + } + }, { + key: "_getFilesWithXhr", + value: function(e) { + return this.files.filter((function(t) { + return t.xhr === e + })).map((function(e) { + return e + })) + } + }, { + key: "cancelUpload", + value: function(e) { + if (e.status === o.UPLOADING) { + var t = this._getFilesWithXhr(e.xhr), + i = !0, + n = !1, + r = void 0; + try { + for (var a, l = t[Symbol.iterator](); !(i = (a = l.next()).done); i = !0) { + (p = a.value).status = o.CANCELED + } + } catch (e) { + n = !0, r = e + } finally { + try { + i || null == l.return || l.return() + } finally { + if (n) throw r + } + } + void 0 !== e.xhr && e.xhr.abort(); + var s = !0, + u = !1, + c = void 0; + try { + for (var d, h = t[Symbol.iterator](); !(s = (d = h.next()).done); s = !0) { + var p = d.value; + this.emit("canceled", p) + } + } catch (e) { + u = !0, c = e + } finally { + try { + s || null == h.return || h.return() + } finally { + if (u) throw c + } + } + this.options.uploadMultiple && this.emit("canceledmultiple", t) + } else e.status !== o.ADDED && e.status !== o.QUEUED || (e.status = o.CANCELED, this.emit("canceled", e), this.options.uploadMultiple && this.emit("canceledmultiple", [e])); + if (this.options.autoProcessQueue) return this.processQueue() + } + }, { + key: "resolveOption", + value: function(e) { + for (var t = arguments.length, i = new Array(t > 1 ? t - 1 : 0), n = 1; n < t; n++) i[n - 1] = arguments[n]; + return "function" == typeof e ? e.apply(this, i) : e + } + }, { + key: "uploadFile", + value: function(e) { + return this.uploadFiles([e]) + } + }, { + key: "uploadFiles", + value: function(e) { + var t = this; + this._transformFiles(e, (function(i) { + if (t.options.chunking) { + var n = i[0]; + e[0].upload.chunked = t.options.chunking && (t.options.forceChunking || n.size > t.options.chunkSize), e[0].upload.totalChunkCount = Math.ceil(n.size / t.options.chunkSize) + } + if (e[0].upload.chunked) { + var r = t, + a = t, + l = e[0]; + n = i[0]; + l.upload.chunks = []; + var s = function() { + for (var t = 0; void 0 !== l.upload.chunks[t];) t++; + if (!(t >= l.upload.totalChunkCount)) { + 0; + var i = t * r.options.chunkSize, + a = Math.min(i + r.options.chunkSize, n.size), + s = { + name: r._getParamName(0), + data: n.webkitSlice ? n.webkitSlice(i, a) : n.slice(i, a), + filename: l.upload.filename, + chunkIndex: t + }; + l.upload.chunks[t] = { + file: l, + index: t, + dataBlock: s, + status: o.UPLOADING, + progress: 0, + retries: 0 + }, r._uploadData(e, [s]) + } + }; + if (l.upload.finishedChunkUpload = function(t, i) { + var n = a, + r = !0; + t.status = o.SUCCESS, t.dataBlock = null, t.xhr = null; + for (var u = 0; u < l.upload.totalChunkCount; u++) { + if (void 0 === l.upload.chunks[u]) return s(); + l.upload.chunks[u].status !== o.SUCCESS && (r = !1) + } + r && a.options.chunksUploaded(l, (function() { + n._finished(e, i, null) + })) + }, t.options.parallelChunkUploads) + for (var u = 0; u < l.upload.totalChunkCount; u++) s(); + else s() + } else { + var c = []; + for (u = 0; u < e.length; u++) c[u] = { + name: t._getParamName(u), + data: i[u], + filename: e[u].upload.filename + }; + t._uploadData(e, c) + } + })) + } + }, { + key: "_getChunk", + value: function(e, t) { + for (var i = 0; i < e.upload.totalChunkCount; i++) + if (void 0 !== e.upload.chunks[i] && e.upload.chunks[i].xhr === t) return e.upload.chunks[i] + } + }, { + key: "_uploadData", + value: function(t, i) { + var n = this, + r = this, + a = this, + o = this, + l = new XMLHttpRequest, + s = !0, + c = !1, + d = void 0; + try { + for (var h, p = t[Symbol.iterator](); !(s = (h = p.next()).done); s = !0) { + (_ = h.value).xhr = l + } + } catch (e) { + c = !0, d = e + } finally { + try { + s || null == p.return || p.return() + } finally { + if (c) throw d + } + } + t[0].upload.chunked && (t[0].upload.chunks[i[0].chunkIndex].xhr = l); + var f = this.resolveOption(this.options.method, t), + m = this.resolveOption(this.options.url, t); + l.open(f, m, !0), this.resolveOption(this.options.timeout, t) && (l.timeout = this.resolveOption(this.options.timeout, t)), l.withCredentials = !!this.options.withCredentials, l.onload = function(e) { + n._finishedUploading(t, l, e) + }, l.ontimeout = function() { + r._handleUploadError(t, l, "Request timedout after ".concat(r.options.timeout / 1e3, " seconds")) + }, l.onerror = function() { + a._handleUploadError(t, l) + }, (null != l.upload ? l.upload : l).onprogress = function(e) { + return o._updateFilesUploadProgress(t, l, e) + }; + var v = { + Accept: "application/json", + "Cache-Control": "no-cache", + "X-Requested-With": "XMLHttpRequest" + }; + for (var y in this.options.headers && e(u)(v, this.options.headers), v) { + var g = v[y]; + g && l.setRequestHeader(y, g) + } + var b = new FormData; + if (this.options.params) { + var k = this.options.params; + for (var w in "function" == typeof k && (k = k.call(this, t, l, t[0].upload.chunked ? this._getChunk(t[0], l) : null)), k) { + var F = k[w]; + if (Array.isArray(F)) + for (var E = 0; E < F.length; E++) b.append(w, F[E]); + else b.append(w, F) + } + } + var x = !0, + z = !1, + C = void 0; + try { + for (var S, A = t[Symbol.iterator](); !(x = (S = A.next()).done); x = !0) { + var _ = S.value; + this.emit("sending", _, l, b) + } + } catch (e) { + z = !0, C = e + } finally { + try { + x || null == A.return || A.return() + } finally { + if (z) throw C + } + } + this.options.uploadMultiple && this.emit("sendingmultiple", t, l, b), this._addFormElementData(b); + for (E = 0; E < i.length; E++) { + var T = i[E]; + b.append(T.name, T.data, T.filename) + } + this.submitRequest(l, b, t) + } + }, { + key: "_transformFiles", + value: function(e, t) { + for (var i = this, n = function(n) { + i.options.transformFile.call(i, e[n], (function(i) { + r[n] = i, ++a === e.length && t(r) + })) + }, r = [], a = 0, o = 0; o < e.length; o++) n(o) + } + }, { + key: "_addFormElementData", + value: function(e) { + var t = !0, + i = !1, + n = void 0; + if ("FORM" === this.element.tagName) try { + for (var r = this.element.querySelectorAll("input, textarea, select, button")[Symbol.iterator](); !(t = (s = r.next()).done); t = !0) { + var a = s.value, + o = a.getAttribute("name"), + l = a.getAttribute("type"); + if (l && (l = l.toLowerCase()), null != o) + if ("SELECT" === a.tagName && a.hasAttribute("multiple")) { + t = !0, i = !1, n = void 0; + try { + var s; + for (r = a.options[Symbol.iterator](); !(t = (s = r.next()).done); t = !0) { + var u = s.value; + u.selected && e.append(o, u.value) + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == r.return || r.return() + } finally { + if (i) throw n + } + } + } else(!l || "checkbox" !== l && "radio" !== l || a.checked) && e.append(o, a.value) + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == r.return || r.return() + } finally { + if (i) throw n + } + } + } + }, { + key: "_updateFilesUploadProgress", + value: function(e, t, i) { + var n = !0, + r = !1, + a = void 0; + if (e[0].upload.chunked) { + c = e[0]; + var o = this._getChunk(c, t); + i ? (o.progress = 100 * i.loaded / i.total, o.total = i.total, o.bytesSent = i.loaded) : (o.progress = 100, o.bytesSent = o.total), c.upload.progress = 0, c.upload.total = 0, c.upload.bytesSent = 0; + for (var l = 0; l < c.upload.totalChunkCount; l++) c.upload.chunks[l] && void 0 !== c.upload.chunks[l].progress && (c.upload.progress += c.upload.chunks[l].progress, c.upload.total += c.upload.chunks[l].total, c.upload.bytesSent += c.upload.chunks[l].bytesSent); + c.upload.progress = c.upload.progress / c.upload.totalChunkCount, this.emit("uploadprogress", c, c.upload.progress, c.upload.bytesSent) + } else try { + for (var s, u = e[Symbol.iterator](); !(n = (s = u.next()).done); n = !0) { + var c; + (c = s.value).upload.total && c.upload.bytesSent && c.upload.bytesSent == c.upload.total || (i ? (c.upload.progress = 100 * i.loaded / i.total, c.upload.total = i.total, c.upload.bytesSent = i.loaded) : (c.upload.progress = 100, c.upload.bytesSent = c.upload.total), this.emit("uploadprogress", c, c.upload.progress, c.upload.bytesSent)) + } + } catch (e) { + r = !0, a = e + } finally { + try { + n || null == u.return || u.return() + } finally { + if (r) throw a + } + } + } + }, { + key: "_finishedUploading", + value: function(e, t, i) { + var n; + if (e[0].status !== o.CANCELED && 4 === t.readyState) { + if ("arraybuffer" !== t.responseType && "blob" !== t.responseType && (n = t.responseText, t.getResponseHeader("content-type") && ~t.getResponseHeader("content-type").indexOf("application/json"))) try { + n = JSON.parse(n) + } catch (e) { + i = e, n = "Invalid JSON response from server." + } + this._updateFilesUploadProgress(e, t), 200 <= t.status && t.status < 300 ? e[0].upload.chunked ? e[0].upload.finishedChunkUpload(this._getChunk(e[0], t), n) : this._finished(e, n, i) : this._handleUploadError(e, t, n) + } + } + }, { + key: "_handleUploadError", + value: function(e, t, i) { + if (e[0].status !== o.CANCELED) { + if (e[0].upload.chunked && this.options.retryChunks) { + var n = this._getChunk(e[0], t); + if (n.retries++ < this.options.retryChunksLimit) return void this._uploadData(e, [n.dataBlock]); + console.warn("Retried this chunk too often. Giving up.") + } + this._errorProcessing(e, i || this.options.dictResponseError.replace("{{statusCode}}", t.status), t) + } + } + }, { + key: "submitRequest", + value: function(e, t, i) { + 1 == e.readyState ? e.send(t) : console.warn("Cannot send this request because the XMLHttpRequest.readyState is not OPENED.") + } + }, { + key: "_finished", + value: function(e, t, i) { + var n = !0, + r = !1, + a = void 0; + try { + for (var l, s = e[Symbol.iterator](); !(n = (l = s.next()).done); n = !0) { + var u = l.value; + u.status = o.SUCCESS, this.emit("success", u, t, i), this.emit("complete", u) + } + } catch (e) { + r = !0, a = e + } finally { + try { + n || null == s.return || s.return() + } finally { + if (r) throw a + } + } + if (this.options.uploadMultiple && (this.emit("successmultiple", e, t, i), this.emit("completemultiple", e)), this.options.autoProcessQueue) return this.processQueue() + } + }, { + key: "_errorProcessing", + value: function(e, t, i) { + var n = !0, + r = !1, + a = void 0; + try { + for (var l, s = e[Symbol.iterator](); !(n = (l = s.next()).done); n = !0) { + var u = l.value; + u.status = o.ERROR, this.emit("error", u, t, i), this.emit("complete", u) + } + } catch (e) { + r = !0, a = e + } finally { + try { + n || null == s.return || s.return() + } finally { + if (r) throw a + } + } + if (this.options.uploadMultiple && (this.emit("errormultiple", e, t, i), this.emit("completemultiple", e)), this.options.autoProcessQueue) return this.processQueue() + } + }], [{ + key: "initClass", + value: function() { + this.prototype.Emitter = h, this.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"], this.prototype._thumbnailQueue = [], this.prototype._processingThumbnail = !1 + } + }, { + key: "uuidv4", + value: function() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (function(e) { + var t = 16 * Math.random() | 0; + return ("x" === e ? t : 3 & t | 8).toString(16) + })) + } + }]), o + }(h); + f.initClass(), f.options = {}, f.optionsForElement = function(e) { + return e.getAttribute("id") ? f.options[v(e.getAttribute("id"))] : void 0 + }, f.instances = [], f.forElement = function(e) { + if ("string" == typeof e && (e = document.querySelector(e)), null == (null != e ? e.dropzone : void 0)) throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."); + return e.dropzone + }, f.discover = function() { + var e; + if (document.querySelectorAll) e = document.querySelectorAll(".dropzone"); + else { + e = []; + var t = function(t) { + return function() { + var i = [], + n = !0, + r = !1, + a = void 0; + try { + for (var o, l = t[Symbol.iterator](); !(n = (o = l.next()).done); n = !0) { + var s = o.value; + /(^| )dropzone($| )/.test(s.className) ? i.push(e.push(s)) : i.push(void 0) + } + } catch (e) { + r = !0, a = e + } finally { + try { + n || null == l.return || l.return() + } finally { + if (r) throw a + } + } + return i + }() + }; + t(document.getElementsByTagName("div")), t(document.getElementsByTagName("form")) + } + return function() { + var t = [], + i = !0, + n = !1, + r = void 0; + try { + for (var a, o = e[Symbol.iterator](); !(i = (a = o.next()).done); i = !0) { + var l = a.value; + !1 !== f.optionsForElement(l) ? t.push(new f(l)) : t.push(void 0) + } + } catch (e) { + n = !0, r = e + } finally { + try { + i || null == o.return || o.return() + } finally { + if (n) throw r + } + } + return t + }() + }, f.blockedBrowsers = [/opera.*(Macintosh|Windows Phone).*version\/12/i], f.isBrowserSupported = function() { + var e = !0; + if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) + if ("classList" in document.createElement("a")) { + void 0 !== f.blacklistedBrowsers && (f.blockedBrowsers = f.blacklistedBrowsers); + var t = !0, + i = !1, + n = void 0; + try { + for (var r, a = f.blockedBrowsers[Symbol.iterator](); !(t = (r = a.next()).done); t = !0) { + r.value.test(navigator.userAgent) && (e = !1) + } + } catch (e) { + i = !0, n = e + } finally { + try { + t || null == a.return || a.return() + } finally { + if (i) throw n + } + } + } else e = !1; + else e = !1; + return e + }, f.dataURItoBlob = function(e) { + for (var t = atob(e.split(",")[1]), i = e.split(",")[0].split(":")[1].split(";")[0], n = new ArrayBuffer(t.length), r = new Uint8Array(n), a = 0, o = t.length, l = 0 <= o; l ? a <= o : a >= o; l ? a++ : a--) r[a] = t.charCodeAt(a); + return new Blob([n], { + type: i + }) + }; + var m = function(e, t) { + return e.filter((function(e) { + return e !== t + })).map((function(e) { + return e + })) + }, + v = function(e) { + return e.replace(/[\-_](\w)/g, (function(e) { + return e.charAt(1).toUpperCase() + })) + }; + f.createElement = function(e) { + var t = document.createElement("div"); + return t.innerHTML = e, t.childNodes[0] + }, f.elementInside = function(e, t) { + if (e === t) return !0; + for (; e = e.parentNode;) + if (e === t) return !0; + return !1 + }, f.getElement = function(e, t) { + var i; + if ("string" == typeof e ? i = document.querySelector(e) : null != e.nodeType && (i = e), null == i) throw new Error("Invalid `".concat(t, "` option provided. Please provide a CSS selector or a plain HTML element.")); + return i + }, f.getElements = function(e, t) { + var i, n; + if (e instanceof Array) { + n = []; + try { + var r = !0, + a = !1, + o = void 0; + try { + for (var l = e[Symbol.iterator](); !(r = (s = l.next()).done); r = !0) i = s.value, n.push(this.getElement(i, t)) + } catch (e) { + a = !0, o = e + } finally { + try { + r || null == l.return || l.return() + } finally { + if (a) throw o + } + } + } catch (e) { + n = null + } + } else if ("string" == typeof e) { + n = []; + r = !0, a = !1, o = void 0; + try { + var s; + for (l = document.querySelectorAll(e)[Symbol.iterator](); !(r = (s = l.next()).done); r = !0) i = s.value, n.push(i) + } catch (e) { + a = !0, o = e + } finally { + try { + r || null == l.return || l.return() + } finally { + if (a) throw o + } + } + } else null != e.nodeType && (n = [e]); + if (null == n || !n.length) throw new Error("Invalid `".concat(t, "` option provided. Please provide a CSS selector, a plain HTML element or a list of those.")); + return n + }, f.confirm = function(e, t, i) { + return window.confirm(e) ? t() : null != i ? i() : void 0 + }, f.isValidFile = function(e, t) { + if (!t) return !0; + t = t.split(","); + var i = e.type, + n = i.replace(/\/.*$/, ""), + r = !0, + a = !1, + o = void 0; + try { + for (var l, s = t[Symbol.iterator](); !(r = (l = s.next()).done); r = !0) { + var u = l.value; + if ("." === (u = u.trim()).charAt(0)) { + if (-1 !== e.name.toLowerCase().indexOf(u.toLowerCase(), e.name.length - u.length)) return !0 + } else if (/\/\*$/.test(u)) { + if (n === u.replace(/\/.*$/, "")) return !0 + } else if (i === u) return !0 + } + } catch (e) { + a = !0, o = e + } finally { + try { + r || null == s.return || s.return() + } finally { + if (a) throw o + } + } + return !1 + }, "undefined" != typeof jQuery && null !== jQuery && (jQuery.fn.dropzone = function(e) { + return this.each((function() { + return new f(this, e) + })) + }), f.ADDED = "added", f.QUEUED = "queued", f.ACCEPTED = f.QUEUED, f.UPLOADING = "uploading", f.PROCESSING = f.UPLOADING, f.CANCELED = "canceled", f.ERROR = "error", f.SUCCESS = "success"; + var y = function(e, t, i, n, r, a, o, l, s, u) { + var c = function(e) { + e.naturalWidth; + var t = e.naturalHeight, + i = document.createElement("canvas"); + i.width = 1, i.height = t; + var n = i.getContext("2d"); + n.drawImage(e, 0, 0); + for (var r = n.getImageData(1, 0, 1, t).data, a = 0, o = t, l = t; l > a;) 0 === r[4 * (l - 1) + 3] ? o = l : a = l, l = o + a >> 1; + var s = l / t; + return 0 === s ? 1 : s + }(t); + return e.drawImage(t, i, n, r, a, o, l, s, u / c) + }, + g = function() { + "use strict"; + + function e() { + i(this, e) + } + return r(e, null, [{ + key: "initClass", + value: function() { + this.KEY_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" + } + }, { + key: "encode64", + value: function(e) { + for (var t = "", i = void 0, n = void 0, r = "", a = void 0, o = void 0, l = void 0, s = "", u = 0; a = (i = e[u++]) >> 2, o = (3 & i) << 4 | (n = e[u++]) >> 4, l = (15 & n) << 2 | (r = e[u++]) >> 6, s = 63 & r, isNaN(n) ? l = s = 64 : isNaN(r) && (s = 64), t = t + this.KEY_STR.charAt(a) + this.KEY_STR.charAt(o) + this.KEY_STR.charAt(l) + this.KEY_STR.charAt(s), i = n = r = "", a = o = l = s = "", u < e.length;); + return t + } + }, { + key: "restore", + value: function(e, t) { + if (!e.match("data:image/jpeg;base64,")) return t; + var i = this.decode64(e.replace("data:image/jpeg;base64,", "")), + n = this.slice2Segments(i), + r = this.exifManipulation(t, n); + return "data:image/jpeg;base64,".concat(this.encode64(r)) + } + }, { + key: "exifManipulation", + value: function(e, t) { + var i = this.getExifArray(t), + n = this.insertExif(e, i); + return new Uint8Array(n) + } + }, { + key: "getExifArray", + value: function(e) { + for (var t = void 0, i = 0; i < e.length;) { + if (255 === (t = e[i])[0] & 225 === t[1]) return t; + i++ + } + return [] + } + }, { + key: "insertExif", + value: function(e, t) { + var i = e.replace("data:image/jpeg;base64,", ""), + n = this.decode64(i), + r = n.indexOf(255, 3), + a = n.slice(0, r), + o = n.slice(r), + l = a; + return l = (l = l.concat(t)).concat(o) + } + }, { + key: "slice2Segments", + value: function(e) { + for (var t = 0, i = [];;) { + if (255 === e[t] & 218 === e[t + 1]) break; + if (255 === e[t] & 216 === e[t + 1]) t += 2; + else { + var n = t + (256 * e[t + 2] + e[t + 3]) + 2, + r = e.slice(t, n); + i.push(r), t = n + } + if (t > e.length) break + } + return i + } + }, { + key: "decode64", + value: function(e) { + var t = void 0, + i = void 0, + n = "", + r = void 0, + a = void 0, + o = "", + l = 0, + s = []; + for (/[^A-Za-z0-9\+\/\=]/g.exec(e) && console.warn("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."), e = e.replace(/[^A-Za-z0-9\+\/\=]/g, ""); t = this.KEY_STR.indexOf(e.charAt(l++)) << 2 | (r = this.KEY_STR.indexOf(e.charAt(l++))) >> 4, i = (15 & r) << 4 | (a = this.KEY_STR.indexOf(e.charAt(l++))) >> 2, n = (3 & a) << 6 | (o = this.KEY_STR.indexOf(e.charAt(l++))), s.push(t), 64 !== a && s.push(i), 64 !== o && s.push(n), t = i = n = "", r = a = o = "", l < e.length;); + return s + } + }]), e + }(); + g.initClass(); + window.Dropzone = f +}(); +//# sourceMappingURL=dropzone-min.js.map \ No newline at end of file diff --git a/skanhama/static/dropzone.css b/skanhama/static/dropzone.css new file mode 100644 index 0000000..b9c0814 --- /dev/null +++ b/skanhama/static/dropzone.css @@ -0,0 +1,265 @@ +@keyframes passing-through{ + 0%{ + opacity:0; + transform:translateY(40px) + } + 30%,70%{ + opacity:1; + transform:translateY(0px) + } + 100%{ + opacity:0; + transform:translateY(-40px) + } +} +@keyframes slide-in{ + 0%{ + opacity:0; + transform:translateY(40px) + } + 30%{ + opacity:1; + transform:translateY(0px) + } +} +@keyframes pulse{ + 0%{ + transform:scale(1) + } + 10%{ + transform:scale(1.1) + } + 20%{ + transform:scale(1) + } +} +.dropzone,.dropzone *{ + box-sizing:border-box +} +.dropzone{ + min-height:150px; + border:1px solid rgba(0,0,0,.8); + border-radius:5px; + padding:20px 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-message{ + text-align:center; + margin:2em 0 +} +.dropzone .dz-message .dz-button{ + background:none; + color:inherit; + border:none; + padding:0; + font:inherit; + cursor:pointer; + outline:inherit +} +.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:hover .dz-details{ + opacity:1 +} +.dropzone .dz-preview.dz-file-preview .dz-image{ + border-radius:20px; + background:#999; + background:linear-gradient(to bottom, #eee, #ddd) +} +.dropzone .dz-preview.dz-file-preview .dz-details{ + opacity:1 +} +.dropzone .dz-preview.dz-image-preview{ + background:#fff +} +.dropzone .dz-preview.dz-image-preview .dz-details{ + 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:hover .dz-details{ + opacity:1 +} +.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{ + transform:scale(1.05, 1.05); + 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{ + animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1) +} +.dropzone .dz-preview.dz-error .dz-error-mark{ + opacity:1; + animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1) +} +.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{ + pointer-events:none; + opacity:0; + z-index:500; + position:absolute; + display:block; + top:50%; + left:50%; + margin-left:-27px; + margin-top:-27px; + background:rgba(0,0,0,.8); + border-radius:50% +} +.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{ + display:block; + width:54px; + height:54px; + fill:#fff +} +.dropzone .dz-preview.dz-processing .dz-progress{ + opacity:1; + transition:all .2s linear +} +.dropzone .dz-preview.dz-complete .dz-progress{ + opacity:0; + transition:opacity .4s ease-in +} +.dropzone .dz-preview:not(.dz-processing) .dz-progress{ + animation:pulse 6s ease infinite +} +.dropzone .dz-preview .dz-progress{ + opacity:1; + z-index:1000; + pointer-events:none; + position:absolute; + height:20px; + top:50%; + margin-top:-10px; + left:15%; + right:15%; + border:3px solid rgba(0,0,0,.8); + background:rgba(0,0,0,.8); + border-radius:10px; + overflow:hidden +} +.dropzone .dz-preview .dz-progress .dz-upload{ + background:#fff; + display:block; + position:relative; + height:100%; + width:0; + transition:width 300ms ease-in-out; + border-radius:17px +} +.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; + transition:opacity .3s ease; + border-radius:8px; + font-size:13px; + top:130px; + left:-10px; + width:140px; + background:#b10606; + padding:.5em 1em; + 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 #b10606 +} +/*# sourceMappingURL=dropzone.css.map */ \ No newline at end of file diff --git a/skanhama/static/scripts.js b/skanhama/static/scripts.js index 3165577..f108f41 100644 --- a/skanhama/static/scripts.js +++ b/skanhama/static/scripts.js @@ -1,3 +1,4 @@ +// - Navbar Search - // // Handle opening and closing of the navbar search input // Opens on search icon click and closes when user presses outside the search input const navSearchIcon = document.getElementById("nav-search-icon"); @@ -22,6 +23,7 @@ const navSearchClickOut = (e) => { } }; +// - Account Menu - // // Handle opening and closing of the Account Menu // Opens on Account icon click and closes when user presses outside the menu const accountMenuIcon = document.getElementById("accountMenuIcon"); @@ -47,6 +49,7 @@ const accountMenuClickOut = (e) => { } } +// - Requirements Fields - // // Handle adding of Upload Requirements input fields // Adds a new div with inputs on icon click const reqsAddIcon = document.getElementById("icon-reqs-add"); @@ -90,6 +93,204 @@ function reqsRemoveInputFunction() { this.parentElement.remove(); } +// Printing the field input data to the form input +const reqsManual = document.getElementById("reqs-manual"); +const reqsFormControl = document.getElementById("requirements"); +if (reqsManual) { + reqsManual.addEventListener("click", reqsDataCompiler); +} +function reqsDataCompiler() { + let dict = {}; + const names = document.querySelectorAll(".fc-reqs-name"); + const links = document.querySelectorAll(".fc-reqs-link"); + const notes = document.querySelectorAll(".fc-reqs-note"); + for (let i = 0; i < names.length; i++){ + if (names[i].value !== "") { + dict[i] = [names[i].value, links[i].value, notes[i].value]; + } + } + reqsFormControl.textContent = JSON.stringify(dict); +} + +// Print on form submit +const submit = document.getElementById("submit"); +if (submit) { + reqsDataCompiler(); +} + +// - Dropzone.js Config - // +// Upload Dropzone +if (document.getElementById("dropzoneAnimation")) { + // Handle dismiss icon event on error display + const errorDismiss1 = document.getElementById("js-error-dismiss-file"); + const errorDismiss2 = document.getElementById("js-error-dismiss-banner"); + const errorDismiss3 = document.getElementById("js-error-dismiss-gallery"); + if (errorDismiss1) { + errorDismiss1.addEventListener("click", errorDismiss); + } + if (errorDismiss2) { + errorDismiss2.addEventListener("click", errorDismiss); + } + if (errorDismiss3) { + errorDismiss3.addEventListener("click", errorDismiss); + } + function errorDismiss() { + this.parentElement.classList.add("hidden"); + } + + // Options - Animation zip file + const dropzoneOptionsFile = { + dictDefaultMessage: 'Drop your file here or click to select.
Max file size: 40 MB. Accepted file format: .zip.', + paramName: "file", + maxFilesize: 40, // MB + acceptedFiles: ".zip", + addRemoveLinks: true, + dictRemoveFile: "Remove", + dictCancelUpload: "Cancel", + uploadMultiple: false, + maxFiles: 1, + url: "/upload", + init: function () { + let container = document.getElementById("js-error-file"); + let error = document.getElementById("js-error-file-text"); + let error_text = document.createTextNode(""); + this.on("addedfile", function (file) { + let _this = this; + console.log(file.name + " -> type: " + file.type); + if ($.inArray(file.type, ["application/x-zip-compressed", "application/zip"]) === -1) { + _this.removeFile(file); + container.classList.remove("hidden"); + error_text.data = "Incompatible file format. Please ensure that the file is a .zip file."; + error.appendChild(error_text); + console.log("error > incorrect file type for " + file.name) + } else if (this.files.length > 1) { + _this.removeFile(file); + console.log("error > too many files (" + this.files.length + ") reached when adding " + file.name) + error_text.data = "You have reached the maximum number of files and cannot upload any more." + error.appendChild(error_text); + container.classList.remove("hidden"); + } + }); + this.on("success", function (file) { + console.log("success > " + file.name); + if (!container.classList.contains("hidden")) { + error.removeChild(error_text); + container.classList.add("hidden"); + } + }); + }, + accept: function(file, done) { + return done(); + } + }; + // Options - Banner Image + const dropzoneOptionsBanner = { + dictDefaultMessage: 'Drop your banner image here or click to select.
Max file size: 2 MB. Accepted file formats: .jpg, .jpeg, .gif, .png.', + paramName: "file", + maxFilesize: 2, // MB + acceptedFiles: ".jpg, .gif, .png, .jpeg", + addRemoveLinks: true, + dictRemoveFile: "Remove", + dictCancelUpload: "Cancel", + uploadMultiple: false, + maxFiles: 1, + createImageThumbnails: true, + maxThumbnailFilesize: 2, + thumbnailWidth: 96, + thumbnailHeight: 96, + thumbnailMethod: "crop", + url: "/upload", + init: function () { + let container = document.getElementById("js-error-banner"); + let error = document.getElementById("js-error-banner-text"); + let error_text = document.createTextNode(""); + this.on("addedfile", function (file) { + let _this = this; + console.log(file.name + " -> type: " + file.type); + if ($.inArray(file.type, ["image/jpeg", "image/jpg", "image/png", "image/gif"]) === -1) { + _this.removeFile(file); + container.classList.remove("hidden"); + error_text.data = "Incompatible file format. Please ensure all files are either .jpg, .jpeg, .gif or .png."; + error.appendChild(error_text); + console.log("error > incorrect file type for " + file.name) + } else if (this.files.length > 1) { + _this.removeFile(file); + console.log("error > too many files (" + this.files.length + ") reached when adding " + file.name) + error_text.data = "You have reached the maximum number of files and cannot upload any more." + error.appendChild(error_text); + container.classList.remove("hidden"); + } + }); + this.on("success", function (file) { + console.log("success > " + file.name); + if (!container.classList.contains("hidden")) { + error.removeChild(error_text); + container.classList.add("hidden"); + } + }); + }, + accept: function(file, done) { + return done(); + } + }; + // Options - Gallery Images + const dropzoneOptionsGallery = { + dictDefaultMessage: 'Drop your gallery images here or click to select.
Max size per file: 4 MB. Accepted file formats: .jpg, .jpeg, .gif, .png.', + paramName: "file", + maxFilesize: 4, // MB + acceptedFiles: ".jpg, .gif, .png, .jpeg", + resizeWidth: 1080, + resizeQuality: 1, + addRemoveLinks: true, + dictRemoveFile: "Remove", + dictCancelUpload: "Cancel", + uploadMultiple: true, + maxFiles: 27, + createImageThumbnails: true, + maxThumbnailFilesize: 10, + thumbnailWidth: 96, + thumbnailHeight: 96, + thumbnailMethod: "crop", + url: "/upload", + init: function () { + let container = document.getElementById("js-error-gallery"); + let error = document.getElementById("js-error-gallery-text"); + let error_text = document.createTextNode(""); + this.on("addedfile", function (file) { + let _this = this; + console.log(file.name + " -> type: " + file.type); + if ($.inArray(file.type, ["image/jpeg", "image/jpg", "image/png", "image/gif"]) === -1) { + _this.removeFile(file); + container.classList.remove("hidden"); + error_text.data = "Incompatible file format. Please ensure all files are either .jpg, .jpeg, .gif or .png."; + error.appendChild(error_text); + console.log("error > incorrect file type for " + file.name) + } else if (this.files.length > 27) { + _this.removeFile(file); + console.log("error > too many files (" + this.files.length + ") reached when adding " + file.name) + error_text.data = "You have reached the maximum number of files and cannot upload any more." + error.appendChild(error_text); + container.classList.remove("hidden"); + } + }); + this.on("success", function (file) { + console.log("success > " + file.name); + if (!container.classList.contains("hidden")) { + error.removeChild(error_text); + container.classList.add("hidden"); + } + }); + }, + accept: function(file, done) { + return done(); + } + }; + const dzAnimations = new Dropzone("div#dropzoneAnimation", dropzoneOptionsFile); + const dzBanner = new Dropzone("div#dropzoneBanner", dropzoneOptionsBanner); + const dzGallery = new Dropzone("div#dropzoneGallery", dropzoneOptionsGallery); +} + + // Live (Active) Search function in the search bar in the nav menu // $(function (){ diff --git a/skanhama/static/style.css b/skanhama/static/style.css index b08e415..2a308d3 100644 --- a/skanhama/static/style.css +++ b/skanhama/static/style.css @@ -7,18 +7,18 @@ --container-body-margin: 0.5rem 2rem; --container-body-padding: 0.25rem; --row-gap: calc(10vmin + 5px); - /*--clr-AA: rgb(102, 252, 241);*/ - /*--clr-AB: rgb(69, 149, 143);*/ - /*--clr-BA: rgb(225, 153, 19);*/ - /*--clr-BB: rgb(141, 95, 34);*/ - /*--clr-CA: rgb(139, 57, 187);*/ - /*--clr-CB: rgb(69, 34, 111);*/ - --clr-AA: rgb(253, 41, 122); - --clr-AB: rgb(163, 47, 88); - --clr-CA: rgb(243, 165, 21); - --clr-CB: rgb(141, 95, 34); - --clr-BA: rgb(139, 57, 187); - --clr-BB: rgb(69, 34, 111); + --clr-AA: rgb(102, 252, 241); + --clr-AB: rgb(69, 149, 143); + --clr-BA: rgb(225, 153, 19); + --clr-BB: rgb(141, 95, 34); + --clr-CA: rgb(139, 57, 187); + --clr-CB: rgb(69, 34, 111); + /*--clr-AA: rgb(253, 41, 122);*/ + /*--clr-AB: rgb(163, 47, 88);*/ + /*--clr-CA: rgb(243, 165, 21);*/ + /*--clr-CB: rgb(141, 95, 34);*/ + /*--clr-BA: rgb(139, 57, 187);*/ + /*--clr-BB: rgb(69, 34, 111);*/ --clr-border-light: rgb(42, 48, 52); --clr-border-dark: rgb(15, 15, 23); @@ -40,8 +40,10 @@ --clr-bg-content-accent: rgb(33, 33, 36); --clr-bg-content-dark: rgb(29, 29, 31); --clr-bg-footer: var(--clr-bg-header); - --clr-bg-hover: rgb(43, 43, 49); - --clr-bg-btn: rgba(16, 16, 19, 0.4); + --clr-btn-hover: rgb(35, 35, 40); + --clr-btn-solid: rgba(16, 16, 19, 0.4); + --clr-bg-fc: rgba(52, 56, 68, 0.15); + --clr-bg-light: rgb(222, 225, 229); --size-h1: 1.3em; --size-h2: 1.2em; @@ -106,7 +108,7 @@ a:visited { ***********************************************************************/ /* Misc */ .hidden { - display: none; + display: none !important; } /* Main */ .main { @@ -393,7 +395,7 @@ input:-webkit-autofill:active{ font-family: 'Work Sans', sans-serif; font-weight: 400; color: var(--clr-text-light); - background: rgba(52, 56, 68, 0.15); + background: var(--clr-bg-fc); border: solid var(--clr-offsetA) 1px; } .form-control:focus { @@ -418,6 +420,7 @@ input:-webkit-autofill:active{ /* Upload Page Forms + */ /* Upload Divs */ .upload-content { @@ -506,7 +509,10 @@ input:-webkit-autofill:active{ flex-basis: 35%; } #upload-category-nsfw { - flex-basis: 35%; + flex-basis: 25%; +} +#upload-category-game { + flex-basis: 25%; } #upload-desc-summary { width: 50%; @@ -559,8 +565,115 @@ input:-webkit-autofill:active{ flex-basis: 40%; } +/* + Dropzone +*/ +.dropzone { + background-color: var(--clr-bg-fc); + border: solid var(--clr-offsetA) 1px !important; + border-radius: 0 !important; + padding: 0.5rem !important; + min-height: 175px !important; +} +.dz-image { + width: 6rem !important; + height: 6rem !important; + border-radius: 10px !important; +} +/* Filename, expands on hover */ +.dz-details { + padding: 1.25rem 0.75rem !important; +} +.dz-details span { + background-color: transparent !important; + border: none !important; + border-radius: 0 !important; + padding: 0 !important; +} +.dz-filename:hover span { + background-image: var(--gradient-B) !important; + border-radius: 5px !important; + padding: 0.25rem !important; + position: absolute; + left: 50%; + transform: translateX(-50%); +} +/* Remove button */ +.dz-remove { + width: inherit; + margin-top: 0.3rem; + border-radius: 5px; + text-decoration: none; + background-color: var(--clr-btn-solid); + color: white; + border: none !important; + line-height: 1rem !important; + padding: 0.25rem; +} +.dz-remove:hover { + background-image: var(--gradient-B) !important; + text-decoration: none !important; +} +/* Hint text */ +.dz-button { + font-size: 0.9rem !important; + line-height: 2rem !important; +} +/* Error message */ +.dz-error-message { + background: var(--clr-bg-light) !important; + color: var(--clr-text-dark) !important; +} +.dz-error-message:after { + border-bottom: 6px solid var(--clr-bg-light) !important; +} +/* Image previews */ +.dz-image-preview { + background: transparent !important; +} +/* + Javascript enforced error checking + +*/ +.js-upload-error { + display: flex; + flex-direction: row; + margin: 0.5rem 0 0 0; + padding: 0.25rem; + background-color: rgba(156, 41, 58, 0.9); + color: var(--clr-text-light); + font-size: 0.9rem; + width: max-content; + border-radius: 3px; +} +.js-error-dismiss { + font-size: 1rem; + padding: 0; + margin: 0 0.25rem 0 1rem; + cursor: pointer; +} +/* + Upload Submit Part + +*/ +#upload-submit-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 2rem 0; +} +#upload-submit-a { + flex-basis: 50%; +} +#upload-submit-b { + flex-basis: 50%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-evenly; +} /* Misc Forms @@ -782,7 +895,7 @@ Misc Forms border: solid 1px rgba(0, 0, 0, 0); } .dropdown-content a:hover { - background: var(--clr-bg-hover); + background: var(--clr-btn-hover); border: solid 1px; border-image: var(--gradient-A); } @@ -797,49 +910,51 @@ Misc Forms background: var(--clr-bg-footer); } -/* Buttons */ +/* + Buttons + +*/ .btn { width: 8rem; height: 2.4rem; display: inline-flex; justify-content: center; align-items: center; -} -.btn { cursor: pointer; text-decoration: none; color: var(--clr-text-light); font-size: var(--size-button); - background-color: var(--clr-bg-btn); - border: solid var(--clr-border-light) 1px; -} -.btn-clear { - background: none; + background-color: var(--clr-btn-solid); border: solid var(--clr-border-light) 1px; } .btn:hover { color: var(--clr-text-light) !important; - background: var(--clr-bg-hover); + background: var(--clr-btn-hover); border: solid 1px; border-image: var(--gradient-A); } .btn:visited { color: var(--clr-text-light); } +/* TODO: check if btn-solid is deprecated */ .btn-solid { color: var(--clr-text-light); - background: var(--clr-bg-btn); + background: var(--clr-btn-solid); border: solid var(--clr-AA) 1px; } .btn-solid:hover { color: var(--clr-text-light) !important; - background: var(--clr-bg-hover); + background: var(--clr-btn-hover); border: solid 1px; border-image: var(--gradient-A); } .btn-solid:visited { color: var(--clr-text-light); } +.btn-upload { + width: 12rem; + height: 3rem; +} @@ -868,7 +983,7 @@ Misc Forms transition: all 150ms ease; } .nav-sidebar a:hover { - background: var(--clr-bg-hover); + background: var(--clr-btn-hover); outline: none; border: solid var(--clr-bg-header) 1px; border-image: var(--gradient-A); diff --git a/skanhama/templates/layout.html b/skanhama/templates/layout.html index 8c99d0e..3696ee4 100644 --- a/skanhama/templates/layout.html +++ b/skanhama/templates/layout.html @@ -19,8 +19,11 @@ - - + + + {% block route_references %} + + {% endblock %}
@@ -89,11 +92,11 @@

{{ current_user.username }}

- + diff --git a/skanhama/templates/upload.html b/skanhama/templates/upload.html index 7dd7c38..6a59397 100644 --- a/skanhama/templates/upload.html +++ b/skanhama/templates/upload.html @@ -1,4 +1,9 @@ {% extends "layout.html" %} +{% block route_references %} + + + +{% endblock %} {% block content %}
@@ -78,6 +83,22 @@

Categorization

{{ form.category(class="form-control fc-upload fc-select") }} {% endif %}
+ +
+ {{ form.game.label(class="form-label fl-upload") }} + {% if form.game.errors %} + {{ form.game(class="form-control invalid-form") }} +
+ {% for error in form.game.errors %} + {{ error }} + {% endfor %} +
+ {% else %} +

Select the main base game your animation pack is compatible + with.

+ {{ form.game(class="form-control fc-upload fc-select") }} + {% endif %} +
{{ form.nsfw.label(class="form-label fl-upload") }} @@ -145,11 +166,11 @@

Requirements

Add Requirements

-

It is imperative that people know what animation files are required to - ensure that they can download your animation package. Once you have uploaded your animation - package all .hkx files (Skyrim animation files) are removed to ensure that no intellectual - property rights are infringed upon. You can read a more thorough description of why this - step is so important at LINK.

+

Please list all requirements needed to make your animation package + work. This includes both mods and animation files. Even if you only use one animation + from a larger set please list the full set as + a requirement. If you have animations released on for example a Patreon discord please + list that as well with a link to the Patreon subscription page.

Name

@@ -189,25 +210,93 @@

Note

{{ form.requirements(class="form-control fc-upload") }} {% endif %}
+
Update
+
+ + +
+
+

File Upload

+
+ +
+
+ {{ form.package.label() }} +

You can only upload one file. Please ensure that you zip the root + folder and that there are no other zipped files in the directory.

+
+
+ {% if form.package.errors %} + {% for error in form.package.errors %} + {{ error }}
+ {% endfor %} + {% endif %} + +
+
+
+ +
+
+

Media

+
+ +
+
+ {{ form.banner.label() }} +

Upload a banner image that will be displayed at the top of the + download page and as a thumbnail when browsing. Full size is 230x230 pixels.

+
+
+ {% if form.banner.errors %} + {% for error in form.banner.errors %} + {{ error }}
+ {% endfor %} + {% endif %} + +
+
+ +
+
+ {{ form.gallery.label() }} +

Upload additional images that will be added to a gallery that users + can view. You can only upload 20 images.

+
+
+ {% if form.gallery.errors %} + {% for error in form.gallery.errors %} + {{ error }}
+ {% endfor %} + {% endif %} + +
-
- -
- {{ form.package.label() }} - {{ form.package(class="form-control-file") }} - {% if form.package.errors %} - {% for error in form.package.errors %} - {{ error }}
- {% endfor %} - {% endif %} -
-
-
- {{ form.upload(class="btn btn-clear btn-upload") }} +
+
+

Finalize and Upload

+

Before pressing the "Upload" button make sure that you have followed the guidance provided in + the form. Your animation package and media files will be scanned and may be rejected if they do + not meet the guidelines. Are you sure you want to upload?

+
+
+
+ {{ form.upload(class="btn btn-upload") }} +
+