@ -50,6 +50,7 @@ function rcube_webmail()
this . menu _buttons = { } ;
this . entity _selectors = [ ] ;
this . image _style = { } ;
this . uploads = { } ;
// webmail client settings
this . dblclick _time = 500 ;
@ -684,7 +685,7 @@ function rcube_webmail()
}
// activate html5 file drop feature (if browser supports it and if configured)
if ( this . gui _objects . filedrop && this . env . filedrop && ( ( window . XMLHttpRequest && XMLHttpRequest . prototype && XMLHttpRequest . prototype . sendAsBinary ) || window . FormData ) ) {
if ( this . gui _objects . filedrop && this . env . filedrop && window . FormData ) {
$ ( document . body ) . on ( 'dragover dragleave drop' , function ( e ) { return ref . document _drag _hover ( e , e . type == 'dragover' ) ; } ) ;
$ ( this . gui _objects . filedrop ) . addClass ( 'droptarget' )
. on ( 'dragover dragleave' , function ( e ) { return ref . file _drag _hover ( e , e . type == 'dragover' ) ; } )
@ -1393,8 +1394,6 @@ function rcube_webmail()
var form = props || this . gui _objects . importform ,
importlock = this . set _busy ( true , 'importwait' ) ;
$ ( '[name="_unlock"]' , form ) . val ( importlock ) ;
if ( ! ( flag = this . upload _file ( form , 'import' , importlock ) ) ) {
this . set _busy ( false , null , importlock ) ;
if ( flag !== false )
@ -5314,77 +5313,21 @@ function rcube_webmail()
// upload (attachment) file
this . upload _file = function ( form , action , lock )
{
if ( ! form )
return ;
// count files and size on capable browser
var size = 0 , numfiles = 0 ;
$ . each ( $ ( form ) . get ( 0 ) . elements || [ ] , function ( ) {
if ( this . type != 'file' )
return ;
var i , files = this . files ? this . files . length : ( this . value ? 1 : 0 ) ;
// check file size
if ( this . files ) {
for ( i = 0 ; i < files ; i ++ )
size += this . files [ i ] . size ;
}
numfiles += files ;
} ) ;
// create hidden iframe and post upload form
if ( numfiles ) {
if ( this . env . max _filesize && this . env . filesizeerror && size > this . env . max _filesize ) {
this . display _message ( this . env . filesizeerror , 'error' ) ;
return false ;
}
if ( this . env . max _filecount && this . env . filecounterror && numfiles > this . env . max _filecount ) {
this . display _message ( this . env . filecounterror , 'error' ) ;
return false ;
}
var frame _name = this . async _upload _form ( form , action || 'upload' , function ( e ) {
var d , content = '' ;
try {
if ( this . contentDocument ) {
d = this . contentDocument ;
} else if ( this . contentWindow ) {
d = this . contentWindow . document ;
}
content = d . childNodes [ 1 ] . innerHTML ;
} catch ( err ) { }
if ( ! content . match ( /add2attachment/ ) && ( ! bw . opera || ( ref . env . uploadframe && ref . env . uploadframe == e . data . ts ) ) ) {
if ( ! content . match ( /display_message/ ) )
ref . display _message ( 'fileuploaderror' , 'error' ) ;
ref . remove _from _attachment _list ( e . data . ts ) ;
if ( lock )
ref . set _busy ( false , null , lock ) ;
if ( form ) {
var fname , files = [ ] ;
$ ( 'input' , form ) . each ( function ( ) {
if ( this . files ) {
fname = this . name ;
for ( var i = 0 ; i < this . files . length ; i ++ )
files . push ( this . files [ i ] ) ;
}
// Opera hack: handle double onload
if ( bw . opera )
ref . env . uploadframe = e . data . ts ;
} ) ;
// display upload indicator and cancel button
var content = '<span>' + this . get _label ( 'uploading' + ( numfiles > 1 ? 'many' : '' ) ) + '</span>' ,
ts = frame _name . replace ( /^rcmupload/ , '' ) ;
this . add2attachment _list ( ts , { name : '' , html : content , classname : 'uploading' , frame : frame _name , complete : false } ) ;
// upload progress support
if ( this . env . upload _progress _time ) {
this . upload _progress _start ( 'upload' , ts ) ;
}
// set reference to the form object
this . gui _objects . attachmentform = form ;
return true ;
return this . file _upload ( files , { _id : this . env . compose _id || '' } , {
name : fname ,
action : action ,
lock : lock
} ) ;
}
} ;
@ -5408,12 +5351,15 @@ function rcube_webmail()
var label , indicator , li = $ ( '<li>' ) ;
if ( ! att . complete && att . html . indexOf ( '<' ) < 0 )
att . html = '<span class="uploading">' + att . html + '</span>' ;
if ( ! att . complete && this . env . loadingicon )
att . html = '<img src="' + this . env . loadingicon + '" alt="" class="uploading" />' + att . html ;
if ( ! att . complete && att . frame ) {
if ( ! att . complete ) {
label = this . get _label ( 'cancel' ) ;
att . html = '<a title="' + label + '" onclick="return rcmail.cancel_attachment_upload(\'' + name + '\' , \''+ att . frame + '\' );" href="#cancelupload" class="cancelupload">'
att . html = '<a title="' + label + '" onclick="return rcmail.cancel_attachment_upload(\'' + name + '\' );" href="#cancelupload" class="cancelupload">'
+ ( this . env . cancelicon ? '<img src="' + this . env . cancelicon + '" alt="' + label + '" />' : '<span class="inner">' + label + '</span>' ) + '</a>' + att . html ;
}
@ -5453,35 +5399,16 @@ function rcube_webmail()
return false ;
} ;
this . cancel _attachment _upload = function ( name , frame _name )
this . cancel _attachment _upload = function ( name )
{
if ( ! name || ! frame _name )
if ( ! name || ! this . uploads [ name ] )
return false ;
this . remove _from _attachment _list ( name ) ;
$ ( "iframe[name='" + frame _name + "']" ) . remove ( ) ;
this . uploads [ name ] . abort ( ) ;
return false ;
} ;
this . upload _progress _start = function ( action , name )
{
setTimeout ( function ( ) { ref . http _request ( action , { _progress : name } ) ; } ,
this . env . upload _progress _time * 1000 ) ;
} ;
this . upload _progress _update = function ( param )
{
var elem = $ ( '#' + param . name + ' > span' ) ;
if ( ! elem . length || ! param . text )
return ;
elem . text ( param . text ) ;
if ( ! param . done )
this . upload _progress _start ( param . action , param . name ) ;
} ;
// rename uploaded attachment (in compose)
this . rename _attachment = function ( id )
{
@ -9069,7 +8996,7 @@ function rcube_webmail()
return ;
if ( response . unlock )
this . set _busy ( false );
this . set _busy ( false , null , response . unlock );
this . triggerEvent ( 'responsebefore' , { response : response } ) ;
this . triggerEvent ( 'responsebefore' + response . action , { response : response } ) ;
@ -9472,19 +9399,6 @@ function rcube_webmail()
frame _name = 'rcmupload' + ts ,
frame = this . dummy _iframe ( frame _name ) ;
// upload progress support
if ( this . env . upload _progress _name ) {
var fname = this . env . upload _progress _name ,
field = $ ( 'input[name=' + fname + ']' , form ) ;
if ( ! field . length ) {
field = $ ( '<input>' ) . attr ( { type : 'hidden' , name : fname } ) ;
field . prependTo ( form ) ;
}
field . val ( ts ) ;
}
// handle upload errors by parsing iframe content in onload
frame . on ( 'load' , { ts : ts } , onload ) ;
@ -9532,19 +9446,14 @@ function rcube_webmail()
this . file _drag _hover ( e , false ) ;
// prepare multipart form data composition
var uri , size = 0 , numfiles = 0 ,
var uri ,
files = e . target . files || e . dataTransfer . files ,
formdata = window . FormData ? new FormData ( ) : null ,
fieldname = ( this . env . filedrop . fieldname || '_file' ) + ( this . env . filedrop . single ? '' : '[]' ) ,
boundary = '------multipartformboundary' + ( new Date ) . getTime ( ) ,
dashdash = '--' , crlf = '\r\n' ,
multipart = dashdash + boundary + crlf ,
args = { _id : this . env . compose _id || this . env . cid || '' , _remote : 1 , _from : this . env . action } ;
if ( ! files || ! files . length ) {
// Roundcube attachment, pass its uri to the backend and attach
if ( uri = e . dataTransfer . getData ( 'roundcube-uri' ) ) {
var ts = new Date ( ) . getTime ( ) ,
var ts = 'upload' + new Date ( ) . getTime ( ) ,
// jQuery way to escape filename (#1490530)
content = $ ( '<span>' ) . text ( e . dataTransfer . getData ( 'roundcube-name' ) || this . get _label ( 'attaching' ) ) . html ( ) ;
@ -9557,110 +9466,126 @@ function rcube_webmail()
this . http _post ( this . env . filedrop . action || 'upload' , args ) ;
}
return ;
}
// inline function to submit the files to the server
var submit _data = function ( ) {
if ( ref . env . max _filesize && ref . env . filesizeerror && size > ref . env . max _filesize ) {
ref . display _message ( ref . env . filesizeerror , 'error' ) ;
return ;
this . file _upload ( files , args , {
name : ( this . env . filedrop . fieldname || '_file' ) + ( this . env . filedrop . single ? '' : '[]' ) ,
single : this . env . filedrop . single ,
filter : this . env . filedrop . filter ,
action : ref . env . filedrop . action
} ) ;
} ;
// Files upload using ajax
this . file _upload = function ( files , post _args , props )
{
if ( ! window . FormData || ! files || ! files . length )
return false ;
var f , i , fname , size = 0 , numfiles = 0 ,
formdata = new FormData ( ) ,
fieldname = props . name || '_file[]' ,
limit = props . single ? 1 : files . length ;
args = $ . extend ( { _remote : 1 , _from : this . env . action } , post _args || { } ) ;
// add files to form data
for ( i = 0 ; numfiles < limit && ( f = files [ i ] ) ; i ++ ) {
// filter by file type if requested
if ( props . filter && ! f . type . match ( new RegExp ( props . filter ) ) ) {
// TODO: show message to user
continue ;
}
if ( ref . env . max _filecount && ref . env . filecounterror && numfiles > ref . env . max _filecount ) {
ref . display _message ( ref . env . filecounterror , 'error' ) ;
return ;
formdata . append ( fieldname , f ) ;
size += f . size ;
fname = f . name ;
numfiles ++ ;
}
if ( numfiles ) {
if ( this . env . max _filesize && this . env . filesizeerror && size > this . env . max _filesize ) {
this . display _message ( this . env . filesizeerror , 'error' ) ;
return false ;
}
var multiple = files . length > 1 ,
ts = new Date ( ) . getTime ( ) ,
if ( this . env . max _filecount && this . env . filecounterror && numfiles > this . env . max _filecount ) {
this . display _message ( this . env . filecounterror , 'error' ) ;
return false ;
}
var ts = 'upload' + new Date ( ) . getTime ( ) ,
label = numfiles > 1 ? this . get _label ( 'uploadingmany' ) : fname ,
// jQuery way to escape filename (#1490530)
content = $ ( '<span>' ) . text ( multiple ? ref . get _label ( 'uploadingmany' ) : files [ 0 ] . name ) . html ( ) ;
content = $ ( '<span>' ) . text ( label) . html ( ) ;
// add to attachments list
if ( ! ref . add2attachment _list ( ts , { name : '' , html : content , classname : 'uploading' , complete : false } ) )
ref . file _upload _id = ref . set _busy ( true , 'uploading' ) ;
// complete multipart content and post request
multipart += dashdash + boundary + dashdash + crlf ;
if ( ! this . add2attachment _list ( ts , { name : '' , html : content , classname : 'uploading' , complete : false } ) && ! props . lock )
props . lock = this . file _upload _id = this . set _busy ( true , 'uploading' ) ;
args . _uploadid = ts ;
args . _unlock = props . lock ;
$ . ajax ( {
this . uploads [ ts ] = $ . ajax ( {
type : 'POST' ,
dataType : 'json' ,
url : ref . url ( ref . env . filedrop . action || 'upload' , args ) ,
contentType : formdata ? false : 'multipart/form-data; boundary=' + boundary ,
url : this . url ( props . action || 'upload' , args ) ,
contentType : false ,
processData : false ,
timeout : 0 , // disable default timeout set in ajaxSetup()
data : formdata || multipart ,
headers : { 'X-Roundcube-Request' : ref . env . request _token } ,
xhr : function ( ) { var xhr = jQuery . ajaxSettings . xhr ( ) ; if ( ! formdata && xhr . sendAsBinary ) xhr . send = xhr . sendAsBinary ; return xhr ; } ,
success : function ( data ) { ref . http _response ( data ) ; } ,
error : function ( o , status , err ) { ref . http _error ( o , status , err , null , 'attachment' ) ; }
data : formdata ,
headers : { 'X-Roundcube-Request' : this . env . request _token } ,
xhr : function ( ) {
var xhr = $ . ajaxSettings . xhr ( ) ;
if ( xhr . upload && ref . labels . uploadprogress ) {
xhr . upload . onprogress = function ( e ) {
var msg = ref . file _upload _msg ( e . loaded , e . total ) ;
if ( msg ) {
$ ( '#' + ts ) . find ( '.uploading' ) . text ( msg ) ;
}
} ;
}
return xhr ;
} ,
success : function ( data ) {
delete ref . uploads [ ts ] ;
ref . http _response ( data ) ;
} ,
error : function ( o , status , err ) {
delete ref . uploads [ ts ] ;
ref . remove _from _attachment _list ( ts ) ;
ref . http _error ( o , status , err , props . lock , 'attachment' ) ;
}
} ) ;
} ;
}
// get contents of all dropped files
var last = this . env . filedrop . single ? 0 : files . length - 1 ;
for ( var j = 0 , i = 0 , f ; j <= last && ( f = files [ i ] ) ; i ++ ) {
if ( ! f . name ) f . name = f . fileName ;
if ( ! f . size ) f . size = f . fileSize ;
if ( ! f . type ) f . type = 'application/octet-stream' ;
return true ;
} ;
// file name contains non-ASCII characters, do UTF8-binary string conversion.
if ( ! formdata && /[^\x20-\x7E]/ . test ( f . name ) )
f . name _bin = unescape ( encodeURIComponent ( f . name ) ) ;
this . file _upload _msg = function ( current , total )
{
if ( total && current < total ) {
var percent = Math . round ( current / total * 100 ) ,
label = ref . get _label ( 'uploadprogress' ) ;
// filter by file type if requested
if ( this . env . filedrop . filter && ! f . type . match ( new RegExp ( this . env . filedrop . filter ) ) ) {
// TODO: show message to user
continue ;
if ( total >= 1073741824 ) {
total = parseFloat ( total / 1073741824 ) . toFixed ( 1 ) + ' ' . this . get _label ( 'GB' ) ;
current = parseFloat ( current / 1073741824 ) . toFixed ( 1 ) ;
}
size += f . size ;
numfiles ++ ;
// do it the easy way with FormData (FF 4+, Chrome 5+, Safari 5+)
if ( formdata ) {
formdata . append ( fieldname , f ) ;
if ( j == last )
return submit _data ( ) ;
}
// use FileReader supporetd by Firefox 3.6
else if ( window . FileReader ) {
var reader = new FileReader ( ) ;
// closure to pass file properties to async callback function
reader . onload = ( function ( file , j ) {
return function ( e ) {
multipart += 'Content-Disposition: form-data; name="' + fieldname + '"' ;
multipart += '; filename="' + ( f . name _bin || file . name ) + '"' + crlf ;
multipart += 'Content-Length: ' + file . size + crlf ;
multipart += 'Content-Type: ' + file . type + crlf + crlf ;
multipart += reader . result + crlf ;
multipart += dashdash + boundary + crlf ;
if ( j == last ) // we're done, submit the data
return submit _data ( ) ;
}
} ) ( f , j ) ;
reader . readAsBinaryString ( f ) ;
else if ( total >= 1048576 ) {
total = parseFloat ( total / 1048576 ) . toFixed ( 1 ) + ' ' + this . get _label ( 'MB' ) ;
current = parseFloat ( current / 1048576 ) . toFixed ( 1 ) ;
}
// Firefox 3
else if ( f . getAsBinary ) {
multipart += 'Content-Disposition: form-data; name="' + fieldname + '"' ;
multipart += '; filename="' + ( f . name _bin || f . name ) + '"' + crlf ;
multipart += 'Content-Length: ' + f . size + crlf ;
multipart += 'Content-Type: ' + f . type + crlf + crlf ;
multipart += f . getAsBinary ( ) + crlf ;
multipart += dashdash + boundary + crlf ;
if ( j == last )
return submit _data ( ) ;
else if ( total >= 1024 ) {
total = parseInt ( total / 1024 ) + ' ' + this . get _label ( 'KB' ) ;
current = parseInt ( current / 1024 ) ;
}
else {
total = total + ' ' + this . get _label ( 'B' ) ;
}
j ++ ;
return label . replace ( '$percent' , percent + '%' ) . replace ( '$current' , current ) . replace ( '$total' , total ) ;
}
} ;