summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md301
-rw-r--r--Gruntfile.js6
-rw-r--r--css/pomf.css33
-rw-r--r--js/app.js170
-rw-r--r--js/cabinet.js66
-rw-r--r--js/cheesesteak.js232
-rw-r--r--js/pomf.js175
-rw-r--r--js/zepto.js1035
-rw-r--r--package.json3
-rw-r--r--pages/banners.swig5
-rw-r--r--pages/banners/donate.swig5
-rw-r--r--pages/banners/virusscans.swig5
-rw-r--r--pages/faq.swig2
-rw-r--r--pages/index.swig2
-rw-r--r--pages/layout.swig3
-rw-r--r--pages/nojs.swig2
-rw-r--r--schema.sql2
-rwxr-xr-xstatic/classes/Response.class.php99
-rwxr-xr-xstatic/classes/UploadException.class.php8
-rw-r--r--static/classes/UploadedFile.class.php18
-rw-r--r--static/includes/database.inc.php6
-rw-r--r--static/includes/settings.inc.php71
-rw-r--r--static/upload.php92
23 files changed, 753 insertions, 1588 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..05dd4c1
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,301 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## `2.1.0` - Unreleased
+
+### Added
+
+- `CHANGELOG.md`. (Juuso Lapinlampi)
+- HTML `<meta name="generator">` with Pomf version. Generated with grunt-swig.
+ (Juuso Lapinlampi)
+- PHPDoc DocBlocks. (Juuso Lapinlampi)
+
+### Changed
+
+- All client-side JavaScript replaced with more efficient, smaller vanilla
+ JavaScript. (Luminarys)
+- Luminarys added to `package.json` contributors. (Juuso Lapinlampi)
+- Missing essential PHP includes and classes are now errors (`require_once`)
+ instead of warnings (`include_once`). (Austin Gillmann)
+- Non-abuse contact email to <info@pantsu.cat>. (Juuso Lapinlampi)
+
+### Deprecated
+
+- Swig banners. Use Swig templates or an alternative. (Juuso Lapinlampi)
+- Unused file expiration (`expire`). This feature may be removed in the next
+ major version. (Juuso Lapinlampi)
+- `get_sha1()` function and `$sha1` variable. This API may be changed or removed
+ in the next major version, switching to SHA-256 hashes. (Juuso Lapinlampi)
+- `csv_error`, `csv_success`, `html_error`, `html_success`, `json_error`,
+ `json_success`, `text_error` and `text_success` functions. The functions will
+ be renamed to `camelCase` format (e.g. `csvError`, `htmlSuccess`). (Juuso
+ Lapinlampi)
+- Output buffering (`ob_start`). This feature may be removed in the next
+ major version. (Juuso Lapinlampi)
+
+### Fixed
+
+- Code cleanliness of `upload_file()` in `upload.php`. (Juuso Lapinlampi)
+
+## `2.0.3` - Unreleased
+
+### Fixed
+
+- Incorrectly referring to uploaded/published works as "content" in FAQ,
+ according to the GNU Project. (Juuso Lapinlampi)
+- An unusual case where an error response may return an invalid HTTP
+ status code when `PDO::ATTR_ERRMODE` is set to
+ `PDO::ERRMODE_EXCEPTION`. (Juuso Lapinlampi)
+
+## `2.0.2` - 2016-05-20
+
+### Changed
+
+- `npm bugs` URL replaced with an email address. (Juuso Lapinlampi)
+
+### Fixed
+
+- LibreJS `@source` URL to git.pantsu.cat's new cgit tree. (Juuso Lapinlampi)
+- `package.json` package name to lowercase as per NPM rules. (Juuso Lapinlampi)
+- Alphabetical order of grunt-swig options in `Gruntfile.js`. (Juuso Lapinlampi)
+- "Getting help" IRC contact for developers in `README.md`. (Juuso Lapinlampi)
+- Missing Bootstrap license header in `pomf.css`. (Juuso Lapinlampi)
+
+## `2.0.1` - 2016-01-28
+
+### Changed
+
+- Gyazo Client renamed to Gyazowin in tools page to match the actual project
+ name. (Juuso Lapinlampi)
+
+### Fixed
+
+- ShareX URL and settings in tools page. (Juuso Lapinlampi)
+- Gyazowin URL in tools page. (Juuso Lapinlampi)
+- PHPCS lint error about a missing newline after `text_error()`. (Juuso
+ Lapinlampi)
+- PHP warnings about undeclared variables in `html_success()` and
+ `text_success()`. (Juuso Lapinlampi)
+
+### Removed
+
+- Unused, commented `a:focus` `outline-offset` in CSS. (Luminarys)
+- Unused `check_fileapi.swig` inclusion in `nojs.swig`. (Juuso Lapinlampi)
+- Unused `span.old` and `section.ded` CSS rules leftover from 2.0.0 release.
+ (Eliot Whalan)
+
+## `2.0.0` - 2016-01-10
+
+### Added
+
+- "Other services" page for Pantsu.cat. (Eliot Whalan)
+- `lang` attributes for screen reader accessibility. (Eliot Whalan)
+- Configuration option `POMF_FILES_LENGTH` for number of random characters to
+ use in a generated filename. (Juuso Lapinlampi)
+- JavaScript-free upload. (Eliot Whalan)
+- HTML response API (for JavaScript-free upload). (Eliot Whalan)
+- Text response API. (Eliot Whalan)
+- limf and 1339secure to tools page. (Eliot Whalan, Juuso Lapinlampi)
+- LibreJS compatibility. (Eliot Whalan)
+- License headers in source code. (Juuso Lapinlampi)
+- Link to Pantsu.cat's Tor hidden service. (Eliot Whalan)
+- Credits, contributing, features, demo and getting help sections to
+ `README.md`. (Juuso Lapinlampi)
+- Git mailmap. (Juuso Lapinlampi)
+
+### Changed
+
+- Rebrand from Pomf.se to Pantsu.cat. (Eliot Whalan)
+- Viewports no longer have a maximum scale. (Eliot Whalan)
+- All tool configurations on tools page to use Pantsu.cat for uploading. (Eliot
+ Whalan)
+- "Autist neckbeard" warning on front page for users without JavaScript. (Eliot
+ Whalan)
+- Response Content-Type charset is now all uppercase (UTF-8). (Juuso Lapinlampi)
+- npm dependencies have been updated to latest versions as of release. Fixes
+ deprecation warnings. (Eliot Whalan)
+- npm dependencies accept caret (minor release) instead of tilde (patch
+ release). (Eliot Whalan)
+- Increase user-configured maximum upload size to 128MiB. (Eliot Whalan)
+- The upload button is now a `<button>` element instead of `<a>` hyperlink.
+ Improves semantics and accessibility. (Juuso Lapinlampi)
+- Banners are no longer displayed on the FAQ page. (Juuso Lapinlampi)
+- FAQ has been rewritten from scratch to explain what Pantsu.cat is and what its
+ policies are. Semantically correct and prettier. (Juuso Lapinlampi)
+
+### Deprecated
+
+- Gyazo response API. Use text response instead. (Juuso Lapinlampi)
+- JavaScript ES5 code. A future release will update the JavaScript source to
+ modern ES6. Browser compatibility may be affected. (Juuso Lapinlampi)
+
+### Removed
+
+- All unmaintained and dead tools from tools page. (Eliot Whalan)
+- Internet Explorer 8 quirks mode compatibility in uglified JavaScript. (Juuso
+ Lapinlampi)
+- Moe panel from core, including most of the code in core for it. It is now an
+ (unsupported) extension. (Eliot Whalan, Juuso Lapinlampi)
+- Unnecessary TODO section from `README.md`. (Harry H)
+- Code and images for non-free kawaii anime girls. The latter cannot be
+ redistributed with the source. (Juuso Lapinlampi)
+- Unused Grunt `cssmin` task's banner option. (Juuso Lapinlampi)
+- Grunt HTML minification task. (Juuso Lapinlampi)
+- Unused Grunt `mkdir` task. (Juuso Lapinlampi)
+- Unused "POCKY~" CSS style rules. (Juuso Lapinlampi)
+- Unused, commented Mozilla file input JavaScript code. (Juuso Lapinlampi)
+
+### Fixed
+
+- Missing whitespace in upload button label. (Eliot Whalan)
+- Favicon HTML markup now uses modern `rel="icon"` markup. (Eliot Whalan)
+- Refactored `generate_name()` for bug fixing. (Juuso Lapinlampi)
+- Use UNIX socket in `POMF_DB_CONN` configuration option for faster database
+ queries. (Juuso Lapinlampi)
+- Format all PHP code to PSR-2 style guide, fixes lint errors. (Juuso
+ Lapinlampi)
+- Decode `&hellip;` correctly when the file is too big. (Juuso Lapinlampi)
+- Remove other `&hellip;` decoding hack in `case 413`. (Juuso Lapinlampi)
+- Whitespace in Swig templates, PHP and JavaScript. (Juuso Lapinlampi)
+- `README.md` header outline, fixes accessibility. (Juuso Lapinlampi)
+- Add missing authors to `LICENSE` and other files. (Juuso Lapinlampi)
+- Format and lint all JavaScript code to Airbnb ES5 style. About 350 lint
+ errors found, each carefully fixed by hand. (Juuso Lapinlampi)
+- Ambiguity in choice of words for licensing (MIT to Expat). No actual change
+ of license. (Eliot Whalan)
+- Use strict PHP comparison for upload `$tries`. (Juuso Lapinlampi)
+- Remove multiple instances of code duplication in JavaScript. (Juuso
+ Lapinlampi)
+- Virus scan banner is now informative (`.alert-info`), not an error
+ (`.alert-error`). (Juuso Lapinlampi)
+- Reduced `npm install` time from 15-30 seconds to mere ~5 seconds (on modern
+ Intel i7-3770K machine), resulting in faster builds. (Juuso Lapinlampi)
+- Minor `README.md` documentation errors. (Juuso Lapinlampi)
+- WCAG 2.0 contrast (section 1.4.3) in `.alert-info`. (Juuso Lapinlampi)
+- Capitalization of "ShareX" heading on tools page. (Juuso Lapinlampi)
+
+## `1.0.0+8757e9a` - 2015-08-15
+
+Last Git repository snapshot in `nokonoko/Pomf` before forking to `pantsu/pomf`.
+No formal release.
+
+### Added
+
+- Gyazo and CSV response types. (Peter Lejeck)
+- Flattr to donation banner. (Peter Lejeck)
+- Label and message to Bitcoin URL in donation banner. (Peter Lejeck)
+- CSS for donation buttons. (Peter Lejeck)
+- Moe panel for login/administration. (Eric Johansson)
+- Alternative email contact for file deletion in FAQ. (Eric Johansson)
+- CSS for old and dead tools. (Peter Lejeck)
+- Double dot file extensions support. Configurable in `settings.inc.php`. (Peter
+ Lejeck)
+- Disclaimer about unsupported web browsers in FAQ. (Eric Johansson)
+- Initial file expiration support for moe panel. (Eric Johansson)
+- MySQL schema (`schema.sql`) for installing Pomf. (Eric Johansson, cenci0)
+- MySQL schema (`update.sql`) to help migrate old databases to the new moe panel
+ schema. (cenci0, Austin Gillman)
+- HTTP compression support in `upload.php`. (cenci0)
+- Configuration option `POMF_URL`. (cenci0)
+- `diverse_array` and `refiles` API in `upload.php`. (cenci0)
+- HTML minification using grunt-contrib-htmlmin. (cenci0)
+- Instructions in `README.md` on how to enable compression in Apache webserver.
+ (cenci0)
+- Todo section to `README.md`. (Eric Johansson)
+- Advice to disable PHP execution for uploaded `.php` files. (Eric Johansson)
+- Configurable `{{max_upload_size`}} option in `Gruntfile.js` to replace
+ hardcoded 50MiB value. Defaults to value 50. (Kieran Harkin)
+
+### Changed
+
+- `POMF_DB_CONN` default host to `127.0.0.1`. (Eric Johansson)
+- Paddings on `.alert` boxes simplified. (Peter Lejeck)
+- Donation plea. (Peter Lejeck)
+- Donate buttons are unified and on a single row. (Peter Lejeck)
+- PayPal's encrypted `<form>` replaced with a link in donation banner. (Peter
+ Lejeck)
+- Virus scans banner to match the donate banner scheme.
+- Donate banner is now an `.alert-info` instead of `.alert-error`. (Peter
+ Lejeck)
+- `generate_name` algorithm. Generates random lowercase letters from `a` to `z`
+ instead of a mix of CRC-32B and random lowercase letters. (Eric Johansson)
+- Tools page tool names and API status updated. (Eric Johansson, Peter Lejeck)
+- Gyazo `generate_name` algorithm to `FxxAxxG.png`, where `x` is a random
+ lowercase letter. Removes CRC-32B checksums and `$grill` parameter from the
+ function. (Eric Johansson)
+- Increased length of `generate_name` return string by one character. (Eric
+ Johansson)
+- Default `POMF_FILES_RETRIES` increased from 5 to 15. (Eric Johansson, Peter
+ Lejeck)
+- `nav.swig` links to new places of interest of other Pomf.se projects and
+ social media. (Eric Johansson)
+- Genericize defaults function into merge utility in `cheesesteak.js`. (Peter
+ Lejeck)
+- `README.md` now uses a hyperlink to `@nekunekus` Twitter account. (Eric
+ Johansson)
+- Refactored the core PHP code in Pomf. Introduced classes. (cenci0)
+- `.alert-info`, `.alert-error`, `nav a` and `a` CSS colors to satisfy WCAG 2.0
+ requirements on contrast (section 1.4.3). Patch by Juuso Lapinlampi. (Eric
+ Johansson)
+
+### Fixed
+
+- `git clone` URL in `README.md`. (Eric Johansson)
+- PHPDoc comments in `upload.php`. (Peter Lejeck)
+- A bug in `refiles()`. Deferences `$file`. (Peter Lejeck, catboy)
+- Broken tools download URLs. (Eric Johansson)
+- Uploading files without file extension. (Eric Johansson)
+- Bitcoin wallet URI in donation banner. (Eric Johansson)
+- ShareX settings URL in tools. (Eric Johansson)
+- Gyazo Client author's Twitter URL. (Eric Johansson)
+- Code indentation in `upload.php`. (Peter Lejeck)
+- Reduntant `settings.inc.php` inclusion twice in `upload.php`. (benwaffle)
+- Moe panel incorrectly returning 5 entries instead of none in search while
+ there's no search query. (benwaffle)
+- Cross-site scripting vulnerability in moe panel from arbitrary `originalname`.
+ No CVE requested. Reported by Juuso Lapinlampi. (Eric Johansson)
+- Copy-pasted code rolled into a `for` loop in `generate_name`. (Michael
+ "wafflestealer654")
+- HTML validation for `case 413` in `pomf.js`. (cenci0)
+- `README.md` title from Pomf.se to Pomf. (Eric Johansson)
+- Pass full URL back to the client in response. (Kieran Harkin)
+
+### Removed
+
+- Link to mail newsletter in FAQ. (Eric Johansson)
+- Unused `blackniggers/kittens.php`. Previously used for Gyazo client, now
+ replaced. (Eric Johansson)
+- Unused `get_crc32` function in `UploadedFile.class.php`. Reported by Juuso
+ Lapinlampi. (Eric Johansson)
+
+## `v1.0.0` - 2013-10-31
+
+Initial unofficial release in `nokonoko/Pomf`. Changes are since initial commit.
+
+### Added
+
+- `LICENSE` with Expat license. (Eric Johansson)
+- Email and Twitter contacts to `README.md`. (Eric Johansson)
+- Frequently asked questions (FAQ). (Eric Johansson)
+- Favicon. (Eric Johansson)
+- Kawaii anime girls (`grill.php`). (Eric Johansson)
+- Web interface with JavaScript. (Eric Johansson, Peter Lejeck)
+- Tools page. (Eric Johansson)
+- `upload.php`, `UploadedFile.class.php`, `database.inc.php`,
+ `settings.inc.php`. (Eric Johansson, Peter Lejeck)
+- Gyazo support (`blackniggers/kittens.php`) with `generate_name` function.
+ (Eric Johansson)
+- Grunt buildsystem. (Peter Lejeck)
+- Swig pages. (Peter Lejeck)
+- Swig banners. (Peter Lejeck)
+- `README.md` documentation. (Peter Lejeck)
+
+### Changed
+
+- `.gitignore` will only ignore `dist/` and `node_modules`. (Peter Lejeck)
+
+### Removed
+
+- `.gitattributes`. (Eric Johansson)
diff --git a/Gruntfile.js b/Gruntfile.js
index 9e95c5b..488e5b7 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -11,6 +11,7 @@ module.exports = function (grunt) {
generateRobotstxt: false,
generateSitemap: false,
max_upload_size: 128,
+ pkgVersion: '<%= pkg.version %>',
production: false,
siteUrl: 'https://pantsu.cat/',
src: [
@@ -33,10 +34,7 @@ module.exports = function (grunt) {
dist: {
files: {
'dist/pomf.min.js': [
- 'js/zepto.js',
- 'js/cheesesteak.js',
- 'js/cabinet.js',
- 'js/pomf.js'
+ 'js/app.js'
]
}
}
diff --git a/css/pomf.css b/css/pomf.css
index 66fa382..676845e 100644
--- a/css/pomf.css
+++ b/css/pomf.css
@@ -239,7 +239,7 @@ nav > ul > li:last-child:after {
.error#upload-filelist {
color: #891A18;
}
-.error#upload-filelist .file-size {
+.error#upload-filelist .progress-percent {
color: #B94A48;
}
.error#upload-filelist .file-progress {
@@ -261,13 +261,12 @@ nav > ul > li:last-child:after {
text-overflow: ellipsis;
white-space: nowrap;
}
-.file-size,
+.file-progress,
.file-url {
display: inline-block;
float: right;
font-size: 0.9em;
margin-left: 8px;
- color: #5C5C5C;
vertical-align: middle;
}
.file-url a {
@@ -276,9 +275,29 @@ nav > ul > li:last-child:after {
.file-url a:hover {
color: #1C1C1C;
}
+.progress-percent {
+ float: right;
+}
+
+progress[value] {
+ /* Reset the default appearance */
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+
+ border: none;
+
+}
+
+progress[value]::-webkit-progress-bar {
+ background-color: #eee;
+ border-radius: 2ps;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.25) inset;
+}
+
/* completed rows */
.completed .file-progress,
-.completed .file-size {
+.completed .progress-percent {
display: none;
}
.completed .file-url {
@@ -341,14 +360,8 @@ nav > ul > li:last-child:after {
margin-bottom: 12px;
}
.file-progress {
- float: left;
width: 70%;
}
- .file-size {
- float: left;
- max-width: 30%;
- overflow: hidden;
- }
.file-name, .file-url {
width: 100%;
max-width: 100%;
diff --git a/js/app.js b/js/app.js
new file mode 100644
index 0000000..5b29a63
--- /dev/null
+++ b/js/app.js
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2016 Luminarys <postmaster@gensok.io>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+function addRow(file) {
+ var row = document.createElement('li');
+
+ var name = document.createElement('span');
+ name.textContent = file.name;
+ name.className = 'file-name';
+
+ var progressIndicator = document.createElement('span');
+ progressIndicator.className = 'progress-percent';
+ progressIndicator.textContent = '0%';
+
+ var progressBar = document.createElement('progress');
+ progressBar.className = 'file-progress';
+ progressBar.setAttribute("max", "100");
+ progressBar.setAttribute("value", "0");
+
+ row.appendChild(name);
+ row.appendChild(progressBar);
+ row.appendChild(progressIndicator);
+
+ document.getElementById('upload-filelist').appendChild(row);
+ return row;
+}
+
+function handleUploadProgress(evt) {
+ var xhr = evt.target;
+ var bar = xhr.bar;
+ var percentIndicator = xhr.percent;
+
+ if (evt.lengthComputable) {
+ var progressPercent = Math.floor((evt.loaded / evt.total) * 100);
+ bar.setAttribute("value", progressPercent);
+ percentIndicator.textContent = progressPercent + '%';
+ }
+}
+
+function handleUploadComplete(evt) {
+ var xhr = evt.target;
+ var bar = xhr.bar;
+ var row = xhr.row;
+ var percentIndicator = xhr.percent;
+
+ percentIndicator.style.visibility = "hidden";
+ bar.style.visibility = "hidden";
+ row.removeChild(bar);
+ row.removeChild(percentIndicator);
+ var respStatus = xhr.status;
+
+ var url = document.createElement('span');
+ url.className = 'file-url';
+ row.appendChild(url);
+
+ var link = document.createElement('a');
+ if (respStatus === 200) {
+ var response = JSON.parse(xhr.responseText);
+ if (response.success) {
+ link.textContent = response.files[0].url.replace(/.*?:\/\//g, "");
+ link.href = response.files[0].url;
+ url.appendChild(link);
+ } else {
+ bar.innerHTML = 'Error: ' + response.reason;
+ }
+ } else if (respStatus === 413) {
+ link.textContent = 'File Too big!';
+ url.appendChild(link);
+ } else {
+ link.textContent = 'Server error!';
+ url.appendChild(link);
+ }
+}
+
+function uploadFile(file, row) {
+ var bar = row.querySelector('.file-progress');
+ var percentIndicator = row.querySelector('.progress-percent');
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/upload.php');
+
+ xhr["row"] = row;
+ xhr["bar"] = bar;
+ xhr["percent"] = percentIndicator;
+ xhr.upload["bar"] = bar;
+ xhr.upload["percent"] = percentIndicator;
+
+ xhr.addEventListener('load', handleUploadComplete, false);
+ xhr.upload.onprogress = handleUploadProgress;
+
+ var form = new FormData();
+ form.append('files[]', file);
+ xhr.send(form);
+}
+
+function stopDefaultEvent(evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+}
+
+function handleDrag(state, element, evt) {
+ stopDefaultEvent(evt);
+ if (state.dragCount == 1) {
+ element.textContent = "Drop it here~";
+ }
+ state.dragCount += 1;
+}
+
+function handleDragAway(state, element, evt) {
+ stopDefaultEvent(evt);
+ state.dragCount -= 1;
+ if (state.dragCount == 0) {
+ element.textContent = "Select or drop file(s)";
+ }
+}
+
+function handleDragDrop(state, element, evt) {
+ stopDefaultEvent(evt);
+ handleDragAway(state, element, evt);
+ var len = evt.dataTransfer.files.length;
+ for (var i = 0; i < len; i++) {
+ var file = evt.dataTransfer.files[i];
+ var row = addRow(file);
+ uploadFile(file, row);
+ }
+}
+
+function uploadFiles(evt) {
+ var len = evt.target.files.length;
+ for (var i = 0; i < len; i++) {
+ var file = evt.target.files[i];
+ var row = addRow(file);
+ uploadFile(file, row);
+ }
+}
+
+function selectFiles(target, evt) {
+ stopDefaultEvent(evt);
+ target.click();
+}
+
+var state = { dragCount: 0 };
+var uploadButton = document.getElementById('upload-btn');
+window.addEventListener('dragenter', handleDrag.bind(this, state, uploadButton), false);
+window.addEventListener('dragleave', handleDragAway.bind(this, state, uploadButton), false);
+window.addEventListener('drop', handleDragAway.bind(this, state, uploadButton), false);
+window.addEventListener('dragover', stopDefaultEvent, false);
+
+var uploadInput = document.getElementById('upload-input');
+uploadInput.addEventListener('change', uploadFiles);
+uploadButton.addEventListener('click', selectFiles.bind(this, uploadInput));
+uploadButton.addEventListener('drop', handleDragDrop.bind(this, state, uploadButton), false);
diff --git a/js/cabinet.js b/js/cabinet.js
deleted file mode 100644
index ba28a33..0000000
--- a/js/cabinet.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-(function(cabinet) {
- cabinet.hasFileAPI = function hasFileAPI() {
- return Boolean(window.FormData);
- };
-
- $.fn.cabinet = function(uploadInput) {
- var $input = $(uploadInput);
-
- var _this = this;
- var passthrough = function(outer, inner, prevent, fn) {
- _this.on(outer, function(evt) {
- if (inner === 'click') {
- $input[0].click();
- } else {
- $input.trigger(inner);
- }
-
- if (prevent) {
- evt.preventDefault();
- }
-
- if (fn) {
- fn(evt);
- }
- }, false);
- };
-
- $input[0].filelist = Object.create(FileList);
-
- $input.on('change', function(evt) {
- this.filelist = evt.target.files;
- _this.change();
- });
-
- passthrough('dragenter', 'dragenter', true);
- passthrough('dragover', 'dragover', true);
- passthrough('dragleave', 'dragleave', true);
- passthrough('click', 'click', false);
- passthrough('drop', 'dragleave', true, function(evt) {
- $input[0].filelist = evt.dataTransfer.files;
- _this.change();
- });
- };
-})($);
diff --git a/js/cheesesteak.js b/js/cheesesteak.js
deleted file mode 100644
index 6e20880..0000000
--- a/js/cheesesteak.js
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-(function() {
- // Lightweight EventEmitter implementation
- var EventEmitter = function() {};
-
- // Utility to convert bytes into human units
- var humanSize = {
- get: function humanSize() {
- var units = [
- 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB',
- ];
- var base = Math.floor(Math.log(this.size) / Math.log(1024));
- return (this.size / Math.pow(1024, base)).toFixed(2) + ' ' + units[base];
- },
- };
-
- var percentUploaded = {
- get: function percentUploaded() {
- return this.uploadedSize / this.size;
- },
- };
-
- // b gets merged as defaults for a
- var merge = function merge(base, overlay) {
- var out = base;
- var key;
-
- for (key in overlay) {
- if (typeof overlay[key] === 'object') {
- out[key] = merge(base[key], overlay[key]);
- } else {
- out[key] = overlay[key];
- }
- }
-
- return out;
- };
-
- var FileListUploader = function(url, files, opts) {
- var request = opts || {};
-
- this.url = url;
- this.files = files;
- this.opts = merge({
- field: 'files[]',
- method: 'POST',
- data: {},
- }, request);
- };
-
- EventEmitter.prototype.on = function(evt, fn) {
- this._events = this._events || {};
- this._events[evt] = this._events[evt] || [];
- this._events[evt].push(fn);
- };
-
- EventEmitter.prototype.off = function(evt, fn) {
- if (!this.hasOwnProperty('_events') || evt in this._events === false) {
- return;
- }
-
- this._events[evt].splice(this._events[evt].indexOf(fn), 1);
- };
-
- EventEmitter.prototype.emit = function(evt) {
- var i;
- var len;
-
- // Check whether the event we want to emit actually exists
- if (!this.hasOwnProperty('_events') || evt in this._events === false) {
- return;
- }
-
- len = this._events[evt].length;
-
- // Call all functions that are bound to the event
- for (i = 0; i < len; i++) {
- this._events[evt][i].apply(this, Array.prototype.slice.call(arguments,
- 1));
- }
- };
-
- // Copy the stuff from Array to FileList
- FileList.prototype.forEach = Array.prototype.forEach;
- FileList.prototype.every = Array.prototype.every;
- FileList.prototype.some = Array.prototype.some;
- FileList.prototype.filter = Array.prototype.filter;
- FileList.prototype.map = Array.prototype.map;
- FileList.prototype.reduce = Array.prototype.reduce;
- FileList.prototype.reduceRight = Array.prototype.reduceRight;
-
- Object.defineProperty(FileList.prototype, 'size', {
- get: function getSize() {
- return this.reduce(function(prev, curr) {
- return prev + curr.size;
- }, 0);
- },
- });
-
- Object.defineProperty(FileList.prototype, 'humanSize', humanSize);
- Object.defineProperty(File.prototype, 'humanSize', humanSize);
-
- Object.defineProperty(FileList.prototype, 'percentUploaded',
- percentUploaded);
- Object.defineProperty(File.prototype, 'percentUploaded', percentUploaded);
-
- Object.defineProperty(FileList.prototype, 'uploadedSize', {
- get: function getUploadedSize() {
- return this.reduce(function(prev, curr) {
- return prev + (curr.uploadedSize || 0);
- }, 0);
- },
- });
-
- // Object URL stuff
- Object.defineProperty(File.prototype, 'url', {
- get: function getURL() {
- return window.URL.createObjectURL(this);
- },
- });
- File.revokeURL = function revokeURL(url) {
- window.URL.revokeObjectURL(url);
- };
-
- FileListUploader.prototype = Object.create(EventEmitter.prototype);
- FileListUploader.prototype.upload = function(cb) {
- var opts = this.opts;
- var files = this.files;
- var _this = this;
- var len = files.length;
- var data = new FormData();
- var xhr = new XMLHttpRequest();
- var i;
-
- if (cb) {
- this.on('uploadcomplete', cb);
- }
-
- files.forEach(function(file) {
- data.append(opts.field, file);
- });
-
- xhr.open(opts.method, this.url, true);
-
- function initProgressBar() {
- for (i = 0; i < len; i++) {
- files[i].uploadedSize = 0;
- }
- }
-
- xhr.upload.addEventListener('loadstart', initProgressBar);
-
- function updateProgressBar(evt) {
- var size;
-
- if (evt.lengthComputable) {
- size = evt.loaded;
-
- /**
- * We know the size of the files, the order they're in, and how
- * much we've uploaded. Based on this data, we can do some magic
- * to figure out which file we're on, and how much we've uploaded
- * of that file.
- */
-
- // TODO: This math has trouble on later uploads. Leak somewhere.
- for (i = 0; i < len; i++) {
- files[i].uploadedSize = Math.min(size, files[i].size);
- size -= files[i].uploadedSize;
- if (size <= 0) {
- files.current = files[i];
- size = 0;
- }
- }
- }
-
- _this.emit('uploadprogress', evt, files);
- }
-
- xhr.upload.addEventListener('progress', updateProgressBar, false);
-
- // The upload is complete, now tell the user to wait for URLs.
- function postUpload(evt) {
- _this.emit('uploadcomplete', evt);
- }
-
- xhr.upload.addEventListener('load', postUpload);
-
- // Tell the browser the upload completed and wait for response.
- function postProgress(evt) {
- _this.emit('progress', evt);
- }
-
- xhr.addEventListener('progress', postProgress);
-
- // Send a success/error response. Nothing more to do.
- function uploadFinished(evt) {
- _this.emit('load', evt, xhr.responseText);
- }
-
- xhr.addEventListener('load', uploadFinished);
-
- xhr.send(data);
-
- // TODO: Add more event passthroughs, maybe abstract it into a helper?
- };
-
- FileList.prototype.upload = function uploadFileList(url, opts) {
- return new FileListUploader(url, this, opts);
- };
-})();
diff --git a/js/pomf.js b/js/pomf.js
deleted file mode 100644
index cf91e35..0000000
--- a/js/pomf.js
+++ /dev/null
@@ -1,175 +0,0 @@
-/**
- * Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com>
- * Copyright (c) 2015 the Pantsu.cat developers <hostmaster@pantsu.cat>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-$(function() {
- var $uploadInput = $('#upload-input');
- var $uploadBtn = $('#upload-btn');
- var $uploadFiles = $('#upload-filelist');
-
- var $btnContent = '';
-
- var MAX_SIZE = (function(node) {
- var max = node.attr('data-max-size') || '10MiB';
- var num = parseInt(/([0-9,]+).*/.exec(max)[1].replace(',', ''), 10);
- var unit = /(?:([KMGTPEZY])(i)?B|([BKMGTPEZY]))/.exec(max) || ['B', '', ''];
-
- var oneUnit = Math.pow(
- (unit[2] === 'i' ? 1024 : 1000),
- 'BKMGTPEZY'.indexOf(unit[1])
- );
-
- return num * oneUnit;
- })($uploadInput);
-
- var createRow = function(filename, size, extra) {
- var $rowItem = $('<li class=file>');
- var $rowName = $('<span class=file-name>');
- var $rowProg = $('<div class="file-progress progress-outer">');
- var $rowSize = $('<span class=file-size>');
- var $rowUrl = $('<span class=file-url>');
-
- $rowItem.addClass(extra || '');
-
- $('<div class=progress-inner>').appendTo($rowProg);
-
- $rowItem.attr('data-filename', escape(filename));
- $rowName.text(filename);
- $rowSize.text(size);
-
- $rowItem.append($rowName, $rowProg, $rowSize, $rowUrl);
-
- return $rowItem;
- };
-
- if (!$.hasFileAPI()) {
- $('#no-file-api').show();
- $uploadBtn.hide();
- }
-
- $uploadBtn.cabinet($uploadInput);
-
- $uploadBtn.on('dragenter', function(evt) {
- if (this === evt.target) {
- $(this).addClass('drop');
- $btnContent = $(this).html();
- $(this).html('Drop it here~');
- }
- });
-
- $uploadBtn.on('drop', function() {
- $(this).trigger('dragleave');
- });
-
- $uploadBtn.on('dragleave', function(evt) {
- var node = evt.target;
- do {
- if (node === this) {
- $(this).removeClass('drop');
- $(this).html($btnContent);
- break;
- }
- } while (node === node.parentNode);
- });
-
- $uploadBtn.on('change', function() {
- var files = $uploadInput[0].filelist;
- var totalRow = createRow('', files.humanSize, 'total');
- var $totalName = $('.file-name', totalRow);
- var UPLOAD_ERR_MAX_SIZE = 'onii-chan y-your upload is t-too big&hellip;';
- var UPLOAD_ERR_FAILED = 'Something went wrong; try again later.';
- var up = files.upload('upload.php');
-
- var eachRow = function(_files, fn) {
- var hits = {};
- _files.forEach(function(file) {
- var row = $($('li[data-filename="' +
- escape(file.name) + '"]')[hits[file.name] || 0]);
- ++hits[file.name];
- fn.call(row, row, file, _files);
- });
- };
-
- $uploadFiles.empty().removeClass('error completed');
-
- files.forEach(function(file) {
- createRow(file.name, file.humanSize).appendTo($uploadFiles);
- });
-
- totalRow.appendTo($uploadFiles);
-
- if (files.size > MAX_SIZE) {
- $uploadFiles.addClass('error');
- $totalName.html(UPLOAD_ERR_MAX_SIZE);
- return;
- }
-
- up.on('uploadprogress', function(evt, _files) {
- eachRow(_files, function(row, file) {
- $('.progress-inner', row).width((file.percentUploaded * 100) + '%');
- });
-
- $('.progress-inner', totalRow).width((_files.percentUploaded * 100) + '%');
- });
-
- up.on('uploadcomplete', function() {
- $('.progress-inner').width('100%');
- $totalName.html('Grabbing URLs&hellip;');
- });
-
- up.on('load', function(evt, response) {
- var res = JSON.parse(response);
-
- switch (evt.target.status) {
- case 200:
- if (!res.success) {
- $uploadFiles.addClass('error');
- $totalName.text(UPLOAD_ERR_FAILED);
- break;
- }
-
- eachRow(res.files, function(row, file) {
- var $link = $('<a>');
-
- $link.attr('href', file.url)
- .attr('target', '_BLANK')
- .text(file.url.replace('http://', '').replace('https://', ''));
-
- $('.file-url', row).append($link);
- });
-
- $uploadFiles.addClass('completed');
- $totalName.text('Done!');
- break;
- case 413:
- $uploadFiles.addClass('error completed');
- $totalName.html(UPLOAD_ERR_MAX_SIZE);
- break;
- default:
- $uploadFiles.addClass('error completed');
- $totalName.text(UPLOAD_ERR_FAILED);
- }
- });
-
- up.upload();
- });
-});
diff --git a/js/zepto.js b/js/zepto.js
deleted file mode 100644
index ab89703..0000000
--- a/js/zepto.js
+++ /dev/null
@@ -1,1035 +0,0 @@
-/* Zepto v1.0-7-g579f376 - zepto event - zeptojs.com/license */
-
-
-var Zepto = (function() {
- var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
- document = window.document,
- elementDisplay = {}, classCache = {},
- getComputedStyle = document.defaultView.getComputedStyle,
- cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
- fragmentRE = /^\s*<(\w+|!)[^>]*>/,
- tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
- rootNodeRE = /^(?:body|html)$/i,
-
- // special attributes that should be get/set via method calls
- methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
-
- adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
- table = document.createElement('table'),
- tableRow = document.createElement('tr'),
- containers = {
- 'tr': document.createElement('tbody'),
- 'tbody': table, 'thead': table, 'tfoot': table,
- 'td': tableRow, 'th': tableRow,
- '*': document.createElement('div')
- },
- readyRE = /complete|loaded|interactive/,
- classSelectorRE = /^\.([\w-]+)$/,
- idSelectorRE = /^#([\w-]*)$/,
- tagSelectorRE = /^[\w-]+$/,
- class2type = {},
- toString = class2type.toString,
- zepto = {},
- camelize, uniq,
- tempParent = document.createElement('div')
-
- zepto.matches = function(element, selector) {
- if (!element || element.nodeType !== 1) return false
- var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
- element.oMatchesSelector || element.matchesSelector
- if (matchesSelector) return matchesSelector.call(element, selector)
- // fall back to performing a selector:
- var match, parent = element.parentNode, temp = !parent
- if (temp) (parent = tempParent).appendChild(element)
- match = ~zepto.qsa(parent, selector).indexOf(element)
- temp && tempParent.removeChild(element)
- return match
- }
-
- function type(obj) {
- return obj == null ? String(obj) :
- class2type[toString.call(obj)] || "object"
- }
-
- function isFunction(value) { return type(value) == "function" }
- function isWindow(obj) { return obj != null && obj == obj.window }
- function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }
- function isObject(obj) { return type(obj) == "object" }
- function isPlainObject(obj) {
- return isObject(obj) && !isWindow(obj) && obj.__proto__ == Object.prototype
- }
- function isArray(value) { return value instanceof Array }
- function likeArray(obj) { return typeof obj.length == 'number' }
-
- function compact(array) { return filter.call(array, function(item){ return item != null }) }
- function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }
- camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
- function dasherize(str) {
- return str.replace(/::/g, '/')
- .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
- .replace(/([a-z\d])([A-Z])/g, '$1_$2')
- .replace(/_/g, '-')
- .toLowerCase()
- }
- uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) }
-
- function classRE(name) {
- return name in classCache ?
- classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))
- }
-
- function maybeAddPx(name, value) {
- return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
- }
-
- function defaultDisplay(nodeName) {
- var element, display
- if (!elementDisplay[nodeName]) {
- element = document.createElement(nodeName)
- document.body.appendChild(element)
- display = getComputedStyle(element, '').getPropertyValue("display")
- element.parentNode.removeChild(element)
- display == "none" && (display = "block")
- elementDisplay[nodeName] = display
- }
- return elementDisplay[nodeName]
- }
-
- function children(element) {
- return 'children' in element ?
- slice.call(element.children) :
- $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node })
- }
-
- // `$.zepto.fragment` takes a html string and an optional tag name
- // to generate DOM nodes nodes from the given html string.
- // The generated DOM nodes are returned as an array.
- // This function can be overriden in plugins for example to make
- // it compatible with browsers that don't support the DOM fully.
- zepto.fragment = function(html, name, properties) {
- if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
- if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
- if (!(name in containers)) name = '*'
-
- var nodes, dom, container = containers[name]
- container.innerHTML = '' + html
- dom = $.each(slice.call(container.childNodes), function(){
- container.removeChild(this)
- })
- if (isPlainObject(properties)) {
- nodes = $(dom)
- $.each(properties, function(key, value) {
- if (methodAttributes.indexOf(key) > -1) nodes[key](value)
- else nodes.attr(key, value)
- })
- }
- return dom
- }
-
- // `$.zepto.Z` swaps out the prototype of the given `dom` array
- // of nodes with `$.fn` and thus supplying all the Zepto functions
- // to the array. Note that `__proto__` is not supported on Internet
- // Explorer. This method can be overriden in plugins.
- zepto.Z = function(dom, selector) {
- dom = dom || []
- dom.__proto__ = $.fn
- dom.selector = selector || ''
- return dom
- }
-
- // `$.zepto.isZ` should return `true` if the given object is a Zepto
- // collection. This method can be overriden in plugins.
- zepto.isZ = function(object) {
- return object instanceof zepto.Z
- }
-
- // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
- // takes a CSS selector and an optional context (and handles various
- // special cases).
- // This method can be overriden in plugins.
- zepto.init = function(selector, context) {
- // If nothing given, return an empty Zepto collection
- if (!selector) return zepto.Z()
- // If a function is given, call it when the DOM is ready
- else if (isFunction(selector)) return $(document).ready(selector)
- // If a Zepto collection is given, juts return it
- else if (zepto.isZ(selector)) return selector
- else {
- var dom
- // normalize array if an array of nodes is given
- if (isArray(selector)) dom = compact(selector)
- // Wrap DOM nodes. If a plain object is given, duplicate it.
- else if (isObject(selector))
- dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null
- // If it's a html fragment, create nodes from it
- else if (fragmentRE.test(selector))
- dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
- // If there's a context, create a collection on that context first, and select
- // nodes from there
- else if (context !== undefined) return $(context).find(selector)
- // And last but no least, if it's a CSS selector, use it to select nodes.
- else dom = zepto.qsa(document, selector)
- // create a new Zepto collection from the nodes found
- return zepto.Z(dom, selector)
- }
- }
-
- // `$` will be the base `Zepto` object. When calling this
- // function just call `$.zepto.init, which makes the implementation
- // details of selecting nodes and creating Zepto collections
- // patchable in plugins.
- $ = function(selector, context){
- return zepto.init(selector, context)
- }
-
- function extend(target, source, deep) {
- for (key in source)
- if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
- if (isPlainObject(source[key]) && !isPlainObject(target[key]))
- target[key] = {}
- if (isArray(source[key]) && !isArray(target[key]))
- target[key] = []
- extend(target[key], source[key], deep)
- }
- else if (source[key] !== undefined) target[key] = source[key]
- }
-
- // Copy all but undefined properties from one or more
- // objects to the `target` object.
- $.extend = function(target){
- var deep, args = slice.call(arguments, 1)
- if (typeof target == 'boolean') {
- deep = target
- target = args.shift()
- }
- args.forEach(function(arg){ extend(target, arg, deep) })
- return target
- }
-
- // `$.zepto.qsa` is Zepto's CSS selector implementation which
- // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
- // This method can be overriden in plugins.
- zepto.qsa = function(element, selector){
- var found
- return (isDocument(element) && idSelectorRE.test(selector)) ?
- ( (found = element.getElementById(RegExp.$1)) ? [found] : [] ) :
- (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
- slice.call(
- classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
- tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
- element.querySelectorAll(selector)
- )
- }
-
- function filtered(nodes, selector) {
- return selector == null ? $(nodes) : $(nodes).filter(selector)
- }
-
- $.contains = function(parent, node) {
- return parent !== node && parent.contains(node)
- }
-
- function funcArg(context, arg, idx, payload) {
- return isFunction(arg) ? arg.call(context, idx, payload) : arg
- }
-
- function setAttribute(node, name, value) {
- value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
- }
-
- // access className property while respecting SVGAnimatedString
- function className(node, value){
- var klass = node.className,
- svg = klass && klass.baseVal !== undefined
-
- if (value === undefined) return svg ? klass.baseVal : klass
- svg ? (klass.baseVal = value) : (node.className = value)
- }
-
- // "true" => true
- // "false" => false
- // "null" => null
- // "42" => 42
- // "42.5" => 42.5
- // JSON => parse if valid
- // String => self
- function deserializeValue(value) {
- var num
- try {
- return value ?
- value == "true" ||
- ( value == "false" ? false :
- value == "null" ? null :
- !isNaN(num = Number(value)) ? num :
- /^[\[\{]/.test(value) ? $.parseJSON(value) :
- value )
- : value
- } catch(e) {
- return value
- }
- }
-
- $.type = type
- $.isFunction = isFunction
- $.isWindow = isWindow
- $.isArray = isArray
- $.isPlainObject = isPlainObject
-
- $.isEmptyObject = function(obj) {
- var name
- for (name in obj) return false
- return true
- }
-
- $.inArray = function(elem, array, i){
- return emptyArray.indexOf.call(array, elem, i)
- }
-
- $.camelCase = camelize
- $.trim = function(str) {
- return str == null ? "" : String.prototype.trim.call(str)
- }
-
- // plugin compatibility
- $.uuid = 0
- $.support = { }
- $.expr = { }
-
- $.map = function(elements, callback){
- var value, values = [], i, key
- if (likeArray(elements))
- for (i = 0; i < elements.length; i++) {
- value = callback(elements[i], i)
- if (value != null) values.push(value)
- }
- else
- for (key in elements) {
- value = callback(elements[key], key)
- if (value != null) values.push(value)
- }
- return flatten(values)
- }
-
- $.each = function(elements, callback){
- var i, key
- if (likeArray(elements)) {
- for (i = 0; i < elements.length; i++)
- if (callback.call(elements[i], i, elements[i]) === false) return elements
- } else {
- for (key in elements)
- if (callback.call(elements[key], key, elements[key]) === false) return elements
- }
-
- return elements
- }
-
- $.grep = function(elements, callback){
- return filter.call(elements, callback)
- }
-
- if (window.JSON) $.parseJSON = JSON.parse
-
- // Populate the class2type map
- $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase()
- })
-
- // Define methods that will be available on all
- // Zepto collections
- $.fn = {
- // Because a collection acts like an array
- // copy over these useful array functions.
- forEach: emptyArray.forEach,
- reduce: emptyArray.reduce,
- push: emptyArray.push,
- sort: emptyArray.sort,
- indexOf: emptyArray.indexOf,
- concat: emptyArray.concat,
-
- // `map` and `slice` in the jQuery API work differently
- // from their array counterparts
- map: function(fn){
- return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
- },
- slice: function(){
- return $(slice.apply(this, arguments))
- },
-
- ready: function(callback){
- if (readyRE.test(document.readyState)) callback($)
- else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
- return this
- },
- get: function(idx){
- return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
- },
- toArray: function(){ return this.get() },
- size: function(){
- return this.length
- },
- remove: function(){
- return this.each(function(){
- if (this.parentNode != null)
- this.parentNode.removeChild(this)
- })
- },
- each: function(callback){
- emptyArray.every.call(this, function(el, idx){
- return callback.call(el, idx, el) !== false
- })
- return this
- },
- filter: function(selector){
- if (isFunction(selector)) return this.not(this.not(selector))
- return $(filter.call(this, function(element){
- return zepto.matches(element, selector)
- }))
- },
- add: function(selector,context){
- return $(uniq(this.concat($(selector,context))))
- },
- is: function(selector){
- return this.length > 0 && zepto.matches(this[0], selector)
- },
- not: function(selector){
- var nodes=[]
- if (isFunction(selector) && selector.call !== undefined)
- this.each(function(idx){
- if (!selector.call(this,idx)) nodes.push(this)
- })
- else {
- var excludes = typeof selector == 'string' ? this.filter(selector) :
- (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
- this.forEach(function(el){
- if (excludes.indexOf(el) < 0) nodes.push(el)
- })
- }
- return $(nodes)
- },
- has: function(selector){
- return this.filter(function(){
- return isObject(selector) ?
- $.contains(this, selector) :
- $(this).find(selector).size()
- })
- },
- eq: function(idx){
- return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
- },
- first: function(){
- var el = this[0]
- return el && !isObject(el) ? el : $(el)
- },
- last: function(){
- var el = this[this.length - 1]
- return el && !isObject(el) ? el : $(el)
- },
- find: function(selector){
- var result, $this = this
- if (typeof selector == 'object')
- result = $(selector).filter(function(){
- var node = this
- return emptyArray.some.call($this, function(parent){
- return $.contains(parent, node)
- })
- })
- else if (this.length == 1) result = $(zepto.qsa(this[0], selector))
- else result = this.map(function(){ return zepto.qsa(this, selector) })
- return result
- },
- closest: function(selector, context){
- var node = this[0], collection = false
- if (typeof selector == 'object') collection = $(selector)
- while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector)))
- node = node !== context && !isDocument(node) && node.parentNode
- return $(node)
- },
- parents: function(selector){
- var ancestors = [], nodes = this
- while (nodes.length > 0)
- nodes = $.map(nodes, function(node){
- if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) {
- ancestors.push(node)
- return node
- }
- })
- return filtered(ancestors, selector)
- },
- parent: function(selector){
- return filtered(uniq(this.pluck('parentNode')), selector)
- },
- children: function(selector){
- return filtered(this.map(function(){ return children(this) }), selector)
- },
- contents: function() {
- return this.map(function() { return slice.call(this.childNodes) })
- },
- siblings: function(selector){
- return filtered(this.map(function(i, el){
- return filter.call(children(el.parentNode), function(child){ return child!==el })
- }), selector)
- },
- empty: function(){
- return this.each(function(){ this.innerHTML = '' })
- },
- // `pluck` is borrowed from Prototype.js
- pluck: function(property){
- return $.map(this, function(el){ return el[property] })
- },
- show: function(){
- return this.each(function(){
- this.style.display == "none" && (this.style.display = null)
- if (getComputedStyle(this, '').getPropertyValue("display") == "none")
- this.style.display = defaultDisplay(this.nodeName)
- })
- },
- replaceWith: function(newContent){
- return this.before(newContent).remove()
- },
- wrap: function(structure){
- var func = isFunction(structure)
- if (this[0] && !func)
- var dom = $(structure).get(0),
- clone = dom.parentNode || this.length > 1
-
- return this.each(function(index){
- $(this).wrapAll(
- func ? structure.call(this, index) :
- clone ? dom.cloneNode(true) : dom
- )
- })
- },
- wrapAll: function(structure){
- if (this[0]) {
- $(this[0]).before(structure = $(structure))
- var children
- // drill down to the inmost element
- while ((children = structure.children()).length) structure = children.first()
- $(structure).append(this)
- }
- return this
- },
- wrapInner: function(structure){
- var func = isFunction(structure)
- return this.each(function(index){
- var self = $(this), contents = self.contents(),
- dom = func ? structure.call(this, index) : structure
- contents.length ? contents.wrapAll(dom) : self.append(dom)
- })
- },
- unwrap: function(){
- this.parent().each(function(){
- $(this).replaceWith($(this).children())
- })
- return this
- },
- clone: function(){
- return this.map(function(){ return this.cloneNode(true) })
- },
- hide: function(){
- return this.css("display", "none")
- },
- toggle: function(setting){
- return this.each(function(){
- var el = $(this)
- ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
- })
- },
- prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },
- next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },
- html: function(html){
- return html === undefined ?
- (this.length > 0 ? this[0].innerHTML : null) :
- this.each(function(idx){
- var originHtml = this.innerHTML
- $(this).empty().append( funcArg(this, html, idx, originHtml) )
- })
- },
- text: function(text){
- return text === undefined ?
- (this.length > 0 ? this[0].textContent : null) :
- this.each(function(){ this.textContent = text })
- },
- attr: function(name, value){
- var result
- return (typeof name == 'string' && value === undefined) ?
- (this.length == 0 || this[0].nodeType !== 1 ? undefined :
- (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
- (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
- ) :
- this.each(function(idx){
- if (this.nodeType !== 1) return
- if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
- else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
- })
- },
- removeAttr: function(name){
- return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) })
- },
- prop: function(name, value){
- return (value === undefined) ?
- (this[0] && this[0][name]) :
- this.each(function(idx){
- this[name] = funcArg(this, value, idx, this[name])
- })
- },
- data: function(name, value){
- var data = this.attr('data-' + dasherize(name), value)
- return data !== null ? deserializeValue(data) : undefined
- },
- val: function(value){
- return (value === undefined) ?
- (this[0] && (this[0].multiple ?
- $(this[0]).find('option').filter(function(o){ return this.selected }).pluck('value') :
- this[0].value)
- ) :
- this.each(function(idx){
- this.value = funcArg(this, value, idx, this.value)
- })
- },
- offset: function(coordinates){
- if (coordinates) return this.each(function(index){
- var $this = $(this),
- coords = funcArg(this, coordinates, index, $this.offset()),
- parentOffset = $this.offsetParent().offset(),
- props = {
- top: coords.top - parentOffset.top,
- left: coords.left - parentOffset.left
- }
-
- if ($this.css('position') == 'static') props['position'] = 'relative'
- $this.css(props)
- })
- if (this.length==0) return null
- var obj = this[0].getBoundingClientRect()
- return {
- left: obj.left + window.pageXOffset,
- top: obj.top + window.pageYOffset,
- width: Math.round(obj.width),
- height: Math.round(obj.height)
- }
- },
- css: function(property, value){
- if (arguments.length < 2 && typeof property == 'string')
- return this[0] && (this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property))
-
- var css = ''
- if (type(property) == 'string') {
- if (!value && value !== 0)
- this.each(function(){ this.style.removeProperty(dasherize(property)) })
- else
- css = dasherize(property) + ":" + maybeAddPx(property, value)
- } else {
- for (key in property)
- if (!property[key] && property[key] !== 0)
- this.each(function(){ this.style.removeProperty(dasherize(key)) })
- else
- css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
- }
-
- return this.each(function(){ this.style.cssText += ';' + css })
- },
- index: function(element){
- return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
- },
- hasClass: function(name){
- return emptyArray.some.call(this, function(el){
- return this.test(className(el))
- }, classRE(name))
- },
- addClass: function(name){
- return this.each(function(idx){
- classList = []
- var cls = className(this), newName = funcArg(this, name, idx, cls)
- newName.split(/\s+/g).forEach(function(klass){
- if (!$(this).hasClass(klass)) classList.push(klass)
- }, this)
- classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
- })
- },
- removeClass: function(name){
- return this.each(function(idx){
- if (name === undefined) return className(this, '')
- classList = className(this)
- funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){
- classList = classList.replace(classRE(klass), " ")
- })
- className(this, classList.trim())
- })
- },
- toggleClass: function(name, when){
- return this.each(function(idx){
- var $this = $(this), names = funcArg(this, name, idx, className(this))
- names.split(/\s+/g).forEach(function(klass){
- (when === undefined ? !$this.hasClass(klass) : when) ?
- $this.addClass(klass) : $this.removeClass(klass)
- })
- })
- },
- scrollTop: function(){
- if (!this.length) return
- return ('scrollTop' in this[0]) ? this[0].scrollTop : this[0].scrollY
- },
- position: function() {
- if (!this.length) return
-
- var elem = this[0],
- // Get *real* offsetParent
- offsetParent = this.offsetParent(),
- // Get correct offsets
- offset = this.offset(),
- parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
-
- // Subtract element margins
- // note: when an element has margin: auto the offsetLeft and marginLeft
- // are the same in Safari causing offset.left to incorrectly be 0
- offset.top -= parseFloat( $(elem).css('margin-top') ) || 0
- offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
-
- // Add offsetParent borders
- parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
- parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
-
- // Subtract the two offsets
- return {
- top: offset.top - parentOffset.top,
- left: offset.left - parentOffset.left
- }
- },
- offsetParent: function() {
- return this.map(function(){
- var parent = this.offsetParent || document.body
- while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
- parent = parent.offsetParent
- return parent
- })
- }
- }
-
- // for now
- $.fn.detach = $.fn.remove
-
- // Generate the `width` and `height` functions
- ;['width', 'height'].forEach(function(dimension){
- $.fn[dimension] = function(value){
- var offset, el = this[0],
- Dimension = dimension.replace(/./, function(m){ return m[0].toUpperCase() })
- if (value === undefined) return isWindow(el) ? el['inner' + Dimension] :
- isDocument(el) ? el.documentElement['offset' + Dimension] :
- (offset = this.offset()) && offset[dimension]
- else return this.each(function(idx){
- el = $(this)
- el.css(dimension, funcArg(this, value, idx, el[dimension]()))
- })
- }
- })
-
- function traverseNode(node, fun) {
- fun(node)
- for (var key in node.childNodes) traverseNode(node.childNodes[key], fun)
- }
-
- // Generate the `after`, `prepend`, `before`, `append`,
- // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
- adjacencyOperators.forEach(function(operator, operatorIndex) {
- var inside = operatorIndex % 2 //=> prepend, append
-
- $.fn[operator] = function(){
- // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
- var argType, nodes = $.map(arguments, function(arg) {
- argType = type(arg)
- return argType == "object" || argType == "array" || arg == null ?
- arg : zepto.fragment(arg)
- }),
- parent, copyByClone = this.length > 1
- if (nodes.length < 1) return this
-
- return this.each(function(_, target){
- parent = inside ? target : target.parentNode
-
- // convert all methods to a "before" operation
- target = operatorIndex == 0 ? target.nextSibling :
- operatorIndex == 1 ? target.firstChild :
- operatorIndex == 2 ? target :
- null
-
- nodes.forEach(function(node){
- if (copyByClone) node = node.cloneNode(true)
- else if (!parent) return $(node).remove()
-
- traverseNode(parent.insertBefore(node, target), function(el){
- if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
- (!el.type || el.type === 'text/javascript') && !el.src)
- window['eval'].call(window, el.innerHTML)
- })
- })
- })
- }
-
- // after => insertAfter
- // prepend => prependTo
- // before => insertBefore
- // append => appendTo
- $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
- $(html)[operator](this)
- return this
- }
- })
-
- zepto.Z.prototype = $.fn
-
- // Export internal API functions in the `$.zepto` namespace
- zepto.uniq = uniq
- zepto.deserializeValue = deserializeValue
- $.zepto = zepto
-
- return $
-})()
-
-window.Zepto = Zepto
-'$' in window || (window.$ = Zepto)
-
-;(function($){
- var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={},
- hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
-
- specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
-
- function zid(element) {
- return element._zid || (element._zid = _zid++)
- }
- function findHandlers(element, event, fn, selector) {
- event = parse(event)
- if (event.ns) var matcher = matcherFor(event.ns)
- return (handlers[zid(element)] || []).filter(function(handler) {
- return handler
- && (!event.e || handler.e == event.e)
- && (!event.ns || matcher.test(handler.ns))
- && (!fn || zid(handler.fn) === zid(fn))
- && (!selector || handler.sel == selector)
- })
- }
- function parse(event) {
- var parts = ('' + event).split('.')
- return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
- }
- function matcherFor(ns) {
- return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
- }
-
- function eachEvent(events, fn, iterator){
- if ($.type(events) != "string") $.each(events, iterator)
- else events.split(/\s/).forEach(function(type){ iterator(type, fn) })
- }
-
- function eventCapture(handler, captureSetting) {
- return handler.del &&
- (handler.e == 'focus' || handler.e == 'blur') ||
- !!captureSetting
- }
-
- function realEvent(type) {
- return hover[type] || type
- }
-
- function add(element, events, fn, selector, getDelegate, capture){
- var id = zid(element), set = (handlers[id] || (handlers[id] = []))
- eachEvent(events, fn, function(event, fn){
- var handler = parse(event)
- handler.fn = fn
- handler.sel = selector
- // emulate mouseenter, mouseleave
- if (handler.e in hover) fn = function(e){
- var related = e.relatedTarget
- if (!related || (related !== this && !$.contains(this, related)))
- return handler.fn.apply(this, arguments)
- }
- handler.del = getDelegate && getDelegate(fn, event)
- var callback = handler.del || fn
- handler.proxy = function (e) {
- var result = callback.apply(element, [e].concat(e.data))
- if (result === false) e.preventDefault(), e.stopPropagation()
- return result
- }
- handler.i = set.length
- set.push(handler)
- element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
- })
- }
- function remove(element, events, fn, selector, capture){
- var id = zid(element)
- eachEvent(events || '', fn, function(event, fn){
- findHandlers(element, event, fn, selector).forEach(function(handler){
- delete handlers[id][handler.i]
- element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
- })
- })
- }
-
- $.event = { add: add, remove: remove }
-
- $.proxy = function(fn, context) {
- if ($.isFunction(fn)) {
- var proxyFn = function(){ return fn.apply(context, arguments) }
- proxyFn._zid = zid(fn)
- return proxyFn
- } else if (typeof context == 'string') {
- return $.proxy(fn[context], fn)
- } else {
- throw new TypeError("expected function")
- }
- }
-
- $.fn.bind = function(event, callback){
- return this.each(function(){
- add(this, event, callback)
- })
- }
- $.fn.unbind = function(event, callback){
- return this.each(function(){
- remove(this, event, callback)
- })
- }
- $.fn.one = function(event, callback){
- return this.each(function(i, element){
- add(this, event, callback, null, function(fn, type){
- return function(){
- var result = fn.apply(element, arguments)
- remove(element, type, fn)
- return result
- }
- })
- })
- }
-
- var returnTrue = function(){return true},
- returnFalse = function(){return false},
- ignoreProperties = /^([A-Z]|layer[XY]$)/,
- eventMethods = {
- preventDefault: 'isDefaultPrevented',
- stopImmediatePropagation: 'isImmediatePropagationStopped',
- stopPropagation: 'isPropagationStopped'
- }
- function createProxy(event) {
- var key, proxy = { originalEvent: event }
- for (key in event)
- if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
-
- $.each(eventMethods, function(name, predicate) {
- proxy[name] = function(){
- this[predicate] = returnTrue
- return event[name].apply(event, arguments)
- }
- proxy[predicate] = returnFalse
- })
- return proxy
- }
-
- // emulates the 'defaultPrevented' property for browsers that have none
- function fix(event) {
- if (!('defaultPrevented' in event)) {
- event.defaultPrevented = false
- var prevent = event.preventDefault
- event.preventDefault = function() {
- this.defaultPrevented = true
- prevent.call(this)
- }
- }
- }
-
- $.fn.delegate = function(selector, event, callback){
- return this.each(function(i, element){
- add(element, event, callback, selector, function(fn){
- return function(e){
- var evt, match = $(e.target).closest(selector, element).get(0)
- if (match) {
- evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
- return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
- }
- }
- })
- })
- }
- $.fn.undelegate = function(selector, event, callback){
- return this.each(function(){
- remove(this, event, callback, selector)
- })
- }
-
- $.fn.live = function(event, callback){
- $(document.body).delegate(this.selector, event, callback)
- return this
- }
- $.fn.die = function(event, callback){
- $(document.body).undelegate(this.selector, event, callback)
- return this
- }
-
- $.fn.on = function(event, selector, callback){
- return !selector || $.isFunction(selector) ?
- this.bind(event, selector || callback) : this.delegate(selector, event, callback)
- }
- $.fn.off = function(event, selector, callback){
- return !selector || $.isFunction(selector) ?
- this.unbind(event, selector || callback) : this.undelegate(selector, event, callback)
- }
-
- $.fn.trigger = function(event, data){
- if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event)
- fix(event)
- event.data = data
- return this.each(function(){
- // items in the collection might not be DOM elements
- // (todo: possibly support events on plain old objects)
- if('dispatchEvent' in this) this.dispatchEvent(event)
- })
- }
-
- // triggers event handlers on current element just as if an event occurred,
- // doesn't trigger an actual event, doesn't bubble
- $.fn.triggerHandler = function(event, data){
- var e, result
- this.each(function(i, element){
- e = createProxy(typeof event == 'string' ? $.Event(event) : event)
- e.data = data
- e.target = element
- $.each(findHandlers(element, event.type || event), function(i, handler){
- result = handler.proxy(e)
- if (e.isImmediatePropagationStopped()) return false
- })
- })
- return result
- }
-
- // shortcut methods for `.bind(event, fn)` for each event type
- ;('focusin focusout load resize scroll unload click dblclick '+
- 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
- 'change select keydown keypress keyup error').split(' ').forEach(function(event) {
- $.fn[event] = function(callback) {
- return callback ?
- this.bind(event, callback) :
- this.trigger(event)
- }
- })
-
- ;['focus', 'blur'].forEach(function(name) {
- $.fn[name] = function(callback) {
- if (callback) this.bind(name, callback)
- else this.each(function(){
- try { this[name]() }
- catch(e) {}
- })
- return this
- }
- })
-
- $.Event = function(type, props) {
- if (typeof type != 'string') props = type, type = props.type
- var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
- if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
- event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null)
- event.isDefaultPrevented = function(){ return this.defaultPrevented }
- return event
- }
-
-})(Zepto)
diff --git a/package.json b/package.json
index c3aa3b8..21894d7 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,8 @@
"Eric Johansson <neku@pomf.se>",
"Peter Lejeck <peter.lejeck@gmail.com>",
"cenci0 <alchimist94@gmail.com>",
- "Juuso Lapinlampi <wub@partyvan.eu>"
+ "Juuso Lapinlampi <wub@partyvan.eu>",
+ "Luminarys <postmaster@gensok.io>"
],
"license": "MIT",
"bugs": {
diff --git a/pages/banners.swig b/pages/banners.swig
index e8f0cbd..ce25555 100644
--- a/pages/banners.swig
+++ b/pages/banners.swig
@@ -19,6 +19,11 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#}
+{#
+This template is deprecated since version 2.1.0. It may be removed in a future
+release.
+#}
+
{% for banner in banners %}
{% include banner %}
{% endfor %}
diff --git a/pages/banners/donate.swig b/pages/banners/donate.swig
index f4706c9..6a9b73b 100644
--- a/pages/banners/donate.swig
+++ b/pages/banners/donate.swig
@@ -19,6 +19,11 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#}
+{#
+This template is deprecated since version 2.1.0. It may be removed in a future
+release.
+#}
+
<p class="alert alert-info">
<strong>pomf.se is a free service, but our hosting bill isn't</strong> &mdash; we need almost $100 a month for that. Donations help keep pomf.se free, online, and fast. Everything (after fees) goes to the hosting bill.
<span class="donate-btns">
diff --git a/pages/banners/virusscans.swig b/pages/banners/virusscans.swig
index 4a3d5a8..16a3608 100644
--- a/pages/banners/virusscans.swig
+++ b/pages/banners/virusscans.swig
@@ -19,6 +19,11 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#}
+{#
+This template is deprecated since version 2.1.0. It may be removed in a future
+release.
+#}
+
<p class="alert alert-info">
<strong>Virus scans are now a daily routine</strong> &mdash; if a file gets detected it will be removed without further notice.
</p>
diff --git a/pages/faq.swig b/pages/faq.swig
index 5868906..f4d78a6 100644
--- a/pages/faq.swig
+++ b/pages/faq.swig
@@ -42,7 +42,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
<p>Please contact the appropriate law enforcement agency if you notice illegal works hosted on Pantsu.cat. We have not been trained or qualified to investigate and fight crimes and enforce the law, so it's not appropriate to send accusations of illegal activity to us. <strong>You must contact the appropriate law enforcement office.</strong> They may then contact us if appropriate.</p>
<p>If you are an Australian law enforcement official and you need our assistance, please contact <a href="mailto:abuse@pantsu.cat">abuse@pantsu.cat</a>. If you are a law enforcement official from another country, we may voluntarily cooperate if the crime you are investigating would also be illegal in Australia.</p>
<h2>I have a question...</h2>
- <p>Send us an email at <a href="mailto:contact@pantsu.cat">contact@pantsu.cat</a> and let's talk!</p>
+ <p>Send us an email at <a href="mailto:info@pantsu.cat">info@pantsu.cat</a> and let's talk!</p>
</article>
{% include "nav.swig" %}
{% endblock %}
diff --git a/pages/index.swig b/pages/index.swig
index ff7db42..d9eb263 100644
--- a/pages/index.swig
+++ b/pages/index.swig
@@ -30,7 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{% include "check_fileapi.swig" %}
{% include "upload_form.swig" %}
</div>
-{% include "banners.swig" %}
+{% include "banners.swig" %} {# Deprecated since version 2.1.0. #}
{% include "nav.swig" %}
<script src="pomf.min.js"></script>
{% endblock %}
diff --git a/pages/layout.swig b/pages/layout.swig
index 103c36e..ebc3c53 100644
--- a/pages/layout.swig
+++ b/pages/layout.swig
@@ -1,6 +1,6 @@
{#
Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com>
-Copyright (c) 2015 the Pantsu.cat developers <hostmaster@pantsu.cat>
+Copyright (c) 2015, 2016 the Pantsu.cat developers <hostmaster@pantsu.cat>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -25,6 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="generator" content="Pomf {{pkgVersion}}">
<title>Pantsu.cat &middot; {% block title %}Kawaii File Hosting{% endblock %}</title>
<link rel="icon" href="favicon.ico">
<link rel="stylesheet" href="pomf.min.css">
diff --git a/pages/nojs.swig b/pages/nojs.swig
index 9facb91..5552b11 100644
--- a/pages/nojs.swig
+++ b/pages/nojs.swig
@@ -28,6 +28,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
<p class="lead">Max upload size is {{max_upload_size}}MiB, read the <a href="faq.html">FAQ</a></p>
{% include "upload_form_nojs.swig" %}
</div>
-{% include "banners.swig" %}
+{% include "banners.swig" %} {# Deprecated since version 2.1.0. #}
{% include "nav.swig" %}
{% endblock %}
diff --git a/schema.sql b/schema.sql
index f668c5e..154f095 100644
--- a/schema.sql
+++ b/schema.sql
@@ -13,7 +13,7 @@ CREATE TABLE `files` (
`filename` varchar(30) default NULL,
`size` int(10) unsigned default NULL,
`date` date default NULL,
- `expire` date default NULL,
+ `expire` date default NULL, -- Deprecated since version 2.1.0
`delid` char(40) default NULL,
`user` int(10) unsigned default '0',
`dir` int(2) default '0',
diff --git a/static/classes/Response.class.php b/static/classes/Response.class.php
index eed8ce7..48b9b58 100755
--- a/static/classes/Response.class.php
+++ b/static/classes/Response.class.php
@@ -1,8 +1,11 @@
<?php
/**
- * Copyright (c) 2015 cenci0 <alchimist94@gmail.com>
- * Copyright (c) 2015, 2016 the Pantsu.cat developers <hostmaster@pantsu.cat>
+ * Prints file upload API responses depending on desired response type format.
+ *
+ * @copyright Copyright (c) 2015 cenci0 <alchimist94@gmail.com>
+ * @copyright Copyright (c) 2015, 2016 the Pantsu.cat developers
+ * <hostmaster@pantsu.cat>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,10 +26,30 @@
* SOFTWARE.
*/
+/**
+ * The Response class is a do-it-all for getting responses out in different
+ * formats.
+ *
+ * @todo Create sub-classes to split and extend this god object.
+ */
class Response
{
+ /**
+ * Indicates response type used for routing.
+ *
+ * Valid strings are 'csv', 'html', 'json' and 'text'.
+ *
+ * @var string $type Response type
+ */
private $type;
+ /**
+ * Indicates requested response type.
+ *
+ * Valid strings are 'csv', 'html', 'json', 'gyazo' and 'text'.
+ *
+ * @param string|null $response_type Response type
+ */
public function __construct($response_type = null)
{
switch ($response_type) {
@@ -59,6 +82,13 @@ class Response
}
}
+ /**
+ * Routes error messages depending on response type.
+ *
+ * @param int $code HTTP status code number.
+ * @param int $desc Descriptive error message.
+ * @return void
+ */
public function error($code, $desc)
{
$response = null;
@@ -82,6 +112,12 @@ class Response
echo $response;
}
+ /**
+ * Routes success messages depending on response type.
+ *
+ * @param mixed[] $files
+ * @return void
+ */
public function send($files)
{
$response = null;
@@ -105,11 +141,25 @@ class Response
echo $response;
}
+ /**
+ * Indicates with CSV body the request was invalid.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param int $description Descriptive error message.
+ * @return string Error message in CSV format.
+ */
private static function csv_error($description)
{
return '"error"'."\r\n"."\"$description\""."\r\n";
}
+ /**
+ * Indicates with CSV body the request was successful.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param mixed[] $files
+ * @return string Success message in CSV format.
+ */
private static function csv_success($files)
{
$result = '"name","url","hash","size"'."\r\n";
@@ -123,11 +173,26 @@ class Response
return $result;
}
+ /**
+ * Indicates with HTML body the request was invalid.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param int $code HTTP status code number.
+ * @param int $description Descriptive error message.
+ * @return string Error message in HTML format.
+ */
private static function html_error($code, $description)
{
return '<p>ERROR: ('.$code.') '.$description.'</p>';
}
+ /**
+ * Indicates with HTML body the request was successful.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param mixed[] $files
+ * @return string Success message in HTML format.
+ */
private static function html_success($files)
{
$result = '';
@@ -139,6 +204,14 @@ class Response
return $result;
}
+ /**
+ * Indicates with JSON body the request was invalid.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param int $code HTTP status code number.
+ * @param int $description Descriptive error message.
+ * @return string Error message in pretty-printed JSON format.
+ */
private static function json_error($code, $description)
{
return json_encode(array(
@@ -148,6 +221,13 @@ class Response
), JSON_PRETTY_PRINT);
}
+ /**
+ * Indicates with JSON body the request was successful.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param mixed[] $files
+ * @return string Success message in pretty-printed JSON format.
+ */
private static function json_success($files)
{
return json_encode(array(
@@ -156,11 +236,26 @@ class Response
), JSON_PRETTY_PRINT);
}
+ /**
+ * Indicates with plain text body the request was invalid.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param int $code HTTP status code number.
+ * @param int $description Descriptive error message.
+ * @return string Error message in plain text format.
+ */
private static function text_error($code, $description)
{
return 'ERROR: ('.$code.') '.$description;
}
+ /**
+ * Indicates with plain text body the request was successful.
+ *
+ * @deprecated 2.1.0 Will be renamed to camelCase format.
+ * @param mixed[] $files
+ * @return string Success message in plain text format.
+ */
private static function text_success($files)
{
$result = '';
diff --git a/static/classes/UploadException.class.php b/static/classes/UploadException.class.php
index dbe47eb..7740ee6 100755
--- a/static/classes/UploadException.class.php
+++ b/static/classes/UploadException.class.php
@@ -1,6 +1,11 @@
<?php
/**
+ * Returns a human readable error description for file upload errors.
+ *
+ * @author Dan Brown <danbrown@php.net>
+ * @author Michiel Thalen
+ * @copyright
* --------------------------------------------------------------------
* The PHP License, version 3.01
* Copyright (c) 1999 - 2014 The PHP Group. All rights reserved.
@@ -71,9 +76,6 @@
* <http://www.zend.com>.
*/
-// Exception class extension from: http://php.net/manual/en/features.file-upload.errors.php#89374
-// Handles files error returning a proper error description
-
class UploadException extends Exception
{
public function __construct($code)
diff --git a/static/classes/UploadedFile.class.php b/static/classes/UploadedFile.class.php
index ac39646..ccbaea8 100644
--- a/static/classes/UploadedFile.class.php
+++ b/static/classes/UploadedFile.class.php
@@ -1,7 +1,11 @@
<?php
/**
- * Copyright (c) 2013, 2014 Peter Lejeck <peter.lejeck@gmail.com>
+ * Returns information about uploaded files.
+ *
+ * @copyright Copyright (c) 2013, 2014 Peter Lejeck <peter.lejeck@gmail.com>
+ * @copyright Copyright (c) 2016 the Pantsu.cat developers
+ * <hostmaster@pantsu.cat>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -31,13 +35,19 @@ class UploadedFile
public $tempfile;
public $error;
- /* Cached checksums */
+ /**
+ * SHA-1 checksum
+ *
+ * @deprecated 2.1.0 To be replaced with SHA-256 hashing.
+ * @var string 40 digit hexadecimal hash (160 bits)
+ */
private $sha1;
/**
- * Generates the SHA1 or returns the cached SHA1 hash for the file.
+ * Generates the SHA-1 or returns the cached SHA-1 hash for the file.
*
- * @return string
+ * @deprecated 2.1.0 To be replaced with SHA-256 hashing.
+ * @return string|false $sha1
*/
public function get_sha1()
{
diff --git a/static/includes/database.inc.php b/static/includes/database.inc.php
index 0374afa..7a1d4f9 100644
--- a/static/includes/database.inc.php
+++ b/static/includes/database.inc.php
@@ -1,7 +1,9 @@
<?php
/**
- * Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com>
+ * Prepares a PDO connection between Pomf and a database server.
+ *
+ * @copyright Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +24,7 @@
* SOFTWARE.
*/
-include_once 'settings.inc.php';
+require_once 'settings.inc.php';
/* NOTE: we don't have to unref the PDO because we're not long-running */
$db = new PDO(POMF_DB_CONN, POMF_DB_USER, POMF_DB_PASS);
diff --git a/static/includes/settings.inc.php b/static/includes/settings.inc.php
index 6072249..872fde0 100644
--- a/static/includes/settings.inc.php
+++ b/static/includes/settings.inc.php
@@ -1,9 +1,12 @@
<?php
/**
- * Copyright (c) 2013, 2014 Peter Lejeck <peter.lejeck@gmail.com>
- * Copyright (c) 2015 cenci0 <alchimist94@gmail.com>
- * Copyright (c) 2015 the Pantsu.cat developers <hostmaster@pantsu.cat>
+ * User configurable settings for Pomf.
+ *
+ * @copyright Copyright (c) 2013, 2014 Peter Lejeck <peter.lejeck@gmail.com>
+ * @copyright Copyright (c) 2015 cenci0 <alchimist94@gmail.com>
+ * @copyright Copyright (c) 2015, 2016 the Pantsu.cat developers
+ * <hostmaster@pantsu.cat>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,22 +27,70 @@
* SOFTWARE.
*/
-// PDO socket
-// mysql.sock path can be different from /tmp/mysql.sock, see /etc/my.cnf
+/**
+ * PDO connection socket
+ *
+ * Database connection to use for communication. Currently, MySQL is the only
+ * DSN prefix supported.
+ *
+ * @see http://php.net/manual/en/ref.pdo-mysql.connection.php PHP manual for
+ * PDO_MYSQL DSN.
+ * @param string POMF_DB_CONN DSN:host|unix_socket=hostname|path;dbname=database
+ */
define('POMF_DB_CONN', 'mysql:unix_socket=/tmp/mysql.sock;dbname=pomf');
-// MySQL user and password
+
+/**
+ * PDO database login credentials
+ */
+
+/** @param string POMF_DB_NAME Database username */
define('POMF_DB_USER', 'pomf');
+/** @param string POMF_DB_PASS Database password */
define('POMF_DB_PASS', '***');
-// Root location of files
+/**
+ * File system location where to store uploaded files
+ *
+ * @param string Path to directory with trailing delimiter
+ */
define('POMF_FILES_ROOT', '/mnt/pantsu/http/files/');
-// Maximum number of iterations while generating a new filename
+
+/**
+ * Maximum number of iterations while generating a new filename
+ *
+ * Pomf uses an algorithm to generate random filenames. Sometimes a file may
+ * exist under a randomly generated filename, so we count tries and keep trying.
+ * If this value is exceeded, we give up trying to generate a new filename.
+ *
+ * @param int POMF_FILES_RETRIES Number of attempts to retry
+ */
define('POMF_FILES_RETRIES', 15);
-// Number of random characters to use in a new filename
+
+/**
+ * The length of generated filename (without file extension)
+ *
+ * @param int POMF_FILES_LENGTH Number of random alphabetical ASCII characters
+ * to use
+ */
define('POMF_FILES_LENGTH', 6);
-// URL to prepend to output (include trailing slash)
+
+/**
+ * URI to prepend to links for uploaded files
+ *
+ * @param string POMF_URL URI with trailing delimiter
+ */
define('POMF_URL', 'https://i.pantsu.cat/');
+/**
+ * Double dot file extensions
+ *
+ * Pomf keeps the last file extension for the uploaded file. In other words, an
+ * uploaded file with `.tar.gz` extension will be given a random filename which
+ * ends in `.gz` unless configured here to ignore discards for `.tar.gz`.
+ *
+ * @param string[] $doubledots Array of double dot file extensions strings
+ * without the first prefixing dot
+ */
$doubledots = array_map('strrev', array(
'tar.gz',
'tar.bz',
diff --git a/static/upload.php b/static/upload.php
index a37c549..ebedeab 100644
--- a/static/upload.php
+++ b/static/upload.php
@@ -1,10 +1,14 @@
<?php
/**
- * Copyright (c) 2013, 2014 Peter Lejeck <peter.lejeck@gmail.com>
- * Copyright (c) 2013, 2014, 2015 Eric Johansson <neku@pomf.se>
- * Copyright (c) 2015 cenci0 <alchimist94@gmail.com>
- * Copyright (c) 2015 the Pantsu.cat developers <hostmaster@pantsu.cat>
+ * Handles POST uploads, generates filenames, moves files around and commits
+ * uploaded metadata to database.
+ *
+ * @copyright Copyright (c) 2013, 2014 Peter Lejeck <peter.lejeck@gmail.com>
+ * @copyright Copyright (c) 2013, 2014, 2015 Eric Johansson <neku@pomf.se>
+ * @copyright Copyright (c) 2015 cenci0 <alchimist94@gmail.com>
+ * @copyright Copyright (c) 2015, 2016 the Pantsu.cat developers
+ * <hostmaster@pantsu.cat>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,16 +30,17 @@
*/
// Check if we can compress our output; if we can, we'll do it
+// This feature is deprecated since version 2.1.0.
if (ini_get('zlib.output_compression') !== 'Off'
&& isset($_SERVER['HTTP_ACCEPT_ENCODING'])
&& strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) {
ob_start('ob_gzhandler');
}
-include_once 'classes/Response.class.php';
-include_once 'classes/UploadException.class.php';
-include_once 'classes/UploadedFile.class.php';
-include_once 'includes/database.inc.php';
+require_once 'classes/Response.class.php';
+require_once 'classes/UploadException.class.php';
+require_once 'classes/UploadedFile.class.php';
+require_once 'includes/database.inc.php';
/**
* Generates a random name for the file, retrying until we get an unused one.
@@ -65,7 +70,8 @@ function generate_name($file)
do {
// Iterate until we reach the maximum number of retries
if ($tries-- === 0) {
- throw new Exception('Gave up trying to find an unused name', 500);
+ throw new Exception('Gave up trying to find an unused name',
+ 500); // HTTP status code "500 Internal Server Error"
}
$chars = range('a', 'z');
@@ -128,37 +134,45 @@ function upload_file($file)
// Generate a name for the file
$newname = generate_name($file);
+ // Store the file's full file path in memory
+ $uploadDir = POMF_FILES_ROOT;
+ $uploadFile = $uploadDir . $newname;
+
// Attempt to move it to the static directory
- if (move_uploaded_file($file->tempfile, POMF_FILES_ROOT.$newname)) {
- // Need to change permissions for the new file to make it world readable
- if (chmod(POMF_FILES_ROOT.$newname, 0644)) {
- // Add it to the database
- $q = $db->prepare('INSERT INTO files (hash, originalname, filename, size, date, '.
- 'expire, delid) VALUES (:hash, :orig, :name, :size, :date, '.
- ':exp, :del)');
-
- // Common parameters binding
- $q->bindValue(':hash', $file->get_sha1(), PDO::PARAM_STR);
- $q->bindValue(':orig', strip_tags($file->name), PDO::PARAM_STR);
- $q->bindValue(':name', $newname, PDO::PARAM_STR);
- $q->bindValue(':size', $file->size, PDO::PARAM_INT);
- $q->bindValue(':date', date('Y-m-d'), PDO::PARAM_STR);
- $q->bindValue(':exp', null, PDO::PARAM_STR);
- $q->bindValue(':del', sha1($file->tempfile), PDO::PARAM_STR);
- $q->execute();
-
- return array(
- 'hash' => $file->get_sha1(),
- 'name' => $file->name,
- 'url' => POMF_URL.$newname,
- 'size' => $file->size,
- );
- } else {
- throw new Exception('Failed to change file permissions', 500);
- }
- } else {
- throw new Exception('Failed to move file to destination', 500);
+ if (!move_uploaded_file($file->tempfile, $uploadFile)) {
+ throw new Exception('Failed to move file to destination',
+ 500); // HTTP status code "500 Internal Server Error"
+ }
+
+ // Need to change permissions for the new file to make it world readable
+ if (!chmod($uploadFile, 0644)) {
+ throw new Exception('Failed to change file permissions',
+ 500); // HTTP status code "500 Internal Server Error"
}
+
+ // Add it to the database
+ // 'expire' support is deprecated since version 2.1.0. It may be
+ // removed in a future release.
+ $q = $db->prepare('INSERT INTO files (hash, originalname, filename, size, date, '.
+ 'expire, delid) VALUES (:hash, :orig, :name, :size, :date, '.
+ ':exp, :del)');
+
+ // Common parameters binding
+ $q->bindValue(':hash', $file->get_sha1(), PDO::PARAM_STR);
+ $q->bindValue(':orig', strip_tags($file->name), PDO::PARAM_STR);
+ $q->bindValue(':name', $newname, PDO::PARAM_STR);
+ $q->bindValue(':size', $file->size, PDO::PARAM_INT);
+ $q->bindValue(':date', date('Y-m-d'), PDO::PARAM_STR);
+ $q->bindValue(':exp', null, PDO::PARAM_STR); // Deprecated since version 2.1.0
+ $q->bindValue(':del', sha1($file->tempfile), PDO::PARAM_STR);
+ $q->execute();
+
+ return array(
+ 'hash' => $file->get_sha1(),
+ 'name' => $file->name,
+ 'url' => POMF_URL.$newname,
+ 'size' => $file->size,
+ );
}
/**
@@ -201,7 +215,7 @@ function refiles($files)
$f->tempfile = $file['tmp_name'];
$f->error = $file['error'];
// 'expire' doesn't exist neither in $_FILES nor in UploadedFile;
- // commented out for future implementation
+ // it was never implemented and has been deprecated since version 2.1.0.
//$f->expire = $file['expire'];
$result[] = $f;
}