Compare commits
31 Commits
v1.0.0
...
v1.1.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
| d24c3d4895 | |||
| 5e9fcc09c8 | |||
| fb7f1dd3a5 | |||
| 2f4fe0a55f | |||
| a3fc9092e3 | |||
| 6eb48bf72f | |||
| 40aa6c8917 | |||
| 24febefba4 | |||
| 57d9b4d324 | |||
| 3ca73e3221 | |||
| 27cf4cf0c0 | |||
| ef6b680b7e | |||
| 188ca7e679 | |||
| b8f1b26aba | |||
| ac768524ee | |||
| a5008ab455 | |||
| 9436dcd43e | |||
| f543347b1c | |||
| 582854878b | |||
| 642ee5a485 | |||
| 19b20daded | |||
| 270e6a9397 | |||
| ef5351a075 | |||
| 01ac021c7e | |||
| 47aeaff66e | |||
| d18a5fd272 | |||
| 3a0566ca0a | |||
| 36f62dd502 | |||
| 5b1a94c735 | |||
| 75c3a98f33 | |||
| e9fbfd0277 |
2
.hgtags
Normal file
2
.hgtags
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
e2250a7fd29052ea767f18e1459cabea4cd7efd3 release-1.0.0
|
||||||
|
b8975b9e75648a7c2a5003c67db92cf2216e01c0 release-1.0.1
|
||||||
2
Makefile
2
Makefile
@@ -2,7 +2,7 @@
|
|||||||
# Makefile for contented
|
# Makefile for contented
|
||||||
#
|
#
|
||||||
|
|
||||||
VERSION:=1.0.0
|
VERSION:=1.1.0
|
||||||
|
|
||||||
SOURCES:=Makefile \
|
SOURCES:=Makefile \
|
||||||
static \
|
static \
|
||||||
|
|||||||
25
Server.go
25
Server.go
@@ -20,9 +20,11 @@ type ServerPublicProperties struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ServerOptions struct {
|
type ServerOptions struct {
|
||||||
DataDirectory string
|
DataDirectory string
|
||||||
DBPath string
|
DBPath string
|
||||||
BandwidthLimit int64
|
BandwidthLimit int64
|
||||||
|
TrustXForwardedFor bool
|
||||||
|
EnableHomepage bool
|
||||||
ServerPublicProperties
|
ServerPublicProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +77,13 @@ func (this *Server) handleAbout(w http.ResponseWriter) {
|
|||||||
this.serveJsonObject(w, this.opts.ServerPublicProperties)
|
this.serveJsonObject(w, this.opts.ServerPublicProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
func remoteIP(r *http.Request) string {
|
func (this *Server) remoteIP(r *http.Request) string {
|
||||||
|
if this.opts.TrustXForwardedFor {
|
||||||
|
if xff := r.Header.Get("X-Forwarded-For"); len(xff) > 0 {
|
||||||
|
return xff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return strings.TrimRight(strings.TrimRight(r.RemoteAddr, "0123456789"), ":")
|
return strings.TrimRight(strings.TrimRight(r.RemoteAddr, "0123456789"), ":")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +94,7 @@ const (
|
|||||||
|
|
||||||
func (this *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (this *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set(`Server`, SERVER_HEADER)
|
w.Header().Set(`Server`, SERVER_HEADER)
|
||||||
|
w.Header().Set(`Access-Control-Allow-Origin`, `*`) // Blanket allow CORS
|
||||||
if this.opts.MaxUploadBytes > 0 {
|
if this.opts.MaxUploadBytes > 0 {
|
||||||
r.Body = http.MaxBytesReader(w, r.Body, this.opts.MaxUploadBytes)
|
r.Body = http.MaxBytesReader(w, r.Body, this.opts.MaxUploadBytes)
|
||||||
}
|
}
|
||||||
@@ -108,11 +117,13 @@ func (this *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
this.handleUpload(w, r)
|
this.handleUpload(w, r)
|
||||||
|
|
||||||
} else if r.Method == "OPTIONS" {
|
} else if r.Method == "OPTIONS" {
|
||||||
// Blanket allow
|
// Blanket allow (headers already set)
|
||||||
w.Header().Set(`Access-Control-Allow-Origin`, `*`)
|
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
|
|
||||||
} else if static, err := Asset(r.URL.Path[1:]); err == nil && r.Method == "GET" {
|
} else if r.Method == "GET" && r.URL.Path == `/` && this.opts.EnableHomepage {
|
||||||
|
http.Redirect(w, r, `/index.html`, http.StatusFound)
|
||||||
|
|
||||||
|
} else if static, err := Asset(r.URL.Path[1:]); err == nil && r.Method == "GET" && (this.opts.EnableHomepage || r.URL.Path != `/index.html`) {
|
||||||
http.ServeContent(w, r, r.URL.Path[1:], this.startTime, bytes.NewReader(static))
|
http.ServeContent(w, r, r.URL.Path[1:], this.startTime, bytes.NewReader(static))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
4
TODO.txt
4
TODO.txt
@@ -8,6 +8,6 @@ TODO
|
|||||||
|
|
||||||
- Nicer preview page after uploading
|
- Nicer preview page after uploading
|
||||||
|
|
||||||
- Option to disable manual uploading from the landing page
|
|
||||||
|
|
||||||
- Gallery preview (multiple element hashid)
|
- Gallery preview (multiple element hashid)
|
||||||
|
|
||||||
|
- Prevent selecting around the toggle buttons (firefox for android)
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ You can use contented as a standalone upload server, or you can use the SDK to e
|
|||||||
|
|
||||||
=FEATURES=
|
=FEATURES=
|
||||||
|
|
||||||
- Drag and drop uploader, or fallback classic uploader, or pastebin-style uploader
|
- Drag and drop upload
|
||||||
- Multiple files upload
|
- Multiple files upload
|
||||||
- Pastebin upload
|
- Pastebin upload
|
||||||
|
- Custom drawing upload ([url=https://github.com/Leimi/drawingboard.js]via[/url])
|
||||||
|
- Ctrl-V upload
|
||||||
- SDK-oriented design for embedding, including CORS support
|
- SDK-oriented design for embedding, including CORS support
|
||||||
- Mobile friendly HTML interface
|
- Mobile friendly HTML interface
|
||||||
- Preserves uploaded filename and content-type metadata
|
- Preserves uploaded filename and content-type metadata
|
||||||
@@ -24,6 +26,8 @@ You can use contented as a standalone upload server, or you can use the SDK to e
|
|||||||
Directory for stored content (default "")
|
Directory for stored content (default "")
|
||||||
-db string
|
-db string
|
||||||
Path for metadata database (default "contented.db")
|
Path for metadata database (default "contented.db")
|
||||||
|
-enableHomepage
|
||||||
|
Enable homepage (disable for embedded use only) (default true)
|
||||||
-listen string
|
-listen string
|
||||||
(default "127.0.0.1:80")
|
(default "127.0.0.1:80")
|
||||||
-max int
|
-max int
|
||||||
@@ -32,8 +36,12 @@ You can use contented as a standalone upload server, or you can use the SDK to e
|
|||||||
Maximum upload speed in bytes/sec (set zero for unlimited)
|
Maximum upload speed in bytes/sec (set zero for unlimited)
|
||||||
-title string
|
-title string
|
||||||
Title used in web interface (default "contented")
|
Title used in web interface (default "contented")
|
||||||
|
-trustXForwardedFor
|
||||||
|
Trust X-Forwarded-For reverse proxy headers
|
||||||
`
|
`
|
||||||
|
|
||||||
|
If you are hosting behind a reverse proxy, remember to set its post body size parameter appropriately (e.g. `client_max_body_size` for nginx).
|
||||||
|
|
||||||
=USAGE (HTTP)=
|
=USAGE (HTTP)=
|
||||||
|
|
||||||
The server responds on the following URLs:
|
The server responds on the following URLs:
|
||||||
@@ -44,18 +52,31 @@ The server responds on the following URLs:
|
|||||||
|
|
||||||
=USAGE (EMBEDDING FOR WEB)=
|
=USAGE (EMBEDDING FOR WEB)=
|
||||||
|
|
||||||
Your webpage should load the SDK from the contented server, then call the `contented.init` function to display the upload widget over the top of an existing DOM element.
|
Your webpage should load the SDK from the contented server, then call the `contented.init` function to display the upload widget over the top of an existing DOM element. Your callback will be passed an array of file IDs of any uploaded items.
|
||||||
|
|
||||||
The SDK will run your callback, passing it the file IDs of any uploaded items.
|
`<script type="text/javascript" src="SERVER_ADDR/sdk.js"></script>
|
||||||
|
|
||||||
The SDK depends on jQuery.
|
|
||||||
|
|
||||||
`
|
|
||||||
<script type="text/javascript" src="SERVER_ADDR/sdk.js"></script>
|
|
||||||
contented.init("#target", function(/* String[] */ items) {});
|
contented.init("#target", function(/* String[] */ items) {});
|
||||||
`
|
`
|
||||||
|
|
||||||
=CHANGELOG=
|
=CHANGELOG=
|
||||||
|
|
||||||
|
2017-10-15: 1.1.0
|
||||||
|
- Feature: Drawing mode
|
||||||
|
- Feature: Ctrl+V image upload
|
||||||
|
- Feature: Option to trust X-Forwarded-For headers when using a reverse proxy
|
||||||
|
- Feature: Add `getDownloadURL`, `getInfoJSONURL`, `getPreviewURL` SDK methods
|
||||||
|
- Feature: Option to disable uploading via the homepage
|
||||||
|
- Feature: Add button to repeat when uploading from homepage
|
||||||
|
- Enhancement: Automatically load library dependencies
|
||||||
|
- Enhancement: Display homepage widget using the full screen size
|
||||||
|
- Include drawingboard.js 0.4.6 (MIT license)
|
||||||
|
- Fix a cosmetic issue with javascript console output
|
||||||
|
- Fix a cosmetic issue with error messages if an upload failed
|
||||||
|
|
||||||
|
2017-10-08: 1.0.1
|
||||||
|
- Fix an issue with CORS preflight requests
|
||||||
|
- Fix an issue with index URLs
|
||||||
|
|
||||||
2017-10-08: 1.0.0
|
2017-10-08: 1.0.0
|
||||||
- Initial public release
|
- Initial public release
|
||||||
|
- Include jQuery 1.12.4 (MIT license)
|
||||||
|
|||||||
BIN
_dist/image1.png
Normal file
BIN
_dist/image1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
@@ -18,13 +18,17 @@ func main() {
|
|||||||
appTitle := flag.String("title", "contented", "Title used in web interface")
|
appTitle := flag.String("title", "contented", "Title used in web interface")
|
||||||
maxUploadMb := flag.Int("max", 8, "Maximum size of uploaded files in MiB (set zero for unlimited)")
|
maxUploadMb := flag.Int("max", 8, "Maximum size of uploaded files in MiB (set zero for unlimited)")
|
||||||
maxUploadSpeed := flag.Int("speed", 0, "Maximum upload speed in bytes/sec (set zero for unlimited)")
|
maxUploadSpeed := flag.Int("speed", 0, "Maximum upload speed in bytes/sec (set zero for unlimited)")
|
||||||
|
trustXForwardedFor := flag.Bool("trustXForwardedFor", false, "Trust X-Forwarded-For reverse proxy headers")
|
||||||
|
enableHomepage := flag.Bool("enableHomepage", true, "Enable homepage (disable for embedded use only)")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
svr, err := contented.NewServer(&contented.ServerOptions{
|
svr, err := contented.NewServer(&contented.ServerOptions{
|
||||||
DataDirectory: *dataDir,
|
DataDirectory: *dataDir,
|
||||||
DBPath: *dbPath,
|
DBPath: *dbPath,
|
||||||
BandwidthLimit: int64(*maxUploadSpeed),
|
BandwidthLimit: int64(*maxUploadSpeed),
|
||||||
|
TrustXForwardedFor: *trustXForwardedFor,
|
||||||
|
EnableHomepage: *enableHomepage,
|
||||||
ServerPublicProperties: contented.ServerPublicProperties{
|
ServerPublicProperties: contented.ServerPublicProperties{
|
||||||
AppTitle: *appTitle,
|
AppTitle: *appTitle,
|
||||||
MaxUploadBytes: int64(*maxUploadMb) * 1024 * 1024,
|
MaxUploadBytes: int64(*maxUploadMb) * 1024 * 1024,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
func (this *Server) handleView(w http.ResponseWriter, r *http.Request, fileID string) {
|
func (this *Server) handleView(w http.ResponseWriter, r *http.Request, fileID string) {
|
||||||
err := this.handleViewInternal(w, r, r.URL.Path[len(downloadUrlPrefix):])
|
err := this.handleViewInternal(w, r, r.URL.Path[len(downloadUrlPrefix):])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s View failed: %s\n", r.RemoteAddr, err.Error())
|
log.Printf("%s View failed: %s\n", this.remoteIP(r), err.Error())
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
http.Error(w, "File not found", 404)
|
http.Error(w, "File not found", 404)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
5
static/drawingboard-0.4.6.min.css
vendored
Normal file
5
static/drawingboard-0.4.6.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
static/drawingboard-0.4.6.min.js
vendored
Normal file
4
static/drawingboard-0.4.6.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -7,9 +7,23 @@
|
|||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
html, body {
|
html, body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
margin:0;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
#padder {
|
||||||
|
height:100%;
|
||||||
|
position:relative;
|
||||||
}
|
}
|
||||||
#surrogate-area {
|
#surrogate-area {
|
||||||
height:300px;
|
position:absolute;
|
||||||
|
top:10px;
|
||||||
|
bottom:10px;
|
||||||
|
left:10px;
|
||||||
|
right:10px;
|
||||||
|
}
|
||||||
|
button.again {
|
||||||
|
margin-top:2em;
|
||||||
}
|
}
|
||||||
/* hide close button */
|
/* hide close button */
|
||||||
.contented-close {
|
.contented-close {
|
||||||
@@ -18,9 +32,11 @@ html, body {
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="surrogate-area">
|
<div id="padder">
|
||||||
Loading...
|
<div id="surrogate-area">
|
||||||
</div>
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" src="/jquery-1.12.4.min.js"></script>
|
<script type="text/javascript" src="/jquery-1.12.4.min.js"></script>
|
||||||
<script type="text/javascript" src="/sdk.js"></script>
|
<script type="text/javascript" src="/sdk.js"></script>
|
||||||
@@ -45,11 +61,16 @@ contented.init("#surrogate-area", function(items) {
|
|||||||
for (var i = 0; i < items.length; ++i) {
|
for (var i = 0; i < items.length; ++i) {
|
||||||
$table.append($("<tr>").append([
|
$table.append($("<tr>").append([
|
||||||
$("<td>").text(items[i]),
|
$("<td>").text(items[i]),
|
||||||
$("<td>").html("<a target='_blank' href='/get/" + items[i] + "'>get</a>"),
|
$("<td>").html("<a target='_blank' href='" + contented.getDownloadURL(items[i]) + "'>get</a>"),
|
||||||
$("<td>").html("<a target='_blank' href='/info/" + items[i] + "'>info</a>")
|
$("<td>").html("<a target='_blank' href='" + contented.getInfoJSONURL(items[i]) + "'>info</a>"),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
$("#surrogate-area").html($table);
|
$("#surrogate-area").html([
|
||||||
|
$table,
|
||||||
|
$("<button>").addClass("again").text("Again...").click(function() {
|
||||||
|
window.location.href = window.location.href;
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
639
static/sdk.js
639
static/sdk.js
@@ -1,214 +1,29 @@
|
|||||||
;
|
;
|
||||||
var contented = (function ($, currentScriptPath) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var baseURL = currentScriptPath.replace('sdk.js', '');
|
//
|
||||||
|
|
||||||
var formatBytes = function(bytes) {
|
var contented = (function() {
|
||||||
if (bytes < 1024) {
|
"use strict";
|
||||||
return bytes + " B";
|
|
||||||
} else if (bytes < (1024*1024)) {
|
// @ref https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
|
||||||
return (bytes / 1024).toFixed(1) + " KiB";
|
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||||
} else if (bytes < (1024*1024*1024)) {
|
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
|
||||||
return (bytes / (1024*1024)).toFixed(1) + " MiB";
|
value: function (callback, type, quality) {
|
||||||
} else {
|
|
||||||
return (bytes / (1024*1024*1024)).toFixed(1) + " GiB";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
var binStr = atob( this.toDataURL(type, quality).split(',')[1] ),
|
||||||
* supportsDrop returns whether drag-and-drop is supported by this browser.
|
len = binStr.length,
|
||||||
*
|
arr = new Uint8Array(len);
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
var supportsDrop = function () {
|
|
||||||
return ('ondrop' in window && 'FormData' in window && 'FileReader' in window);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
for (var i = 0; i < len; i++ ) {
|
||||||
* initArea shows the contented upload widget over the top of a target DOM element.
|
arr[i] = binStr.charCodeAt(i);
|
||||||
*
|
}
|
||||||
* @param any element Drop target (string selector / DOMElement / jQuery)
|
|
||||||
* @param Function onUploaded Called with an array of upload IDs
|
|
||||||
* @param Function onClose Called when the widget is being destroyed
|
|
||||||
*/
|
|
||||||
var initArea = function (elementSelector, onUploaded, onClose) {
|
|
||||||
onUploaded = onUploaded || function () { };
|
|
||||||
onClose = onClose || function () { };
|
|
||||||
|
|
||||||
if ($(elementSelector).length != 1) {
|
callback( new Blob( [arr], {type: type || 'image/png'} ) );
|
||||||
return; // should only find one element
|
}
|
||||||
}
|
});
|
||||||
var element = $(elementSelector)[0];
|
}
|
||||||
|
|
||||||
// <input type="hidden" name="MAX_FILE_SIZE" value="` + ret.MaxUploadBytes + `" />
|
|
||||||
|
|
||||||
// Create a new div for ourselves on top of the existing area
|
|
||||||
$.get(baseURL + "about", function (ret) {
|
|
||||||
|
|
||||||
var extraText = "";
|
|
||||||
if (ret.MaxUploadBytes > 0) {
|
|
||||||
extraText = " (max " + formatBytes(ret.MaxUploadBytes) + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
$.get(baseURL + "widget.html", function (widgetHtml) {
|
|
||||||
|
|
||||||
var $f = $("<div>").html(widgetHtml);
|
|
||||||
$f.find(".contented-extratext").text(extraText);
|
|
||||||
|
|
||||||
var ourClose = function () {
|
|
||||||
$f.remove(); // remove from dom
|
|
||||||
onClose(); // upstream close
|
|
||||||
};
|
|
||||||
$f.find(".contented-close").click(function () {
|
|
||||||
ourClose();
|
|
||||||
})
|
|
||||||
|
|
||||||
var setType = function (type) {
|
|
||||||
$f.find(".contented-upload-type").removeClass("contented-upload-type-active");
|
|
||||||
$f.find(".contented-upload-type[data-upload-type=" + type + "]").addClass("contented-upload-type-active");
|
|
||||||
|
|
||||||
$f.find(".contented-upload-if").removeClass("contented-active");
|
|
||||||
$f.find(".contented-if-" + type).addClass("contented-active");
|
|
||||||
};
|
|
||||||
|
|
||||||
$f.find(".contented-upload-type").click(function () {
|
|
||||||
setType($(this).attr('data-upload-type'));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!supportsDrop()) {
|
|
||||||
// switch default
|
|
||||||
setType('file');
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
var $element = $(element);
|
|
||||||
var offset = $element.offset();
|
|
||||||
|
|
||||||
$f.css({
|
|
||||||
'position': 'absolute',
|
|
||||||
'left': offset.left + "px",
|
|
||||||
'top': offset.top + "px",
|
|
||||||
'width': $element.width() + "px",
|
|
||||||
'min-width': $element.width() + "px",
|
|
||||||
'max-width': $element.width() + "px",
|
|
||||||
'height': $element.height() + "px",
|
|
||||||
'min-height': $element.height() + "px",
|
|
||||||
'max-height': $element.height() + "px"
|
|
||||||
});
|
|
||||||
|
|
||||||
$f.find('.contented').on('dragover dragenter', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
$(this).addClass('is-dragging');
|
|
||||||
});
|
|
||||||
|
|
||||||
$f.find('.contented').on('dragleave dragend', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
$(this).removeClass('is-dragging');
|
|
||||||
});
|
|
||||||
|
|
||||||
$f.find('.contented').on('drop', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
handleUploadFrom(e.originalEvent.dataTransfer.files);
|
|
||||||
});
|
|
||||||
|
|
||||||
$f.find('.contented-file-upload').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
handleUploadFrom($(".contented-file-selector")[0].files);
|
|
||||||
});
|
|
||||||
|
|
||||||
$f.find('.contented-paste-upload').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
var blob = new Blob([$(".contented-if-paste textarea").val()], {type : 'text/plain'});
|
|
||||||
handleUploadFrom([blob]);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("body").append($f);
|
|
||||||
|
|
||||||
var setProgressCaption = function(message) {
|
|
||||||
$f.find(".contented-if-progress label").text(message);
|
|
||||||
};
|
|
||||||
var setProgressPercentage = function(frc) {
|
|
||||||
$f.find(".contented-progress-element").css('width', (frc * 100) + "%");
|
|
||||||
};
|
|
||||||
|
|
||||||
var handleUploadFrom = function (files) {
|
|
||||||
|
|
||||||
setProgressCaption("Uploading, please wait...");
|
|
||||||
setProgressPercentage(0);
|
|
||||||
setType("progress");
|
|
||||||
|
|
||||||
$f.find(".contented-upload-type-selector").hide();
|
|
||||||
$f.find(".contented").removeClass('is-dragging');
|
|
||||||
|
|
||||||
// Ajax uploader
|
|
||||||
var ajaxData = new FormData();
|
|
||||||
for (var i = 0; i < files.length; ++i) {
|
|
||||||
ajaxData.append("f", files[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ajax request
|
|
||||||
$.ajax({
|
|
||||||
url: baseURL + "upload",
|
|
||||||
type: "POST",
|
|
||||||
data: ajaxData,
|
|
||||||
dataType: 'json', // response type
|
|
||||||
cache: false,
|
|
||||||
contentType: false,
|
|
||||||
processData: false,
|
|
||||||
xhr: function() {
|
|
||||||
var xhr = $.ajaxSettings.xhr();
|
|
||||||
xhr.upload.addEventListener(
|
|
||||||
'progress',
|
|
||||||
function(ev) {
|
|
||||||
console.log([ev.lengthComputable, ev.loaded, ev.total]);
|
|
||||||
if (ev.lengthComputable) {
|
|
||||||
setProgressCaption("Uploading (" + formatBytes(ev.loaded) + " / " + formatBytes(ev.total) + ")...");
|
|
||||||
setProgressPercentage(ev.total == 0 ? 0 : ev.loaded / ev.total);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
return xhr;
|
|
||||||
},
|
|
||||||
complete: function () {
|
|
||||||
setProgressCaption("Upload complete.");
|
|
||||||
setProgressPercentage(1);
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
onUploaded(data);
|
|
||||||
ourClose();
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
setProgressCaption("Upload failed.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
return {
|
|
||||||
'supportsDrop': supportsDrop,
|
|
||||||
'init': initArea
|
|
||||||
};
|
|
||||||
|
|
||||||
})(
|
|
||||||
jQuery,
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
|
var getCurrentScriptPath = function () {
|
||||||
// Determine current script path
|
// Determine current script path
|
||||||
// @ref https://stackoverflow.com/a/26023176
|
// @ref https://stackoverflow.com/a/26023176
|
||||||
var scripts = document.querySelectorAll('script[src]');
|
var scripts = document.querySelectorAll('script[src]');
|
||||||
@@ -217,5 +32,417 @@ var contented = (function ($, currentScriptPath) {
|
|||||||
var currentScriptFile = currentScriptChunks[currentScriptChunks.length - 1];
|
var currentScriptFile = currentScriptChunks[currentScriptChunks.length - 1];
|
||||||
|
|
||||||
return currentScript.replace(currentScriptFile, '');
|
return currentScript.replace(currentScriptFile, '');
|
||||||
})()
|
};
|
||||||
);
|
|
||||||
|
var currentScriptPath = getCurrentScriptPath();
|
||||||
|
var baseURL = currentScriptPath.replace('sdk.js', '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
"loaded": false,
|
||||||
|
|
||||||
|
"baseURL": baseURL,
|
||||||
|
|
||||||
|
"__preInit": [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initArea shows the contented upload widget over the top of a target DOM element.
|
||||||
|
*
|
||||||
|
* @param any element Drop target (string selector / DOMElement / jQuery)
|
||||||
|
* @param Function onUploaded Called with an array of upload IDs
|
||||||
|
* @param Function onClose Called when the widget is being destroyed
|
||||||
|
*/
|
||||||
|
"init": function(elementSelector, onUploaded, onClose) {
|
||||||
|
contented.__preInit.push([elementSelector, onUploaded, onClose]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* supportsDrop returns whether drag-and-drop is supported by this browser.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
"supportsDrop": function() {
|
||||||
|
return ('ondrop' in window && 'FormData' in window && 'FileReader' in window);
|
||||||
|
},
|
||||||
|
|
||||||
|
"getPreviewURL": function(id) {
|
||||||
|
return baseURL + "get/" + id; // n.b. there's no better preview URL yet
|
||||||
|
},
|
||||||
|
"getDownloadURL": function(id) {
|
||||||
|
return baseURL + "get/" + id;
|
||||||
|
},
|
||||||
|
"getInfoJSONURL": function(id) {
|
||||||
|
return baseURL + "info/" + id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
;(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var loadScript = function(url, onLoad) {
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.onload = onLoad;
|
||||||
|
script.src = url;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadScripts = function(urls, onLoad) {
|
||||||
|
if (urls.length === 0) {
|
||||||
|
onLoad();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalLoaded = 0;
|
||||||
|
var cb = function() {
|
||||||
|
totalLoaded += 1;
|
||||||
|
if (totalLoaded == urls.length) {
|
||||||
|
onLoad();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (var i = 0; i < urls.length; ++i) {
|
||||||
|
loadScript(urls[i], cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var formatBytes = function(bytes) {
|
||||||
|
var k = 1024, m = (1024*1024), g = (1024*1024*1024);
|
||||||
|
|
||||||
|
if (bytes < k) {
|
||||||
|
return bytes + " B";
|
||||||
|
} else if (bytes < m) {
|
||||||
|
return (bytes / k).toFixed(1) + " KiB";
|
||||||
|
} else if (bytes < g) {
|
||||||
|
return (bytes / m).toFixed(1) + " MiB";
|
||||||
|
} else {
|
||||||
|
return (bytes / g).toFixed(1) + " GiB";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ref https://stackoverflow.com/a/2117523
|
||||||
|
var guid = function uuidv4() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var afterScriptsLoaded = function() {
|
||||||
|
|
||||||
|
var initArea = function (elementSelector, onUploaded, onClose) {
|
||||||
|
onUploaded = onUploaded || function () { };
|
||||||
|
onClose = onClose || function () { };
|
||||||
|
|
||||||
|
if ($(elementSelector).length != 1) {
|
||||||
|
return; // should only find one element
|
||||||
|
}
|
||||||
|
var element = $(elementSelector)[0];
|
||||||
|
|
||||||
|
// <input type="hidden" name="MAX_FILE_SIZE" value="` + ret.MaxUploadBytes + `" />
|
||||||
|
|
||||||
|
// Create a new div for ourselves on top of the existing area
|
||||||
|
$.get(contented.baseURL + "about", function (ret) {
|
||||||
|
|
||||||
|
var extraText = "";
|
||||||
|
if (ret.MaxUploadBytes > 0) {
|
||||||
|
extraText = " (max " + formatBytes(ret.MaxUploadBytes) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
$.get(contented.baseURL + "widget.html", function (widgetHtml) {
|
||||||
|
|
||||||
|
var $f = $("<div>").html(widgetHtml);
|
||||||
|
$f.find(".contented-extratext").text(extraText);
|
||||||
|
|
||||||
|
// Tab buttons
|
||||||
|
|
||||||
|
var hasSetupDrawingBoardYet = false;
|
||||||
|
var setType = function (type) {
|
||||||
|
$f.find(".contented-upload-type").removeClass("contented-upload-type-active");
|
||||||
|
$f.find(".contented-upload-type[data-upload-type=" + type + "]").addClass("contented-upload-type-active");
|
||||||
|
|
||||||
|
$f.find(".contented-upload-if").removeClass("contented-active");
|
||||||
|
$f.find(".contented-if-" + type).addClass("contented-active");
|
||||||
|
|
||||||
|
if (type == "drag") {
|
||||||
|
enablePasteHandler();
|
||||||
|
} else {
|
||||||
|
disablePasteHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == "drawing" && !hasSetupDrawingBoardYet) {
|
||||||
|
setupDrawingBoard();
|
||||||
|
hasSetupDrawingBoardYet = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$f.find(".contented-upload-type").click(function () {
|
||||||
|
setType($(this).attr('data-upload-type'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Widget positioning
|
||||||
|
|
||||||
|
var $element = $(element);
|
||||||
|
var offset = $element.offset();
|
||||||
|
|
||||||
|
$f.css({
|
||||||
|
'position': 'absolute',
|
||||||
|
'left': offset.left + "px",
|
||||||
|
'top': offset.top + "px",
|
||||||
|
'width': $element.width() + "px",
|
||||||
|
'min-width': $element.width() + "px",
|
||||||
|
'max-width': $element.width() + "px",
|
||||||
|
'height': $element.height() + "px",
|
||||||
|
'min-height': $element.height() + "px",
|
||||||
|
'max-height': $element.height() + "px"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Drag and drop support
|
||||||
|
|
||||||
|
$f.find('.contented').on('dragover dragenter', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
$(this).addClass('is-dragging');
|
||||||
|
});
|
||||||
|
|
||||||
|
$f.find('.contented').on('dragleave dragend', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
$(this).removeClass('is-dragging');
|
||||||
|
});
|
||||||
|
|
||||||
|
$f.find('.contented').on('drop', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
handleUploadFrom(e.originalEvent.dataTransfer.files);
|
||||||
|
});
|
||||||
|
|
||||||
|
$f.find('.contented-file-upload').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
handleUploadFrom($(".contented-file-selector")[0].files);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pastebin
|
||||||
|
|
||||||
|
$f.find('.contented-paste-upload').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
var blob = new Blob([$(".contented-if-paste textarea").val()], {type : 'text/plain'});
|
||||||
|
handleUploadFrom([blob]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ctrl+V uploads
|
||||||
|
|
||||||
|
var pasteHandler = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var items = (e.clipboardData || e.originalEvent.clipboardData).items;
|
||||||
|
var items_length = items.length;
|
||||||
|
var blobs = [];
|
||||||
|
var handled = 0;
|
||||||
|
var haveHandled = function() {
|
||||||
|
handled += 1;
|
||||||
|
if (handled == items_length) {
|
||||||
|
|
||||||
|
if (blobs.length > 0) {
|
||||||
|
handleUploadFrom( blobs );
|
||||||
|
} else {
|
||||||
|
// alert("Pasted 0 files");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < items.length; ++i) {
|
||||||
|
var item = items[i];
|
||||||
|
var mimeType = item.type;
|
||||||
|
if (item.kind === 'file') {
|
||||||
|
blobs.push(item.getAsFile());
|
||||||
|
haveHandled();
|
||||||
|
|
||||||
|
} else if (item.kind === 'string') {
|
||||||
|
item.getAsString(function(s) {
|
||||||
|
blobs.push( new Blob([s], {type : mimeType}) );
|
||||||
|
haveHandled();
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// file|string are the only supported types in
|
||||||
|
// all browsers at the time of writing
|
||||||
|
// Ignore future possibilities
|
||||||
|
haveHandled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var enablePasteHandler = function() {
|
||||||
|
document.addEventListener('paste', pasteHandler);
|
||||||
|
};
|
||||||
|
var disablePasteHandler = function() {
|
||||||
|
document.removeEventListener('paste', pasteHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Embed in DOM, load default area
|
||||||
|
|
||||||
|
$("body").append($f);
|
||||||
|
if (!contented.supportsDrop()) {
|
||||||
|
setType('file');
|
||||||
|
} else {
|
||||||
|
setType('drag');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawing board
|
||||||
|
|
||||||
|
var setupDrawingBoard = function() {
|
||||||
|
|
||||||
|
var db_id = "contented-drawing-area-" + guid();
|
||||||
|
var $db = $("<div>").attr('id', db_id);
|
||||||
|
|
||||||
|
DrawingBoard.Control.ContentedUpload = DrawingBoard.Control.extend({
|
||||||
|
name: 'upload',
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
var $el = this.$el;
|
||||||
|
|
||||||
|
$el.append('<button class="contented-drawingboard-upload">Upload</button>');
|
||||||
|
$el.on('click', '.contented-drawingboard-upload', $.proxy(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
$el.prop('disabled', true);
|
||||||
|
$el.text('Saving...');
|
||||||
|
|
||||||
|
$db.find("canvas")[0].toBlob(function(theBlob) {
|
||||||
|
handleUploadFrom([ theBlob ]);
|
||||||
|
});
|
||||||
|
}, this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$db.css({
|
||||||
|
//'width': $f.find(".contented-content-area").width(),
|
||||||
|
'height': $f.find(".contented-content-area").height(),
|
||||||
|
'overflow': 'hidden'
|
||||||
|
});
|
||||||
|
|
||||||
|
$f.find(".contented-drawing-area").append($db);
|
||||||
|
var db = new DrawingBoard.Board(db_id, {
|
||||||
|
'controls': [
|
||||||
|
'Color',
|
||||||
|
'Size',
|
||||||
|
'DrawingMode',
|
||||||
|
'Navigation',
|
||||||
|
'ContentedUpload'
|
||||||
|
],
|
||||||
|
'controlsPosition': 'center',
|
||||||
|
'enlargeYourContainer': false,
|
||||||
|
'webStorage': false,
|
||||||
|
'droppable': false // don't mess with existing drop support
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Close button
|
||||||
|
|
||||||
|
var ourClose = function () {
|
||||||
|
$f.remove(); // remove from dom
|
||||||
|
disablePasteHandler();
|
||||||
|
onClose(); // upstream close
|
||||||
|
};
|
||||||
|
$f.find(".contented-close").click(function () {
|
||||||
|
ourClose();
|
||||||
|
})
|
||||||
|
|
||||||
|
// Progress bar
|
||||||
|
|
||||||
|
var setProgressCaption = function(message) {
|
||||||
|
$f.find(".contented-if-progress label").text(message);
|
||||||
|
};
|
||||||
|
var setProgressPercentage = function(frc) {
|
||||||
|
$f.find(".contented-progress-element").css('width', (frc * 100) + "%");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Common upload handler
|
||||||
|
|
||||||
|
var handleUploadFrom = function(files) {
|
||||||
|
|
||||||
|
setProgressCaption("Uploading, please wait...");
|
||||||
|
setProgressPercentage(0);
|
||||||
|
setType("progress");
|
||||||
|
|
||||||
|
$f.find(".contented-upload-type-selector").hide();
|
||||||
|
$f.find(".contented").removeClass('is-dragging');
|
||||||
|
|
||||||
|
// Ajax uploader
|
||||||
|
var ajaxData = new FormData();
|
||||||
|
for (var i = 0; i < files.length; ++i) {
|
||||||
|
ajaxData.append("f", files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ajax request
|
||||||
|
$.ajax({
|
||||||
|
url: contented.baseURL + "upload",
|
||||||
|
type: "POST",
|
||||||
|
data: ajaxData,
|
||||||
|
dataType: 'json', // response type
|
||||||
|
cache: false,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
xhr: function() {
|
||||||
|
var xhr = $.ajaxSettings.xhr();
|
||||||
|
xhr.upload.addEventListener(
|
||||||
|
'progress',
|
||||||
|
function(ev) {
|
||||||
|
if (ev.lengthComputable) {
|
||||||
|
setProgressCaption("Uploading (" + formatBytes(ev.loaded) + " / " + formatBytes(ev.total) + ")...");
|
||||||
|
setProgressPercentage(ev.total == 0 ? 0 : ev.loaded / ev.total);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return xhr;
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
setProgressPercentage(1);
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
setProgressCaption("Upload completed successfully.");
|
||||||
|
onUploaded(data);
|
||||||
|
ourClose();
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
setProgressCaption("Upload failed!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// .
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fields in global variable
|
||||||
|
contented.init = initArea;
|
||||||
|
contented.loaded = true;
|
||||||
|
|
||||||
|
// Call initArea for all pre-initialised elements
|
||||||
|
for (var i = 0; i < contented.__preInit.length; ++i) {
|
||||||
|
initArea(contented.__preInit[i][0], contented.__preInit[i][1], contented.__preInit[i][2]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load scripts
|
||||||
|
var needScripts = [];
|
||||||
|
if (typeof jQuery === "undefined") {
|
||||||
|
needScripts.push("jquery-1.12.4.min.js");
|
||||||
|
}
|
||||||
|
if (typeof DrawingBoard === "undefined") {
|
||||||
|
needScripts.push("drawingboard-0.4.6.min.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
loadScripts(needScripts, afterScriptsLoaded);
|
||||||
|
|
||||||
|
})()
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
.contented .contented-upload-type-selector {
|
.contented .contented-upload-type-selector {
|
||||||
display:block;
|
display:block;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
.contented .contented-upload-type {
|
.contented .contented-upload-type {
|
||||||
@@ -66,7 +67,7 @@
|
|||||||
.contented-upload-if {
|
.contented-upload-if {
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
.contented-if-paste {
|
.contented-if-paste, .contented-if-drawing {
|
||||||
height:100%;
|
height:100%;
|
||||||
}
|
}
|
||||||
.contented-upload-if.contented-active {
|
.contented-upload-if.contented-active {
|
||||||
@@ -122,11 +123,17 @@
|
|||||||
<path fill="#000000" d="M19,20H5V4H7V7H17V4H19M12,2A1,1 0 0,1 13,3A1,1 0 0,1 12,4A1,1 0 0,1 11,3A1,1 0 0,1 12,2M19,2H14.82C14.4,0.84 13.3,0 12,0C10.7,0 9.6,0.84 9.18,2H5A2,2 0 0,0 3,4V20A2,2 0 0,0 5,22H19A2,2 0 0,0 21,20V4A2,2 0 0,0 19,2Z" />
|
<path fill="#000000" d="M19,20H5V4H7V7H17V4H19M12,2A1,1 0 0,1 13,3A1,1 0 0,1 12,4A1,1 0 0,1 11,3A1,1 0 0,1 12,2M19,2H14.82C14.4,0.84 13.3,0 12,0C10.7,0 9.6,0.84 9.18,2H5A2,2 0 0,0 3,4V20A2,2 0 0,0 5,22H19A2,2 0 0,0 21,20V4A2,2 0 0,0 19,2Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="contented-upload-type" data-upload-type="drawing" title="Drawing">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path fill="#000000" d="M16.84,2.73C16.45,2.73 16.07,2.88 15.77,3.17L13.65,5.29L18.95,10.6L21.07,8.5C21.67,7.89 21.67,6.94 21.07,6.36L17.9,3.17C17.6,2.88 17.22,2.73 16.84,2.73M12.94,6L4.84,14.11L7.4,14.39L7.58,16.68L9.86,16.85L10.15,19.41L18.25,11.3M4.25,15.04L2.5,21.73L9.2,19.94L8.96,17.78L6.65,17.61L6.47,15.29" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="contented-content-area">
|
<div class="contented-content-area">
|
||||||
<div class="contented-upload-if contented-if-drag contented-active">
|
<div class="contented-upload-if contented-if-drag contented-active">
|
||||||
<label>Drop files to upload <span class="contented-extratext"></span></label>
|
<label>Drop files or Ctrl-V to upload <span class="contented-extratext"></span></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="contented-upload-if contented-if-file">
|
<div class="contented-upload-if contented-if-file">
|
||||||
@@ -140,9 +147,14 @@
|
|||||||
<button class="contented-paste-upload">Upload »</button>
|
<button class="contented-paste-upload">Upload »</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="contented-upload-if contented-if-drawing">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/drawingboard-0.4.6.min.css">
|
||||||
|
<div class="contented-drawing-area"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="contented-upload-if contented-if-progress">
|
<div class="contented-upload-if contented-if-progress">
|
||||||
<label>...</label>
|
<label>...</label>
|
||||||
<div class="contented-progress-bar"><div class="contented-progress-element"></div></div>
|
<div class="contented-progress-bar"><div class="contented-progress-element"></div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
14
upload.go
14
upload.go
@@ -18,15 +18,17 @@ import (
|
|||||||
|
|
||||||
func (this *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
|
func (this *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
remoteIP := this.remoteIP(r)
|
||||||
|
|
||||||
err := r.ParseMultipartForm(this.opts.MaxUploadBytes * 2)
|
err := r.ParseMultipartForm(this.opts.MaxUploadBytes * 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s Invalid request: %s\n", r.RemoteAddr, err.Error())
|
log.Printf("%s Invalid request: %s\n", remoteIP, err.Error())
|
||||||
http.Error(w, "Invalid request", 400)
|
http.Error(w, "Invalid request", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.MultipartForm == nil || r.MultipartForm.File == nil || len(r.MultipartForm.File["f"]) < 1 {
|
if r.MultipartForm == nil || r.MultipartForm.File == nil || len(r.MultipartForm.File["f"]) < 1 {
|
||||||
log.Printf("%s Invalid request: no multipart content\n", r.RemoteAddr)
|
log.Printf("%s Invalid request: no multipart content\n", remoteIP)
|
||||||
http.Error(w, "Invalid request", 400)
|
http.Error(w, "Invalid request", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -36,14 +38,14 @@ func (this *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
|
|||||||
for _, fhs := range r.MultipartForm.File["f"] {
|
for _, fhs := range r.MultipartForm.File["f"] {
|
||||||
f, err := fhs.Open()
|
f, err := fhs.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s Internal error: %s\n", r.RemoteAddr, err.Error())
|
log.Printf("%s Internal error: %s\n", remoteIP, err.Error())
|
||||||
http.Error(w, "Internal error", 500)
|
http.Error(w, "Internal error", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := this.handleUploadFile(f, fhs, remoteIP(r))
|
path, err := this.handleUploadFile(f, fhs, remoteIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s Upload failed: %s\n", r.RemoteAddr, err.Error())
|
log.Printf("%s Upload failed: %s\n", remoteIP, err.Error())
|
||||||
http.Error(w, "Upload failed", 500)
|
http.Error(w, "Upload failed", 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ func (this *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
jb, err := json.Marshal(ret)
|
jb, err := json.Marshal(ret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s Internal error: %s\n", r.RemoteAddr, err.Error())
|
log.Printf("%s Internal error: %s\n", remoteIP, err.Error())
|
||||||
http.Error(w, "Internal error", 500)
|
http.Error(w, "Internal error", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user