* fixed Issue 157: Upload area tagging file names with 'undefined' and refusing to upload.

* updated plupload to 1.5.1.1

git-svn-id: http://redmine-dmsf.googlecode.com/svn/trunk/redmine_dmsf@248 5e329b0b-a2ee-ea63-e329-299493fc886d
This commit is contained in:
vit.jonas@gmail.com 2011-10-06 08:14:09 +00:00
parent 2e862ae750
commit a52382ad8b
14 changed files with 538 additions and 1115 deletions

View File

@ -0,0 +1,33 @@
// .fi file like language pack
plupload.addI18n({
'Select files' : 'Valitse tiedostoja',
'Add files to the upload queue and click the start button.' : 'Lisää tiedostoja latausjonoon ja klikkaa aloita-nappia.',
'Filename' : 'Tiedostonimi',
'Status' : 'Tila',
'Size' : 'Koko',
'Add files' : 'Lisää tiedostoja',
'Stop current upload' : 'Pysäytä nykyinen lataus',
'Start uploading queue' : 'Aloita jonon lataus',
'Drag files here.' : 'Raahaa tiedostot tänne.',
'Start upload' : 'Aloita lataus',
'Uploaded %d/%d files': 'Ladattu %d/%d tiedostoa',
'Stop upload': 'Pysäytä lataus',
'Start upload': 'Aloita lataus',
'%d files queued': '%d tiedostoa jonossa',
'File: %s': 'Tiedosto: %s',
'Close': 'Sulje',
'Using runtime: ': 'Käytetään ajonaikaista: ',
'File: %f, size: %s, max file size: %m': 'Tiedosto: %f, koko: %s, maksimi tiedostokoko: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Latauselementti sallii ladata vain %d tiedosto(a) kerrallaan. Ylimääräiset tiedostot ohitettiin.',
'Upload URL might be wrong or doesn\'t exist': 'Lataus URL saattaa olla väärin tai ei ole olemassa',
'Error: File too large: ': 'Virhe: Tiedosto liian suuri: ',
'Error: Invalid file extension: ': 'Virhe: Kelpaamaton tiedostopääte: ',
'File extension error.': 'Tiedostopäätevirhe.',
'File size error.': 'Tiedostokokovirhe.',
'File count error.': 'Tiedostolaskentavirhe.',
'Init error.': 'Init virhe.',
'HTTP Error.': 'HTTP virhe.',
'Security error.': 'Tietoturvavirhe.',
'Generic error.': 'Yleinen virhe.',
'IO error.': 'I/O virhe.'
});

View File

@ -1,25 +1,37 @@
// Japanese
plupload.addI18n({
'Select files' : 'ファイルの選択:',
'Add files to the upload queue and click the start button.' : 'アップロードキューにファイルを追加しスタートボタンをクリックしてください。',
'Filename' : 'ファイル名',
'Status' : '進行状況',
'Size' : 'サイズ',
'Add files' : 'ファイルの追加',
'Stop current upload' : '現在のアップロードを中止',
'Start uploading queue' : 'キューのアップロードを開始',
'Uploaded %d/%d files': '%d/%d 個のファイルをアップロードしました',
'N/A' : 'N/A',
'Drag files here.' : 'ここにファイルをドラッグしてください。',
'File extension error.': 'ファイル拡張子エラーです。',
'File size error.': 'ファイルサイズエラーです。',
'Init error.': '初期化エラーです。',
'HTTP Error.': 'HTTP エラーです。',
'Security error.': 'セキュリティエラーです。',
'Generic error.': '原因不明のエラーです。',
'IO error.': 'I/O エラーです。',
'Stop Upload': 'アップロードの中止',
'Add Files': 'ファイルの追加',
'Start Upload': 'アップロードの開始',
'%d files queued': '%d 個のファイルがキューにあります。'
});
// Japanese
plupload.addI18n({
'Select files' : 'ファイル選択',
'Add files to the upload queue and click the start button.' : 'ファイルをアップロードキューに追加してスタートボタンをクリックしてください',
'Filename' : 'ファイル名',
'Status' : 'ステータス',
'Size' : 'サイズ',
'Add Files' : 'ファイルを追加',
'Stop Upload' : 'アップロード停止',
'Start Upload' : 'アップロード',
'Add files' : 'ファイルを追加',
'Add files.' : 'ファイルを追加',
'Stop current upload' : '現在のアップロードを停止',
'Start uploading queue' : 'アップロード',
'Stop upload' : 'アップロード停止',
'Start upload' : 'アップロード',
'Uploaded %d/%d files': 'アップロード中 %d/%d ファイル',
'N/A' : 'N/A',
'Drag files here.' : 'ここにファイルをドラッグ',
'File extension error.': 'ファイル拡張子エラー',
'File size error.': 'ファイルサイズエラー',
'File count error.': 'ファイル数エラー',
'Init error.': 'イニシャライズエラー',
'HTTP Error.': 'HTTP エラー',
'Security error.': 'セキュリティエラー',
'Generic error.': 'エラー',
'IO error.': 'IO エラー',
'File: %s': 'ファイル: %s',
'Close': '閉じる',
'%d files queued': '%d ファイルが追加されました',
'Using runtime: ': 'モード: ',
'File: %f, size: %s, max file size: %m': 'ファイル: %f, サイズ: %s, 最大ファイルサイズ: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'アップロード可能なファイル数は %d です。余分なファイルは削除されました',
'Upload URL might be wrong or doesn\'t exist': 'アップロード先の URL が存在しません',
'Error: File too large: ': 'エラー: サイズが大きすぎます: ',
'Error: Invalid file extension: ': 'エラー: 拡張子が許可されていません: '
});

View File

@ -20,7 +20,7 @@ plupload.addI18n({
'File: %f, size: %s, max file size: %m': 'Fails: %f, izmērs: %s, maksimālais faila izmērs: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Iespējams ielādēt tikai %d failus vienā reizē. Atlikušie faili netika pievienoti',
'Upload URL might be wrong or doesn\'t exist': 'Augšupielādes URL varētu būt nepareizs vai neeksistē',
'Error: File to large: ': 'Kļūda: Fails pārāk liels: ',
'Error: File too large: ': 'Kļūda: Fails pārāk liels: ',
'Error: Invalid file extension: ': 'Kļūda: Nekorekts faila paplašinājums:',
'File extension error.': 'Faila paplašinājuma kļūda.',
'File size error.': 'Faila izmēra kļūda.',

View File

@ -30,6 +30,6 @@ plupload.addI18n({
'File: %f, size: %s, max file size: %m': 'Arquivo: %f, tamanho: %s, máximo: %m',
'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Só são aceitos %d arquivos por vez. O que passou disso foi descartado.',
'Upload URL might be wrong or doesn\'t exist': 'URL de envio está errada ou não existe',
'Error: File to large: ': 'Erro: Arquivo muito grande: ',
'Error: File too large: ': 'Erro: Arquivo muito grande: ',
'Error: Invalid file extension: ': 'Erro: Tipo de arquivo não permitido: '
});

View File

@ -37,7 +37,7 @@ function renderUI(obj) {
'<div class="plupload_header_content">' +
'</div>' +
'</div>' +
'<div class="plupload_content">' +
'<table class="plupload_filelist">' +
'<tr class="ui-widget-header plupload_filelist_header">' +
@ -133,7 +133,11 @@ $.widget("ui.plupload", {
this.container = $('.plupload_container', this.element).attr('id', id + '_container');
// list of files, may become sortable
this.filelist = $('.plupload_filelist_content', this.container).attr('id', id + '_filelist');
this.filelist = $('.plupload_filelist_content', this.container)
.attr({
id: id + '_filelist',
unselectable: 'on'
});
// buttons
this.browse_button = $('.plupload_add', this.container).attr('id', id + '_browse');
@ -217,12 +221,12 @@ $.widget("ui.plupload", {
// check if file count doesn't exceed the limit
if (self.options.max_file_count) {
uploader.bind('FilesAdded', function(up, files) {
var length = files.length, removed = [];
length += up.files.length;
uploader.bind('FilesAdded', function(up, selectedFiles) {
var removed = [], selectedCount = selectedFiles.length;
var extraCount = up.files.length + selectedCount - self.options.max_file_count;
if (length > self.options.max_file_count) {
removed = files.splice(self.options.max_file_count - up.files.length);
if (extraCount > 0) {
removed = selectedFiles.splice(selectedCount - extraCount, extraCount);
up.trigger('Error', {
code : self.FILE_COUNT_ERROR,
@ -336,7 +340,7 @@ $.widget("ui.plupload", {
message += " <br /><i>" + details + "</i>";
}
self._notify('error', message);
self.notify('error', message);
self._trigger('error', null, { up: up, file: file, error: message } );
}
});
@ -651,24 +655,12 @@ $.widget("ui.plupload", {
return el.clone(true).find('td:not(.plupload_file_name)').remove().end().css('width', '100%');
},
start: function(e, ui) {
idxStart = $('tr', this).index(ui.item);
},
stop: function(e, ui) {
var i, length, idx, files = [], idxStop = $('tr', this).index(ui.item);
for (i = 0, length = self.uploader.files.length; i < length; i++) {
if (i === idxStop) {
idx = idxStart;
} else if (i === idxStart) {
idx = idxStop;
} else {
idx = i;
}
files[files.length] = self.uploader.files[idx];
}
var i, length, idx, files = [];
$.each($(this).sortable('toArray'), function(i, id) {
files[files.length] = self.uploader.getFile(id);
});
files.unshift(files.length);
files.unshift(0);
@ -679,7 +671,7 @@ $.widget("ui.plupload", {
});
},
_notify: function(type, message) {
notify: function(type, message) {
var popup = $(
'<div class="plupload_message">' +
'<span class="plupload_message_close ui-icon ui-icon-circle-close" title="'+_('Close')+'"></span>' +
@ -696,8 +688,9 @@ $.widget("ui.plupload", {
.click(function() {
popup.remove();
})
.end()
.appendTo('.plupload_header_content', this.container);
.end();
$('.plupload_header_content', this.container).append(popup);
},

View File

@ -1,337 +0,0 @@
/**
* plupload.browserplus.js
*
* Copyright 2009, Moxiecode Systems AB
* Released under GPL License.
*
* License: http://www.plupload.com/license
* Contributing: http://www.plupload.com/contributing
*/
// JSLint defined globals
/*global plupload:false, BrowserPlus:false, window:false */
(function(plupload) {
/**
* Yahoo BrowserPlus implementation. This runtime supports these features: dragdrop, jpgresize, pngresize.
*
* @static
* @class plupload.runtimes.BrowserPlus
* @extends plupload.Runtime
*/
plupload.runtimes.BrowserPlus = plupload.addRuntime("browserplus", {
/**
* Returns a list of supported features for the runtime.
*
* @return {Object} Name/value object with supported features.
*/
getFeatures : function() {
return {
dragdrop : true,
jpgresize : true,
pngresize : true,
chunks : true,
progress: true,
multipart: true
};
},
/**
* Initializes the browserplus runtime.
*
* @method init
* @param {plupload.Uploader} uploader Uploader instance that needs to be initialized.
* @param {function} callback Callback to execute when the runtime initializes or fails to initialize. If it succeeds an object with a parameter name success will be set to true.
*/
init : function(uploader, callback) {
var browserPlus = window.BrowserPlus, browserPlusFiles = {}, settings = uploader.settings, resize = settings.resize;
function addSelectedFiles(native_files) {
var files, i, selectedFiles = [], file, id;
// Add the native files and setup plupload files
for (i = 0; i < native_files.length; i++) {
file = native_files[i];
id = plupload.guid();
browserPlusFiles[id] = file;
selectedFiles.push(new plupload.File(id, file.name, file.size));
}
// Any files selected fire event
if (i) {
uploader.trigger("FilesAdded", selectedFiles);
}
}
// Setup event listeners if browserplus was initialized
function setup() {
// Add drop handler
uploader.bind("PostInit", function() {
var dropTargetElm, dropElmId = settings.drop_element,
dropTargetId = uploader.id + '_droptarget',
dropElm = document.getElementById(dropElmId),
lastState;
// Enable/disable drop support for the drop target
// this is needed to resolve IE bubbeling issues and make it possible to drag/drop
// files into gears runtimes on the same page
function addDropHandler(id, end_callback) {
// Add drop target and listener
browserPlus.DragAndDrop.AddDropTarget({id : id}, function(res) {
browserPlus.DragAndDrop.AttachCallbacks({
id : id,
hover : function(res) {
if (!res && end_callback) {
end_callback();
}
},
drop : function(res) {
if (end_callback) {
end_callback();
}
addSelectedFiles(res);
}
}, function() {
});
});
}
function hide() {
document.getElementById(dropTargetId).style.top = '-1000px';
}
if (dropElm) {
// Since IE has issues with bubbeling when it comes to the drop of files
// we need to do this hack where we show a drop target div element while dropping
if (document.attachEvent && (/MSIE/gi).test(navigator.userAgent)) {
// Create drop target
dropTargetElm = document.createElement('div');
dropTargetElm.setAttribute('id', dropTargetId);
plupload.extend(dropTargetElm.style, {
position : 'absolute',
top : '-1000px',
background : 'red',
filter : 'alpha(opacity=0)',
opacity : 0
});
document.body.appendChild(dropTargetElm);
plupload.addEvent(dropElm, 'dragenter', function(e) {
var dropElm, dropElmPos;
dropElm = document.getElementById(dropElmId);
dropElmPos = plupload.getPos(dropElm);
plupload.extend(document.getElementById(dropTargetId).style, {
top : dropElmPos.y + 'px',
left : dropElmPos.x + 'px',
width : dropElm.offsetWidth + 'px',
height : dropElm.offsetHeight + 'px'
});
});
addDropHandler(dropTargetId, hide);
} else {
addDropHandler(dropElmId);
}
}
plupload.addEvent(document.getElementById(settings.browse_button), 'click', function(e) {
var mimeTypes = [], i, a, filters = settings.filters, ext;
e.preventDefault();
// Convert extensions to mimetypes
for (i = 0; i < filters.length; i++) {
ext = filters[i].extensions.split(',');
for (a = 0; a < ext.length; a++) {
mimeTypes.push(plupload.mimeTypes[ext[a]]);
}
}
browserPlus.FileBrowse.OpenBrowseDialog({
mimeTypes : mimeTypes
}, function(res) {
if (res.success) {
addSelectedFiles(res.value);
}
});
});
// Prevent IE leaks
dropElm = dropTargetElm = null;
});
uploader.bind("UploadFile", function(up, file) {
var nativeFile = browserPlusFiles[file.id], reqParams = {},
chunkSize = up.settings.chunk_size, loadProgress, chunkStack = [];
function uploadFile(chunk, chunks) {
var chunkFile;
// Stop upload if file is maked as failed
if (file.status == plupload.FAILED) {
return;
}
reqParams.name = file.target_name || file.name;
// Only send chunk parameters if chunk size is defined
if (chunkSize) {
reqParams.chunk = "" + chunk;
reqParams.chunks = "" + chunks;
}
chunkFile = chunkStack.shift();
browserPlus.Uploader.upload({
url : up.settings.url,
files : {file : chunkFile},
cookies : document.cookies,
postvars : plupload.extend(reqParams, up.settings.multipart_params),
progressCallback : function(res) {
var i, loaded = 0;
// since more than 1 chunk can be sent at a time, keep track of how many bytes
// of each chunk was sent
loadProgress[chunk] = parseInt(res.filePercent * chunkFile.size / 100, 10);
for (i = 0; i < loadProgress.length; i++) {
loaded += loadProgress[i];
}
file.loaded = loaded;
up.trigger('UploadProgress', file);
}
}, function(res) {
var httpStatus, chunkArgs;
if (res.success) {
httpStatus = res.value.statusCode;
if (chunkSize) {
up.trigger('ChunkUploaded', file, {
chunk : chunk,
chunks : chunks,
response : res.value.body,
status : httpStatus
});
}
if (chunkStack.length > 0) {
// More chunks to be uploaded
uploadFile(++chunk, chunks);
} else {
file.status = plupload.DONE;
up.trigger('FileUploaded', file, {
response : res.value.body,
status : httpStatus
});
// Is error status
if (httpStatus >= 400) {
up.trigger('Error', {
code : plupload.HTTP_ERROR,
message : plupload.translate('HTTP Error.'),
file : file,
status : httpStatus
});
}
}
} else {
up.trigger('Error', {
code : plupload.GENERIC_ERROR,
message : plupload.translate('Generic Error.'),
file : file,
details : res.error
});
}
});
}
function chunkAndUploadFile(native_file) {
file.size = native_file.size;
if (chunkSize) {
browserPlus.FileAccess.chunk({file : native_file, chunkSize : chunkSize}, function(cr) {
if (cr.success) {
var chunks = cr.value, len = chunks.length;
loadProgress = Array(len);
for (var i = 0; i < len; i++) {
loadProgress[i] = 0;
chunkStack.push(chunks[i]);
}
uploadFile(0, len);
}
});
} else {
loadProgress = Array(1);
chunkStack.push(native_file);
uploadFile(0, 1);
}
}
// Resize image if it's a supported format and resize is enabled
if (resize && /\.(png|jpg|jpeg)$/i.test(file.name)) {
BrowserPlus.ImageAlter.transform({
file : nativeFile,
quality : resize.quality || 90,
actions : [{
scale : {
maxwidth : resize.width,
maxheight : resize.height
}
}]
}, function(res) {
if (res.success) {
chunkAndUploadFile(res.value.file);
}
});
} else {
chunkAndUploadFile(nativeFile);
}
});
callback({success : true});
}
// Check for browserplus object
if (browserPlus) {
browserPlus.init(function(res) {
var services = [
{service: "Uploader", version: "3"},
{service: "DragAndDrop", version: "1"},
{service: "FileBrowse", version: "1"},
{service: "FileAccess", version: "2"}
];
if (resize) {
services.push({service : 'ImageAlter', version : "4"});
}
if (res.success) {
browserPlus.require({
services : services
}, function(sres) {
if (sres.success) {
setup();
} else {
callback();
}
});
} else {
callback();
}
});
} else {
callback();
}
}
});
})(plupload);

View File

@ -42,7 +42,7 @@
* @param {Object} obj Parameters to be passed with event.
*/
trigger : function(id, name, obj) {
// Detach the call so that error handling in the browser is presented correctly
setTimeout(function() {
var uploader = uploadInstances[id], i, args;
@ -76,7 +76,8 @@
maxHeight: 8091,
chunks: true,
progress: true,
multipart: true
multipart: true,
multi_selection: true
};
},
@ -88,12 +89,7 @@
* @param {function} callback Callback to execute when the runtime initializes or fails to initialize. If it succeeds an object with a parameter name success will be set to true.
*/
init : function(uploader, callback) {
var browseButton, flashContainer, flashVars, waitCount = 0, container = document.body;
if (bowser.opera) {
callback({success : false});
return;
}
var browseButton, flashContainer, waitCount = 0, container = document.body;
if (getFlashVersion() < 10) {
callback({success : false});
@ -129,15 +125,33 @@
}
container.appendChild(flashContainer);
// insert flash object
(function() {
var html, el;
html = '<object id="' + uploader.id + '_flash" type="application/x-shockwave-flash" data="' + uploader.settings.flash_swf_url + '" ';
if (plupload.ua.ie) {
html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '
}
flashVars = 'id=' + escape(uploader.id);
// Insert the Flash inide the flash container
flashContainer.innerHTML = '<object id="' + uploader.id + '_flash" width="100%" height="100%" style="outline:0" type="application/x-shockwave-flash" data="' + uploader.settings.flash_swf_url + '">' +
'<param name="movie" value="' + uploader.settings.flash_swf_url + '" />' +
'<param name="flashvars" value="' + flashVars + '" />' +
'<param name="wmode" value="transparent" />' +
'<param name="allowscriptaccess" value="always" /></object>';
html += 'width="100%" height="100%" style="outline:0">' +
'<param name="movie" value="' + uploader.settings.flash_swf_url + '" />' +
'<param name="flashvars" value="id=' + escape(uploader.id) + '" />' +
'<param name="wmode" value="transparent" />' +
'<param name="allowscriptaccess" value="always" />' +
'</object>';
if (plupload.ua.ie) {
el = document.createElement('div');
flashContainer.appendChild(el);
el.outerHTML = html;
el = null; // just in case
} else {
flashContainer.innerHTML = html;
}
}());
function getFlashObj() {
return document.getElementById(uploader.id + '_flash');
@ -334,6 +348,17 @@
plupload.removeClass(browseButton, activeClass);
}
});
uploader.bind('Flash:ExifData', function(up, obj) {
uploader.trigger('ExifData', uploader.getFile(lookup[obj.id]), obj.data);
});
uploader.bind('Flash:GpsData', function(up, obj) {
uploader.trigger('GpsData', uploader.getFile(lookup[obj.id]), obj.data);
});
uploader.bind("QueueChanged", function(up) {
uploader.refresh();

View File

@ -1,420 +0,0 @@
/**
* plupload.gears.js
*
* Copyright 2009, Moxiecode Systems AB
* Released under GPL License.
*
* License: http://www.plupload.com/license
* Contributing: http://www.plupload.com/contributing
*/
// JSLint defined globals
/*global window:false, document:false, plupload:false, google:false, GearsFactory:false, ActiveXObject:false */
// Copyright 2007, Google Inc.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// 3. Neither the name of Google Inc. nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Sets up google.gears.*, which is *the only* supported way to access Gears.
//
// Circumvent this file at your own risk!
//
// In the future, Gears may automatically define google.gears.* without this
// file. Gears may use these objects to transparently fix bugs and compatibility
// issues. Applications that use the code below will continue to work seamlessly
// when that happens.
(function() {
// We are already defined. Hooray!
if (window.google && google.gears) {
return;
}
var factory = null;
// Firefox
if (typeof GearsFactory != 'undefined') {
factory = new GearsFactory();
} else {
// IE
try {
factory = new ActiveXObject('Gears.Factory');
// privateSetGlobalObject is only required and supported on WinCE.
if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
factory.privateSetGlobalObject(this);
}
} catch (e) {
// Safari
if ((typeof navigator.mimeTypes != 'undefined') && navigator.mimeTypes["application/x-googlegears"]) {
factory = document.createElement("object");
factory.style.display = "none";
factory.width = 0;
factory.height = 0;
factory.type = "application/x-googlegears";
document.documentElement.appendChild(factory);
}
}
}
// *Do not* define any objects if Gears is not installed. This mimics the
// behavior of Gears defining the objects in the future.
if (!factory) {
return;
}
// Now set up the objects, being careful not to overwrite anything.
//
// Note: In Internet Explorer for Windows Mobile, you can't add properties to
// the window object. However, global objects are automatically added as
// properties of the window object in all browsers.
if (!window.google) {
window.google = {};
}
if (!google.gears) {
google.gears = {factory: factory};
}
})();
(function(window, document, plupload, undef) {
var blobs = {};
function scaleImage(image_blob, resize, mime) {
var percentage, canvas, context, scale;
// Setup canvas and scale
canvas = google.gears.factory.create('beta.canvas');
try {
canvas.decode(image_blob);
if (!resize['width']) {
resize['width'] = canvas.width;
}
if (!resize['height']) {
resize['height'] = canvas.height;
}
scale = Math.min(width / canvas.width, height / canvas.height);
if (scale < 1 || (scale === 1 && mime === 'image/jpeg')) {
canvas.resize(Math.round(canvas.width * scale), Math.round(canvas.height * scale));
if (resize['quality']) {
return canvas.encode(mime, {quality : resize.quality / 100});
}
return canvas.encode(mime);
}
} catch (e) {
// Ignore for example when a user uploads a file that can't be decoded
}
return image_blob;
}
/**
* Gears implementation. This runtime supports these features: dragdrop, jpgresize, pngresize, chunks.
*
* @static
* @class plupload.runtimes.Gears
* @extends plupload.Runtime
*/
plupload.runtimes.Gears = plupload.addRuntime("gears", {
/**
* Returns a list of supported features for the runtime.
*
* @return {Object} Name/value object with supported features.
*/
getFeatures : function() {
return {
dragdrop: true,
jpgresize: true,
pngresize: true,
chunks: true,
progress: true,
multipart: true
};
},
/**
* Initializes the upload runtime.
*
* @method init
* @param {plupload.Uploader} uploader Uploader instance that needs to be initialized.
* @param {function} callback Callback to execute when the runtime initializes or fails to initialize. If it succeeds an object with a parameter name success will be set to true.
*/
init : function(uploader, callback) {
var desktop;
// Check for gears support
if (!window.google || !google.gears) {
return callback({success : false});
}
try {
desktop = google.gears.factory.create('beta.desktop');
} catch (e) {
// Might fail on the latest Gecko build for some odd reason
return callback({success : false});
}
function addSelectedFiles(selected_files) {
var file, i, files = [], id;
// Add the selected files to the file queue
for (i = 0; i < selected_files.length; i++) {
file = selected_files[i];
// Store away gears blob internally
id = plupload.guid();
blobs[id] = file.blob;
files.push(new plupload.File(id, file.name, file.blob.length));
}
// Fire FilesAdded event
uploader.trigger("FilesAdded", files);
}
// Add drop handler
uploader.bind("PostInit", function() {
var settings = uploader.settings, dropElm = document.getElementById(settings.drop_element);
if (dropElm) {
// Block browser default drag over
plupload.addEvent(dropElm, 'dragover', function(e) {
desktop.setDropEffect(e, 'copy');
e.preventDefault();
}, uploader.id);
// Attach drop handler and grab files from Gears
plupload.addEvent(dropElm, 'drop', function(e) {
var dragData = desktop.getDragData(e, 'application/x-gears-files');
if (dragData) {
addSelectedFiles(dragData.files);
}
e.preventDefault();
}, uploader.id);
// Prevent IE leak
dropElm = 0;
}
// Add browse button
plupload.addEvent(document.getElementById(settings.browse_button), 'click', function(e) {
var filters = [], i, a, ext;
e.preventDefault();
for (i = 0; i < settings.filters.length; i++) {
ext = settings.filters[i].extensions.split(',');
for (a = 0; a < ext.length; a++) {
filters.push('.' + ext[a]);
}
}
desktop.openFiles(addSelectedFiles, {singleFile : !settings.multi_selection, filter : filters});
}, uploader.id);
});
uploader.bind("UploadFile", function(up, file) {
var chunk = 0, chunks, chunkSize, loaded = 0, resize = up.settings.resize, chunking;
// If file is png or jpeg and resize is configured then resize it
if (resize && /\.(png|jpg|jpeg)$/i.test(file.name)) {
blobs[file.id] = scaleImage(blobs[file.id], resize, /\.png$/i.test(file.name) ? 'image/png' : 'image/jpeg');
}
file.size = blobs[file.id].length;
chunkSize = up.settings.chunk_size;
chunking = chunkSize > 0;
chunks = Math.ceil(file.size / chunkSize);
// If chunking is disabled then upload the whole file in one huge chunk
if (!chunking) {
chunkSize = file.size;
chunks = 1;
}
function uploadNextChunk() {
var req, curChunkSize, multipart = up.settings.multipart, multipartLength = 0, reqArgs = {name : file.target_name || file.name}, url = up.settings.url;
// Sends the binary blob multipart encoded or raw depending on config
function sendBinaryBlob(blob) {
var builder, boundary = '----pluploadboundary' + plupload.guid(), dashdash = '--', crlf = '\r\n', multipartBlob, mimeType;
// Build multipart request
if (multipart) {
req.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
builder = google.gears.factory.create('beta.blobbuilder');
// Append mutlipart parameters
plupload.each(plupload.extend(reqArgs, up.settings.multipart_params), function(value, name) {
builder.append(
dashdash + boundary + crlf +
'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf
);
builder.append(value + crlf);
});
mimeType = plupload.mimeTypes[file.name.replace(/^.+\.([^.]+)/, '$1').toLowerCase()] || 'application/octet-stream';
// Add file header
builder.append(
dashdash + boundary + crlf +
'Content-Disposition: form-data; name="' + up.settings.file_data_name + '"; filename="' + file.name + '"' + crlf +
'Content-Type: ' + mimeType + crlf + crlf
);
// Add file data
builder.append(blob);
// Add footer
builder.append(crlf + dashdash + boundary + dashdash + crlf);
multipartBlob = builder.getAsBlob();
multipartLength = multipartBlob.length - blob.length;
blob = multipartBlob;
}
// Send blob or multipart blob depending on config
req.send(blob);
}
// File upload finished
if (file.status == plupload.DONE || file.status == plupload.FAILED || up.state == plupload.STOPPED) {
return;
}
// Only add chunking args if needed
if (chunking) {
reqArgs.chunk = chunk;
reqArgs.chunks = chunks;
}
// Setup current chunk size
curChunkSize = Math.min(chunkSize, file.size - (chunk * chunkSize));
if (!multipart) {
url = plupload.buildUrl(up.settings.url, reqArgs);
}
req = google.gears.factory.create('beta.httprequest');
req.open('POST', url);
// Add disposition and type if multipart is disabled
if (!multipart) {
req.setRequestHeader('Content-Disposition', 'attachment; filename="' + file.name + '"');
req.setRequestHeader('Content-Type', 'application/octet-stream');
}
// Set custom headers
plupload.each(up.settings.headers, function(value, name) {
req.setRequestHeader(name, value);
});
req.upload.onprogress = function(progress) {
file.loaded = loaded + progress.loaded - multipartLength;
up.trigger('UploadProgress', file);
};
req.onreadystatechange = function() {
var chunkArgs;
if (req.readyState == 4) {
if (req.status == 200) {
chunkArgs = {
chunk : chunk,
chunks : chunks,
response : req.responseText,
status : req.status
};
up.trigger('ChunkUploaded', file, chunkArgs);
// Stop upload
if (chunkArgs.cancelled) {
file.status = plupload.FAILED;
return;
}
loaded += curChunkSize;
if (++chunk >= chunks) {
file.status = plupload.DONE;
up.trigger('FileUploaded', file, {
response : req.responseText,
status : req.status
});
} else {
uploadNextChunk();
}
} else {
up.trigger('Error', {
code : plupload.HTTP_ERROR,
message : plupload.translate('HTTP Error.'),
file : file,
chunk : chunk,
chunks : chunks,
status : req.status
});
}
}
};
if (chunk < chunks) {
sendBinaryBlob(blobs[file.id].slice(chunk * chunkSize, curChunkSize));
}
}
// Start uploading chunks
uploadNextChunk();
});
uploader.bind("Destroy", function(up) {
var name, element,
elements = {
browseButton: up.settings.browse_button,
dropElm: up.settings.drop_element
};
// Unbind event handlers
for (name in elements) {
element = document.getElementById(elements[name]);
if (element) {
plupload.removeAllEvents(element, up.id);
}
}
});
callback({success : true});
}
});
})(window, document, plupload);

View File

@ -30,15 +30,13 @@
*
* @return {Object} Name/value object with supported features.
*/
getFeatures : function() {
getFeatures : function() {
// Only multipart feature
return {
multipart: true,
/* WebKit let you trigger file dialog programmatically while FF and Opera - do not, so we
sniff for it here... probably not that good idea, but impossibillity of controlling cursor style
on top of add files button obviously feels even worse */
canOpenDialog: navigator.userAgent.indexOf('WebKit') !== -1
// WebKit and Gecko 2+ can trigger file dialog progrmmatically
triggerDialog: (plupload.ua.gecko && window.FormData || plupload.ua.webkit)
};
},
@ -106,7 +104,7 @@
browseButton = getById(up.settings.browse_button);
// Route click event to input element programmatically, if possible
if (up.features.canOpenDialog && browseButton) {
if (up.features.triggerDialog && browseButton) {
plupload.addEvent(getById(up.settings.browse_button), 'click', function(e) {
input.click();
e.preventDefault();
@ -118,7 +116,7 @@
width : '100%',
height : '100%',
opacity : 0,
fontSize: '99px' // force input element to be bigger then needed to occupy whole space
fontSize: '999px' // force input element to be bigger then needed to occupy whole space
});
plupload.extend(form.style, {
@ -153,7 +151,7 @@
files.push(new plupload.File(currentFileId, name));
// Clean-up events - they won't be needed anymore
if (!up.features.canOpenDialog) {
if (!up.features.triggerDialog) {
plupload.removeAllEvents(form, up.id);
} else {
plupload.removeEvent(browseButton, 'click', up.id);
@ -209,8 +207,8 @@
}
// Get result
result = el.documentElement.innerText || el.documentElement.textContent;
result = el.body.innerHTML;
// Assume no error
if (result) {
currentFile.status = plupload.DONE;
@ -287,14 +285,16 @@
if (up.state == plupload.STOPPED) {
window.setTimeout(function() {
plupload.removeEvent(iframe, 'load', up.id);
iframe.parentNode.removeChild(iframe);
if (iframe.parentNode) { // #382
iframe.parentNode.removeChild(iframe);
}
}, 0);
}
});
// Refresh button, will reposition the input form
up.bind("Refresh", function(up) {
var browseButton, topElement, hoverClass, activeClass, browsePos, browseSize, inputContainer, inputFile, pzIndex;
var browseButton, topElement, hoverClass, activeClass, browsePos, browseSize, inputContainer, inputFile, zIndex;
browseButton = getById(up.settings.browse_button);
if (browseButton) {
@ -312,25 +312,25 @@
// for IE and WebKit place input element underneath the browse button and route onclick event
// TODO: revise when browser support for this feature will change
if (up.features.canOpenDialog) {
pzIndex = parseInt(browseButton.parentNode.style.zIndex, 10);
if (isNaN(pzIndex)) {
pzIndex = 0;
}
plupload.extend(browseButton.style, {
zIndex : pzIndex
});
if (up.features.triggerDialog) {
if (plupload.getStyle(browseButton, 'position') === 'static') {
plupload.extend(browseButton.style, {
position : 'relative'
});
}
zIndex = parseInt(browseButton.style.zIndex, 10);
if (isNaN(zIndex)) {
zIndex = 0;
}
plupload.extend(browseButton.style, {
zIndex : zIndex
});
plupload.extend(inputContainer.style, {
zIndex : pzIndex - 1
zIndex : zIndex - 1
});
}
@ -340,7 +340,7 @@
TODO: needs to be revised as things will change */
hoverClass = up.settings.browse_button_hover;
activeClass = up.settings.browse_button_active;
topElement = up.features.canOpenDialog ? browseButton : inputContainer;
topElement = up.features.triggerDialog ? browseButton : inputContainer;
if (hoverClass) {
plupload.addEvent(topElement, 'mouseover', function() {

View File

@ -12,27 +12,8 @@
/*global plupload:false, File:false, window:false, atob:false, FormData:false, FileReader:false, ArrayBuffer:false, Uint8Array:false, BlobBuilder:false, unescape:false */
(function(window, document, plupload, undef) {
var fakeSafariDragDrop;
if ((typeof File !== 'undefined') && !File.prototype.slice) {
if (File.prototype.webkitSlice) File.prototype.slice = File.prototype.webkitSlice;
if (File.prototype.mozSlice) File.prototype.slice = File.prototype.mozSlice;
}
/* Introduce sendAsBinary for latest WebKits having support for BlobBuilder and typed arrays:
credits: http://javascript0.org/wiki/Portable_sendAsBinary,
more info: http://code.google.com/p/chromium/issues/detail?id=35705
*/
if (window.Uint8Array && window.ArrayBuffer && !XMLHttpRequest.prototype.sendAsBinary) {
XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
var ui8a = new Uint8Array(datastr.length);
for (var i = 0; i < datastr.length; i++) {
ui8a[i] = (datastr.charCodeAt(i) & 0xff);
}
this.send(ui8a.buffer);
};
}
var html5files = {}, // queue of original File objects
fakeSafariDragDrop;
function readFileAsDataURL(file, callback) {
var reader;
@ -64,10 +45,11 @@
}
}
function scaleImage(image_file, resize, mime, callback) {
var canvas, context, img, scale;
readFileAsDataURL(image_file, function(data) {
function scaleImage(file, resize, mime, callback) {
var canvas, context, img, scale,
up = this;
readFileAsDataURL(html5files[file.id], function(data) {
// Setup canvas and context
canvas = document.createElement("canvas");
canvas.style.display = 'none';
@ -112,9 +94,18 @@
// Set new width and height
exifParser.setExif('PixelXDimension', width);
exifParser.setExif('PixelYDimension', height);
// Update EXIF header
jpegHeaders.set('exif', exifParser.getBinary());
// trigger Exif events only if someone listens to them
if (up.hasEventListener('ExifData')) {
up.trigger('ExifData', file, exifParser.EXIF());
}
if (up.hasEventListener('GpsData')) {
up.trigger('GpsData', file, exifParser.GPS());
}
}
}
@ -134,8 +125,8 @@
data = data.substring(data.indexOf('base64,') + 7);
data = atob(data);
// Restore JPEG headers
if (jpegHeaders['headers'] && jpegHeaders['headers'].length) {
// Restore JPEG headers if applicable
if (jpegHeaders && jpegHeaders['headers'] && jpegHeaders['headers'].length) {
data = jpegHeaders.restore(data);
jpegHeaders.purge(); // free memory
}
@ -167,11 +158,11 @@
* @return {Object} Name/value object with supported features.
*/
getFeatures : function() {
var xhr, hasXhrSupport, hasProgress, dataAccessSupport, sliceSupport, win = window;
var xhr, hasXhrSupport, hasProgress, canSendBinary, dataAccessSupport, sliceSupport;
hasXhrSupport = hasProgress = dataAccessSupport = sliceSupport = false;
if (win.XMLHttpRequest) {
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
hasProgress = !!xhr.upload;
hasXhrSupport = !!(xhr.sendAsBinary || xhr.upload);
@ -179,28 +170,35 @@
// Check for support for various features
if (hasXhrSupport) {
canSendBinary = !!(xhr.sendAsBinary || (window.Uint8Array && window.ArrayBuffer));
// Set dataAccessSupport only for Gecko since BlobBuilder and XHR doesn't handle binary data correctly
dataAccessSupport = !!(File && (File.prototype.getAsDataURL || win.FileReader) && xhr.sendAsBinary);
sliceSupport = !!(File && File.prototype.slice);
dataAccessSupport = !!(File && (File.prototype.getAsDataURL || window.FileReader) && canSendBinary);
sliceSupport = !!(File && (File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice));
}
// Sniff for Safari and fake drag/drop
fakeSafariDragDrop = navigator.userAgent.indexOf('Safari') > 0 && navigator.vendor.indexOf('Apple') !== -1;
// sniff out Safari for Windows and fake drag/drop
fakeSafariDragDrop = plupload.ua.safari && plupload.ua.windows;
return {
// Detect drag/drop file support by sniffing, will try to find a better way
html5: hasXhrSupport, // This is a special one that we check inside the init call
dragdrop: win.mozInnerScreenX !== undef || sliceSupport || fakeSafariDragDrop,
dragdrop: (function() {
// this comes directly from Modernizr: http://www.modernizr.com/
var div = document.createElement('div');
return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
}()),
jpgresize: dataAccessSupport,
pngresize: dataAccessSupport,
multipart: dataAccessSupport || !!win.FileReader || !!win.FormData,
multipart: dataAccessSupport || !!window.FileReader || !!window.FormData,
canSendBinary: canSendBinary,
// gecko 2/5/6 can't send blob with FormData: https://bugzilla.mozilla.org/show_bug.cgi?id=649150
cantSendBlobInFormData: !!(plupload.ua.gecko && window.FormData && window.FileReader && !FileReader.prototype.readAsArrayBuffer),
progress: hasProgress,
chunks: sliceSupport || dataAccessSupport,
/* WebKit let you trigger file dialog programmatically while FF and Opera - do not, so we
sniff for it here... probably not that good idea, but impossibillity of controlling cursor style
on top of add files button obviously feels even worse */
canOpenDialog: navigator.userAgent.indexOf('WebKit') !== -1
chunks: sliceSupport,
// Safari on Windows has problems when selecting multiple files
multi_selection: !(plupload.ua.safari && plupload.ua.windows),
// WebKit and Gecko 2+ can trigger file dialog progrmmatically
triggerDialog: (plupload.ua.gecko && window.FormData || plupload.ua.webkit)
};
},
@ -212,7 +210,7 @@
* @param {function} callback Callback to execute when the runtime initializes or fails to initialize. If it succeeds an object with a parameter name success will be set to true.
*/
init : function(uploader, callback) {
var html5files = {}, features;
var features;
function addSelectedFiles(native_files) {
var file, i, files = [], id, fileNames = {};
@ -220,7 +218,7 @@
// Add the selected files to the file queue
for (i = 0; i < native_files.length; i++) {
file = native_files[i];
// Safari on Windows will add first file from dragged set multiple times
// @see: https://bugs.webkit.org/show_bug.cgi?id=37957
if (fileNames[file.name]) {
@ -233,7 +231,7 @@
html5files[id] = file;
// Expose id, name and size
files.push(new plupload.File(id, file.fileName, file.fileSize || file.size)); // File.fileSize depricated
files.push(new plupload.File(id, file.fileName || file.name, file.fileSize || file.size)); // fileName / fileSize depricated
}
// Trigger FilesAdded event if we added any
@ -265,7 +263,6 @@
zIndex : 99999,
opacity : uploader.settings.shim_bgcolor ? '' : 0 // Force transparent if bgcolor is undefined
});
inputContainer.className = 'plupload html5';
if (uploader.settings.container) {
@ -300,29 +297,44 @@
// Insert the input inside the input container
inputContainer.innerHTML = '<input id="' + uploader.id + '_html5" ' +
'style="width:100%;height:100%;font-size:99px" type="file" accept="' +
mimes.join(',') + '" ' +
(uploader.settings.multi_selection ? 'multiple="multiple"' : '') + ' />';
inputContainer.innerHTML = '<input id="' + uploader.id + '_html5" ' + ' style="font-size:999px"' +
' type="file" accept="' + mimes.join(',') + '" ' +
(uploader.settings.multi_selection && uploader.features.multi_selection ? 'multiple="multiple"' : '') + ' />';
inputContainer.scrollTop = 100;
inputFile = document.getElementById(uploader.id + '_html5');
if (up.features.triggerDialog) {
plupload.extend(inputFile.style, {
position: 'absolute',
width: '100%',
height: '100%'
});
} else {
// shows arrow cursor instead of the text one, bit more logical
plupload.extend(inputFile.style, {
cssFloat: 'right',
styleFloat: 'right'
});
}
inputFile.onchange = function() {
// Add the selected files from file input
addSelectedFiles(this.files);
// Clearing the value enables the user to select the same file again if they want to
this.value = '';
};
/* Since we have to place input[type=file] on top of the browse_button for some browsers (FF, Opera),
browse_button loses interactivity, here we try to neutralize this issue highlighting browse_button
with a special class
with a special classes
TODO: needs to be revised as things will change */
browseButton = document.getElementById(up.settings.browse_button);
if (browseButton) {
var hoverClass = up.settings.browse_button_hover,
activeClass = up.settings.browse_button_active,
topElement = up.features.canOpenDialog ? browseButton : inputContainer;
topElement = up.features.triggerDialog ? browseButton : inputContainer;
if (hoverClass) {
plupload.addEvent(topElement, 'mouseover', function() {
@ -343,7 +355,7 @@
}
// Route click event to the input[type=file] element for supporting browsers
if (up.features.canOpenDialog) {
if (up.features.triggerDialog) {
plupload.addEvent(browseButton, 'click', function(e) {
document.getElementById(up.id + '_html5').click();
e.preventDefault();
@ -374,7 +386,7 @@
plupload.addEvent(dropInputElm, 'change', function() {
// Add the selected files from file input
addSelectedFiles(this.files);
// Remove input element
plupload.removeEvent(dropInputElm, 'change', uploader.id);
dropInputElm.parentNode.removeChild(dropInputElm);
@ -426,7 +438,7 @@
});
uploader.bind("Refresh", function(up) {
var browseButton, browsePos, browseSize, inputContainer, pzIndex;
var browseButton, browsePos, browseSize, inputContainer, zIndex;
browseButton = document.getElementById(uploader.settings.browse_button);
if (browseButton) {
@ -443,25 +455,24 @@
// for WebKit place input element underneath the browse button and route onclick event
// TODO: revise when browser support for this feature will change
if (uploader.features.canOpenDialog) {
pzIndex = parseInt(browseButton.parentNode.style.zIndex, 10);
if (isNaN(pzIndex)) {
pzIndex = 0;
}
plupload.extend(browseButton.style, {
zIndex : pzIndex
});
if (uploader.features.triggerDialog) {
if (plupload.getStyle(browseButton, 'position') === 'static') {
plupload.extend(browseButton.style, {
position : 'relative'
});
}
zIndex = parseInt(plupload.getStyle(browseButton, 'z-index'), 10);
if (isNaN(zIndex)) {
zIndex = 0;
}
plupload.extend(browseButton.style, {
zIndex : zIndex
});
plupload.extend(inputContainer.style, {
zIndex : pzIndex - 1
zIndex : zIndex - 1
});
}
}
@ -469,14 +480,201 @@
uploader.bind("UploadFile", function(up, file) {
var settings = up.settings, nativeFile, resize;
function w3cBlobSlice(blob, start, end) {
var blobSlice;
if (File.prototype.slice) {
try {
blob.slice(); // depricated version will throw WRONG_ARGUMENTS_ERR exception
return blob.slice(start, end);
} catch (e) {
// depricated slice method
return blob.slice(start, end - start);
}
// slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672
} else if (blobSlice = File.prototype.webkitSlice || File.prototype.mozSlice) {
return blobSlice.call(blob, start, end);
} else {
return null; // or throw some exception
}
}
function sendBinaryBlob(blob) {
var chunk = 0, loaded = 0;
var chunk = 0, loaded = 0,
fr = ("FileReader" in window) ? new FileReader : null;
function uploadNextChunk() {
var chunkBlob = blob, xhr, upload, chunks, args, multipartDeltaSize = 0,
boundary = '----pluploadboundary' + plupload.guid(), chunkSize, curChunkSize, formData,
dashdash = '--', crlf = '\r\n', multipartBlob = '', mimeType, url = up.settings.url;
var chunkBlob, br, chunks, args, chunkSize, curChunkSize, mimeType, url = up.settings.url;
function prepareAndSend(bin) {
var multipartDeltaSize = 0,
xhr = new XMLHttpRequest,
upload = xhr.upload,
boundary = '----pluploadboundary' + plupload.guid(), formData, dashdash = '--', crlf = '\r\n', multipartBlob = ''
// Do we have upload progress support
if (upload) {
upload.onprogress = function(e) {
file.loaded = Math.min(file.size, loaded + e.loaded - multipartDeltaSize); // Loaded can be larger than file size due to multipart encoding
up.trigger('UploadProgress', file);
};
}
xhr.onreadystatechange = function() {
var httpStatus, chunkArgs;
if (xhr.readyState == 4) {
// Getting the HTTP status might fail on some Gecko versions
try {
httpStatus = xhr.status;
} catch (ex) {
httpStatus = 0;
}
// Is error status
if (httpStatus >= 400) {
up.trigger('Error', {
code : plupload.HTTP_ERROR,
message : plupload.translate('HTTP Error.'),
file : file,
status : httpStatus
});
} else {
// Handle chunk response
if (chunks) {
chunkArgs = {
chunk : chunk,
chunks : chunks,
response : xhr.responseText,
status : httpStatus
};
up.trigger('ChunkUploaded', file, chunkArgs);
loaded += curChunkSize;
// Stop upload
if (chunkArgs.cancelled) {
file.status = plupload.FAILED;
return;
}
file.loaded = Math.min(file.size, (chunk + 1) * chunkSize);
} else {
file.loaded = file.size;
}
up.trigger('UploadProgress', file);
bin = chunkBlob = formData = multipartBlob = null; // Free memory
// Check if file is uploaded
if (!chunks || ++chunk >= chunks) {
file.status = plupload.DONE;
up.trigger('FileUploaded', file, {
response : xhr.responseText,
status : httpStatus
});
} else {
// Still chunks left
uploadNextChunk();
}
}
xhr = null;
}
};
// Build multipart request
if (up.settings.multipart && features.multipart) {
args.name = file.target_name || file.name;
xhr.open("post", url, true);
// Set custom headers
plupload.each(up.settings.headers, function(value, name) {
xhr.setRequestHeader(name, value);
});
// if has FormData support like Chrome 6+, Safari 5+, Firefox 4, use it
if (typeof(bin) !== 'string' && !!window.FormData) {
formData = new FormData();
// Add multipart params
plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
formData.append(name, value);
});
// Add file and send it
formData.append(up.settings.file_data_name, bin);
xhr.send(formData);
return;
} // if no FormData we can still try to send it directly as last resort (see below)
if (typeof(bin) === 'string') {
// Trying to send the whole thing as binary...
// multipart request
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
// append multipart parameters
plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
multipartBlob += dashdash + boundary + crlf +
'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf;
multipartBlob += unescape(encodeURIComponent(value)) + crlf;
});
mimeType = plupload.mimeTypes[file.name.replace(/^.+\.([^.]+)/, '$1').toLowerCase()] || 'application/octet-stream';
// Build RFC2388 blob
multipartBlob += dashdash + boundary + crlf +
'Content-Disposition: form-data; name="' + up.settings.file_data_name + '"; filename="' + unescape(encodeURIComponent(file.name)) + '"' + crlf +
'Content-Type: ' + mimeType + crlf + crlf +
bin + crlf +
dashdash + boundary + dashdash + crlf;
multipartDeltaSize = multipartBlob.length - bin.length;
bin = multipartBlob;
if (xhr.sendAsBinary) { // Gecko
xhr.sendAsBinary(bin);
} else if (features.canSendBinary) { // WebKit with typed arrays support
var ui8a = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
ui8a[i] = (bin.charCodeAt(i) & 0xff);
}
xhr.send(ui8a.buffer);
}
return; // will return from here only if shouldn't send binary
}
}
// if no multipart, or last resort, send as binary stream
url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
xhr.open("post", url, true);
xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
// Set custom headers
plupload.each(up.settings.headers, function(value, name) {
xhr.setRequestHeader(name, value);
});
xhr.send(bin);
} // prepareAndSend
// File upload finished
if (file.status == plupload.DONE || file.status == plupload.FAILED || up.state == plupload.STOPPED) {
@ -487,7 +685,7 @@
args = {name : file.target_name || file.name};
// Only add chunking args if needed
if (settings.chunk_size && features.chunks) {
if (settings.chunk_size && file.size > settings.chunk_size && (features.chunks || typeof(blob) == 'string')) { // blob will be of type string if it was loaded in memory
chunkSize = settings.chunk_size;
chunks = Math.ceil(file.size / chunkSize);
curChunkSize = Math.min(chunkSize, file.size - (chunk * chunkSize));
@ -498,7 +696,7 @@
chunkBlob = blob.substring(chunk * chunkSize, chunk * chunkSize + curChunkSize);
} else {
// Slice the chunk
chunkBlob = blob.slice(chunk * chunkSize, curChunkSize);
chunkBlob = w3cBlobSlice(blob, chunk * chunkSize, chunk * chunkSize + curChunkSize);
}
// Setup query string arguments
@ -506,148 +704,19 @@
args.chunks = chunks;
} else {
curChunkSize = file.size;
chunkBlob = blob;
}
// Setup XHR object
xhr = new XMLHttpRequest();
upload = xhr.upload;
// Do we have upload progress support
if (upload) {
upload.onprogress = function(e) {
file.loaded = Math.min(file.size, loaded + e.loaded - multipartDeltaSize); // Loaded can be larger than file size due to multipart encoding
up.trigger('UploadProgress', file);
};
}
// Add name, chunk and chunks to query string on direct streaming
if (!up.settings.multipart || !features.multipart) {
url = plupload.buildUrl(up.settings.url, args);
} else {
args.name = file.target_name || file.name;
}
xhr.open("post", url, true);
xhr.onreadystatechange = function() {
var httpStatus, chunkArgs;
if (xhr.readyState == 4) {
// Getting the HTTP status might fail on some Gecko versions
try {
httpStatus = xhr.status;
} catch (ex) {
httpStatus = 0;
}
// Is error status
if (httpStatus >= 400) {
up.trigger('Error', {
code : plupload.HTTP_ERROR,
message : plupload.translate('HTTP Error.'),
file : file,
status : httpStatus
});
} else {
// Handle chunk response
if (chunks) {
chunkArgs = {
chunk : chunk,
chunks : chunks,
response : xhr.responseText,
status : httpStatus
};
up.trigger('ChunkUploaded', file, chunkArgs);
loaded += curChunkSize;
// Stop upload
if (chunkArgs.cancelled) {
file.status = plupload.FAILED;
return;
}
file.loaded = Math.min(file.size, (chunk + 1) * chunkSize);
} else {
file.loaded = file.size;
}
up.trigger('UploadProgress', file);
// Check if file is uploaded
if (!chunks || ++chunk >= chunks) {
file.status = plupload.DONE;
up.trigger('FileUploaded', file, {
response : xhr.responseText,
status : httpStatus
});
nativeFile = blob = html5files[file.id] = null; // Free memory
} else {
// Still chunks left
uploadNextChunk();
}
}
xhr = chunkBlob = formData = multipartBlob = null; // Free memory
// workaround Gecko 2,5,6 FormData+Blob bug: https://bugzilla.mozilla.org/show_bug.cgi?id=649150
if (typeof(chunkBlob) !== 'string' && fr && features.cantSendBlobInFormData && features.chunks && up.settings.chunk_size) {// Gecko 2,5,6
fr.onload = function() {
prepareAndSend(fr.result);
}
};
// Set custom headers
plupload.each(up.settings.headers, function(value, name) {
xhr.setRequestHeader(name, value);
});
// Build multipart request
if (up.settings.multipart && features.multipart) {
// Has FormData support like Chrome 6+, Safari 5+, Firefox 4
if (!xhr.sendAsBinary) {
formData = new FormData();
// Add multipart params
plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
formData.append(name, value);
});
// Add file and send it
formData.append(up.settings.file_data_name, chunkBlob);
xhr.send(formData);
return;
}
// Gecko multipart request
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
// Append multipart parameters
plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
multipartBlob += dashdash + boundary + crlf +
'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf;
multipartBlob += unescape(encodeURIComponent(value)) + crlf;
});
mimeType = plupload.mimeTypes[file.name.replace(/^.+\.([^.]+)/, '$1').toLowerCase()] || 'application/octet-stream';
// Build RFC2388 blob
multipartBlob += dashdash + boundary + crlf +
'Content-Disposition: form-data; name="' + up.settings.file_data_name + '"; filename="' + unescape(encodeURIComponent(file.name)) + '"' + crlf +
'Content-Type: ' + mimeType + crlf + crlf +
chunkBlob + crlf +
dashdash + boundary + dashdash + crlf;
multipartDeltaSize = multipartBlob.length - chunkBlob.length;
chunkBlob = multipartBlob;
fr.readAsBinaryString(chunkBlob);
} else {
// Binary stream header
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
}
if (xhr.sendAsBinary) {
xhr.sendAsBinary(chunkBlob); // Gecko
} else {
xhr.send(chunkBlob); // WebKit
prepareAndSend(chunkBlob);
}
}
// Start uploading chunks
@ -655,26 +724,24 @@
}
nativeFile = html5files[file.id];
resize = up.settings.resize;
if (features.jpgresize) {
// Resize image if it's a supported format and resize is enabled
if (resize && /\.(png|jpg|jpeg)$/i.test(file.name)) {
scaleImage(nativeFile, resize, /\.png$/i.test(file.name) ? 'image/png' : 'image/jpeg', function(res) {
// If it was scaled send the scaled image if it failed then
// send the raw image and let the server do the scaling
if (res.success) {
file.size = res.data.length;
sendBinaryBlob(res.data);
} else {
readFileAsBinary(nativeFile, sendBinaryBlob);
}
});
} else {
readFileAsBinary(nativeFile, sendBinaryBlob);
}
// Resize image if it's a supported format and resize is enabled
if (features.jpgresize && up.settings.resize && /\.(png|jpg|jpeg)$/i.test(file.name)) {
scaleImage.call(up, file, up.settings.resize, /\.png$/i.test(file.name) ? 'image/png' : 'image/jpeg', function(res) {
// If it was scaled send the scaled image if it failed then
// send the raw image and let the server do the scaling
if (res.success) {
file.size = res.data.length;
sendBinaryBlob(res.data);
} else {
sendBinaryBlob(nativeFile);
}
});
// if there's no way to slice file without preloading it in memory, preload it
} else if (!features.chunks && features.jpgresize) {
readFileAsBinary(nativeFile, sendBinaryBlob);
} else {
sendBinaryBlob(nativeFile); // this works on older WebKits, but fails on fresh ones
sendBinaryBlob(nativeFile);
}
});
@ -874,10 +941,19 @@
read.init(data);
// Check if data is jpeg
if (read.SHORT(0) !== 0xFFD8) {
var jpegHeaders = new JPEG_Headers(data);
if (!jpegHeaders['headers']) {
return false;
}
// Delete any existing headers that need to be replaced
for (var i = jpegHeaders['headers'].length; i > 0; i--) {
var hdr = jpegHeaders['headers'][i - 1];
read.SEGMENT(hdr.start, hdr.length, '')
}
jpegHeaders.purge();
idx = read.SHORT(2) == 0xFFE0 ? 4 + read.SHORT(4) : 2;
for (var i = 0, max = headers.length; i < max; i++) {
@ -1276,8 +1352,7 @@
}
return false;
},
EXIF: function() {
var Exif;
@ -1285,12 +1360,14 @@
Exif = extractTags(offsets.exifIFD, tags.exif);
// Fix formatting of some tags
Exif.ExifVersion = String.fromCharCode(
Exif.ExifVersion[0],
Exif.ExifVersion[1],
Exif.ExifVersion[2],
Exif.ExifVersion[3]
);
if (Exif.ExifVersion) {
Exif.ExifVersion = String.fromCharCode(
Exif.ExifVersion[0],
Exif.ExifVersion[1],
Exif.ExifVersion[2],
Exif.ExifVersion[3]
);
}
return Exif;
},
@ -1299,7 +1376,11 @@
var GPS;
GPS = extractTags(offsets.gpsIFD, tags.gps);
GPS.GPSVersionID = GPS.GPSVersionID.join('.');
// iOS devices (and probably some others) do not put in GPSVersionID tag (why?..)
if (GPS.GPSVersionID) {
GPS.GPSVersionID = GPS.GPSVersionID.join('.');
}
return GPS;
},

View File

@ -58,6 +58,7 @@
"image/bmp,bmp," +
"image/gif,gif," +
"image/jpeg,jpeg jpg jpe," +
"image/photoshop,psd," +
"image/png,png," +
"image/svg+xml,svg svgz," +
"image/tiff,tiff tif," +
@ -68,7 +69,11 @@
"video/mp4,mp4," +
"video/x-m4v,m4v," +
"video/x-flv,flv," +
"video/x-ms-wmv,wmv," +
"video/avi,avi," +
"video/webm,webm," +
"video/vnd.rn-realvideo,rv," +
"text/csv,csv," +
"text/plain,asc txt text diff log," +
"application/octet-stream,exe"
);
@ -183,7 +188,7 @@
INIT_ERROR : -500,
/**
* File size error. If the user selects a file that is to large it will be blocked and an error of this type will be triggered.
* File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered.
*
* @property FILE_SIZE_ERROR
* @final
@ -231,6 +236,26 @@
* @final
*/
mimeTypes : mimes,
/**
* In some cases sniffing is the only way around :(
*/
ua: (function() {
var nav = navigator, userAgent = nav.userAgent, vendor = nav.vendor, webkit, opera, safari;
webkit = /WebKit/.test(userAgent);
safari = webkit && vendor.indexOf('Apple') !== -1;
opera = window.opera && window.opera.buildNumber;
return {
windows: navigator.platform.indexOf('Win') !== -1,
ie: !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName),
webkit: webkit,
gecko: !webkit && /Gecko/.test(userAgent),
safari: safari,
opera: !!opera
};
}()),
/**
* Extends the specified object with another object.
@ -491,7 +516,7 @@
var mul;
if (typeof(size) == 'string') {
size = /^([0-9]+)([mgk]+)$/.exec(size.toLowerCase().replace(/[^0-9mkg]/g, ''));
size = /^([0-9]+)([mgk]?)$/.exec(size.toLowerCase().replace(/[^0-9mkg]/g, ''));
mul = size[2];
size = +size[1];
@ -657,7 +682,11 @@
}
// Add event listener
if (obj.attachEvent) {
if (obj.addEventListener) {
func = callback;
obj.addEventListener(name, func, false);
} else if (obj.attachEvent) {
func = function() {
var evt = window.event;
@ -672,12 +701,7 @@
callback(evt);
};
obj.attachEvent('on' + name, func);
} else if (obj.addEventListener) {
func = callback;
obj.addEventListener(name, func, false);
}
}
// Log event handler to objects internal Plupload registry
if (obj[uid] === undef) {
@ -786,7 +810,7 @@
plupload.each(eventhash[obj[uid]], function(events, name) {
plupload.removeEvent(obj, name, key);
});
}
}
};
@ -848,8 +872,9 @@
if (!file && files[i].status == plupload.QUEUED) {
file = files[i];
file.status = plupload.UPLOADING;
this.trigger("BeforeUpload", file);
this.trigger("UploadFile", file);
if (this.trigger("BeforeUpload", file)) {
this.trigger("UploadFile", file);
}
} else {
count++;
}
@ -857,8 +882,8 @@
// All files are DONE or FAILED
if (count == files.length) {
this.trigger("UploadComplete", files);
this.stop();
this.trigger("UploadComplete", files);
}
}
}
@ -1302,6 +1327,16 @@
return true;
},
/**
* Check whether uploader has any listeners to the specified event.
*
* @method hasEventListener
* @param {String} name Event name to check for.
*/
hasEventListener : function(name) {
return !!events[name.toLowerCase()];
},
/**
* Adds an event listener by name.

View File

@ -17,6 +17,11 @@
function jsonSerialize(obj) {
var value, type = typeof obj, isArray, i, key;
// Treat undefined as null
if (obj === undef || obj === null) {
return 'null';
}
// Encode strings
if (type === 'string') {
value = '\bb\tt\nn\ff\rr\""\'\'\\\\';
@ -66,11 +71,6 @@
return value;
}
// Treat undefined as null
if (obj === undef) {
return 'null';
}
// Convert all other types to string
return '' + obj;
}
@ -167,7 +167,8 @@
pngresize: true,
chunks: true,
progress: true,
multipart: true
multipart: true,
multi_selection: true
};
},