Improve upload of attachments to messages
parent
1a900b05d9
commit
7369a82bab
4
NEWS
4
NEWS
|
@ -4,12 +4,14 @@
|
||||||
New features
|
New features
|
||||||
- it's now possible to set a default reminder for calendar components
|
- it's now possible to set a default reminder for calendar components
|
||||||
using SOGoCalendarDefaultReminder
|
using SOGoCalendarDefaultReminder
|
||||||
|
- select multiple files to attach to a message or drag'n'drop files onto the
|
||||||
|
mail editor; will also now display progress of uploads
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
- we now automatically convert <img src=data...> into file attachments
|
- we now automatically convert <img src=data...> into file attachments
|
||||||
using CIDs. This prevents Outlook issues.
|
using CIDs. This prevents Outlook issues.
|
||||||
- updated Finnish translation
|
- updated Finnish translation
|
||||||
- XMLHttpRequest is now loaded conditionaly (< IE9)
|
- XMLHttpRequest.js is now loaded conditionaly (< IE9)
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
-
|
-
|
||||||
|
|
|
@ -15,13 +15,6 @@
|
||||||
cssClass = "tbicon_addressbook";
|
cssClass = "tbicon_addressbook";
|
||||||
label = "Contacts";
|
label = "Contacts";
|
||||||
tooltip = "Select a recipient from an Address Book"; },
|
tooltip = "Select a recipient from an Address Book"; },
|
||||||
{ link = "#";
|
|
||||||
isSafe = NO;
|
|
||||||
onclick = "return clickedEditorAttach(this)";
|
|
||||||
image = "tb-compose-attach-flat-24x24.png";
|
|
||||||
cssClass = "tbicon_attach single-window-not-conditional";
|
|
||||||
label = "Attach";
|
|
||||||
tooltip = "Include an attachment"; },
|
|
||||||
{ link = "#";
|
{ link = "#";
|
||||||
isSafe = NO;
|
isSafe = NO;
|
||||||
onclick = "return clickedEditorSave(this);";
|
onclick = "return clickedEditorSave(this);";
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
#import <SOGo/WOResourceManager+SOGo.h>
|
#import <SOGo/WOResourceManager+SOGo.h>
|
||||||
#import <SOGoUI/UIxComponent.h>
|
#import <SOGoUI/UIxComponent.h>
|
||||||
#import <Mailer/SOGoDraftObject.h>
|
#import <Mailer/SOGoDraftObject.h>
|
||||||
|
#import <Mailer/SOGoMailObject+Draft.h>
|
||||||
#import <Mailer/SOGoMailFolder.h>
|
#import <Mailer/SOGoMailFolder.h>
|
||||||
#import <Mailer/SOGoMailAccount.h>
|
#import <Mailer/SOGoMailAccount.h>
|
||||||
#import <Mailer/SOGoMailAccounts.h>
|
#import <Mailer/SOGoMailAccounts.h>
|
||||||
|
@ -63,6 +64,8 @@
|
||||||
#import <Contacts/SOGoContactFolder.h>
|
#import <Contacts/SOGoContactFolder.h>
|
||||||
#import <Contacts/SOGoContactSourceFolder.h>
|
#import <Contacts/SOGoContactSourceFolder.h>
|
||||||
|
|
||||||
|
#import <UI/MailPartViewers/UIxMailSizeFormatter.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
UIxMailEditor
|
UIxMailEditor
|
||||||
|
|
||||||
|
@ -89,8 +92,9 @@
|
||||||
id currentFolder;
|
id currentFolder;
|
||||||
|
|
||||||
/* these are for the inline attachment list */
|
/* these are for the inline attachment list */
|
||||||
NSString *attachmentName;
|
NSDictionary *attachment;
|
||||||
NSArray *attachmentNames;
|
NSArray *attachmentAttrs;
|
||||||
|
NSString *currentAttachment;
|
||||||
NSMutableArray *attachedFiles;
|
NSMutableArray *attachedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +121,9 @@ static NSArray *infoKeys = nil;
|
||||||
priority = @"NORMAL";
|
priority = @"NORMAL";
|
||||||
receipt = nil;
|
receipt = nil;
|
||||||
currentFolder = nil;
|
currentFolder = nil;
|
||||||
|
currentAttachment = nil;
|
||||||
|
attachmentAttrs = nil;
|
||||||
|
attachedFiles = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -137,8 +144,9 @@ static NSArray *infoKeys = nil;
|
||||||
[bcc release];
|
[bcc release];
|
||||||
[sourceUID release];
|
[sourceUID release];
|
||||||
[sourceFolder release];
|
[sourceFolder release];
|
||||||
[attachmentName release];
|
[attachment release];
|
||||||
[attachmentNames release];
|
[currentAttachment release];
|
||||||
|
[attachmentAttrs release];
|
||||||
[attachedFiles release];
|
[attachedFiles release];
|
||||||
[currentFolder release];
|
[currentFolder release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
|
@ -369,14 +377,19 @@ static NSArray *infoKeys = nil;
|
||||||
return (([to count] + [cc count] + [bcc count]) > 0);
|
return (([to count] + [cc count] + [bcc count]) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setAttachmentName: (NSString *) newAttachmentName
|
- (void) setAttachment: (NSDictionary *) newAttachment
|
||||||
{
|
{
|
||||||
ASSIGN (attachmentName, newAttachmentName);
|
ASSIGN (attachment, newAttachment);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *) attachmentName
|
- (NSFormatter *) sizeFormatter
|
||||||
{
|
{
|
||||||
return attachmentName;
|
return [UIxMailSizeFormatter sharedMailSizeFormatter];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *) attachment
|
||||||
|
{
|
||||||
|
return attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* from addresses */
|
/* from addresses */
|
||||||
|
@ -417,7 +430,7 @@ static NSArray *infoKeys = nil;
|
||||||
- (NSDictionary *) storeInfo
|
- (NSDictionary *) storeInfo
|
||||||
{
|
{
|
||||||
[self debugWithFormat:@"storing info ..."];
|
[self debugWithFormat:@"storing info ..."];
|
||||||
return [self valuesForKeys:infoKeys];
|
return [self valuesForKeys: infoKeys];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* contacts search */
|
/* contacts search */
|
||||||
|
@ -517,8 +530,8 @@ static NSArray *infoKeys = nil;
|
||||||
|
|
||||||
- (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
|
- (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
|
||||||
{
|
{
|
||||||
NSMutableDictionary *filenames;
|
NSMutableDictionary *files;
|
||||||
NSDictionary *attachment;
|
NSDictionary *file;
|
||||||
NSArray *parts;
|
NSArray *parts;
|
||||||
unsigned int count, max;
|
unsigned int count, max;
|
||||||
NGMimeBodyPart *part;
|
NGMimeBodyPart *part;
|
||||||
|
@ -527,112 +540,136 @@ static NSArray *infoKeys = nil;
|
||||||
|
|
||||||
parts = [httpBody parts];
|
parts = [httpBody parts];
|
||||||
max = [parts count];
|
max = [parts count];
|
||||||
filenames = [NSMutableDictionary dictionaryWithCapacity: max];
|
files = [NSMutableDictionary dictionaryWithCapacity: max];
|
||||||
|
|
||||||
for (count = 0; count < max; count++)
|
for (count = 0; count < max; count++)
|
||||||
{
|
{
|
||||||
part = [parts objectAtIndex: count];
|
part = [parts objectAtIndex: count];
|
||||||
header = (NGMimeContentDispositionHeaderField *)
|
header = (NGMimeContentDispositionHeaderField *)[part headerForKey: @"content-disposition"];
|
||||||
[part headerForKey: @"content-disposition"];
|
if ([[header name] hasPrefix: @"attachments"])
|
||||||
mimeType = [(NGMimeType *)
|
{
|
||||||
[part headerForKey: @"content-type"] stringValue];
|
mimeType = [(NGMimeType *)[part headerForKey: @"content-type"] stringValue];
|
||||||
filename = [self _fixedFilename: [header filename]];
|
filename = [self _fixedFilename: [header filename]];
|
||||||
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
|
file = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
filename, @"filename",
|
filename, @"filename",
|
||||||
mimeType, @"mimetype", nil];
|
mimeType, @"mimetype",
|
||||||
[filenames setObject: attachment forKey: [header name]];
|
[part body], @"body",
|
||||||
|
nil];
|
||||||
|
[files setObject: file forKey: [NSString stringWithFormat: @"%@_%@", [header name], filename]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filenames;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) _saveAttachments
|
- (NSException *) _saveAttachments
|
||||||
{
|
{
|
||||||
|
NSException *error;
|
||||||
WORequest *request;
|
WORequest *request;
|
||||||
NSEnumerator *allKeys;
|
NSEnumerator *allAttachments;
|
||||||
NSString *key;
|
NSDictionary *attrs, *filenames;
|
||||||
BOOL success;
|
NGMimeType *mimeType;
|
||||||
NSDictionary *filenames;
|
|
||||||
id httpBody;
|
id httpBody;
|
||||||
SOGoDraftObject *co;
|
SOGoDraftObject *co;
|
||||||
|
|
||||||
success = YES;
|
error = nil;
|
||||||
request = [context request];
|
request = [context request];
|
||||||
|
|
||||||
httpBody = [[request httpRequest] body];
|
mimeType = [[request httpRequest] contentType];
|
||||||
filenames = [self _scanAttachmentFilenamesInRequest: httpBody];
|
if ([[mimeType type] isEqualToString: @"multipart"])
|
||||||
|
{
|
||||||
|
httpBody = [[request httpRequest] body];
|
||||||
|
filenames = [self _scanAttachmentFilenamesInRequest: httpBody];
|
||||||
|
|
||||||
co = [self clientObject];
|
co = [self clientObject];
|
||||||
allKeys = [[request formValueKeys] objectEnumerator];
|
allAttachments = [filenames objectEnumerator];
|
||||||
while ((key = [allKeys nextObject]) && success)
|
while ((attrs = [allAttachments nextObject]) && !error)
|
||||||
if ([key hasPrefix: @"attachment"])
|
{
|
||||||
success
|
error = [co saveAttachment: (NSData *) [attrs objectForKey: @"body"]
|
||||||
= (![co saveAttachment: (NSData *) [request formValueForKey: key]
|
withMetadata: attrs];
|
||||||
withMetadata: [filenames objectForKey: key]]);
|
// Keep the name of the last attachment saved
|
||||||
|
ASSIGN(currentAttachment, [attrs objectForKey: @"filename"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) _saveFormInfo
|
- (NSException *) _saveFormInfo
|
||||||
{
|
{
|
||||||
NSDictionary *info;
|
NSDictionary *info;
|
||||||
NSException *error;
|
NSException *error;
|
||||||
BOOL success;
|
|
||||||
SOGoDraftObject *co;
|
SOGoDraftObject *co;
|
||||||
|
|
||||||
co = [self clientObject];
|
co = [self clientObject];
|
||||||
[co fetchInfo];
|
[co fetchInfo];
|
||||||
|
|
||||||
success = YES;
|
error = [self _saveAttachments];
|
||||||
|
if (!error)
|
||||||
if ([self _saveAttachments])
|
|
||||||
{
|
{
|
||||||
info = [self storeInfo];
|
info = [self storeInfo];
|
||||||
[co setHeaders: info];
|
[co setHeaders: info];
|
||||||
[co setIsHTML: isHTML];
|
[co setIsHTML: isHTML];
|
||||||
[co setText: (isHTML ? [NSString stringWithFormat: @"<html>%@</html>", text] : text)];;
|
[co setText: (isHTML ? [NSString stringWithFormat: @"<html>%@</html>", text] : text)];;
|
||||||
error = [co storeInfo];
|
error = [co storeInfo];
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
[self errorWithFormat: @"failed to store draft: %@", error];
|
|
||||||
// TODO: improve error handling
|
|
||||||
success = NO;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
success = NO;
|
|
||||||
|
|
||||||
// TODO: wrap content
|
return error;
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id) failedToSaveFormResponse
|
- (id) failedToSaveFormResponse: (NSString *) msg
|
||||||
{
|
{
|
||||||
// TODO: improve error handling
|
NSDictionary *d;
|
||||||
return [NSException exceptionWithHTTPStatus:500 /* server error */
|
|
||||||
reason:@"failed to store draft object on server!"];
|
d = [NSDictionary dictionaryWithObjectsAndKeys: msg, @"textStatus", nil];
|
||||||
|
|
||||||
|
return [self responseWithStatus: 500
|
||||||
|
andString: [d jsonRepresentation]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* attachment helper */
|
/* attachment helper */
|
||||||
|
|
||||||
- (NSArray *) attachmentNames
|
- (NSArray *) attachmentAttrs
|
||||||
{
|
{
|
||||||
NSArray *a;
|
NSArray *a;
|
||||||
|
SOGoDraftObject *co;
|
||||||
|
SOGoMailObject *mail;
|
||||||
|
|
||||||
if (!attachmentNames)
|
co = [self clientObject];
|
||||||
|
if (!attachmentAttrs || ![co imap4URL])
|
||||||
|
{
|
||||||
|
[co fetchInfo];
|
||||||
|
if ([co IMAP4ID] > -1)
|
||||||
|
{
|
||||||
|
mail = [[[SOGoMailObject alloc] initWithImap4URL: [co imap4URL] inContainer: [co container]] autorelease];
|
||||||
|
a = [mail fetchFileAttachmentKeys];
|
||||||
|
ASSIGN (attachmentAttrs, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentAttachment)
|
||||||
{
|
{
|
||||||
a = [[self clientObject] fetchAttachmentNames];
|
// When currentAttachment is defined, only return the attributes of the last
|
||||||
ASSIGN (attachmentNames,
|
// attachment saved
|
||||||
[a sortedArrayUsingSelector: @selector (compare:)]);
|
NSEnumerator *allAttachments;
|
||||||
|
NSDictionary* attrs;
|
||||||
|
|
||||||
|
allAttachments = [attachmentAttrs objectEnumerator];
|
||||||
|
while ((attrs = [allAttachments nextObject]))
|
||||||
|
{
|
||||||
|
if ([[attrs objectForKey: @"filename"] isEqualToString: currentAttachment])
|
||||||
|
{
|
||||||
|
return [NSArray arrayWithObject: attrs];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return attachmentNames;
|
return attachmentAttrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) hasAttachments
|
- (BOOL) hasAttachments
|
||||||
{
|
{
|
||||||
return [[self attachmentNames] count] > 0 ? YES : NO;
|
return [[self attachmentAttrs] count] > 0 ? YES : NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *) uid
|
- (NSString *) uid
|
||||||
|
@ -658,14 +695,20 @@ static NSArray *infoKeys = nil;
|
||||||
{
|
{
|
||||||
id result;
|
id result;
|
||||||
|
|
||||||
if ([self _saveFormInfo])
|
result = [self _saveFormInfo];
|
||||||
|
if (!result)
|
||||||
{
|
{
|
||||||
result = [[self clientObject] save];
|
result = [[self clientObject] save];
|
||||||
if (!result)
|
}
|
||||||
result = [self responseWith204];
|
if (!result)
|
||||||
|
{
|
||||||
|
attachmentAttrs = nil;
|
||||||
|
NSArray *attrs = [self attachmentAttrs];
|
||||||
|
result = [self responseWithStatus: 200
|
||||||
|
andString: [attrs jsonRepresentation]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
result = [self failedToSaveFormResponse];
|
result = [self failedToSaveFormResponse: [result reason]];
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -740,10 +783,11 @@ static NSArray *infoKeys = nil;
|
||||||
error = [self validateForSend];
|
error = [self validateForSend];
|
||||||
if (!error)
|
if (!error)
|
||||||
{
|
{
|
||||||
if ([self _saveFormInfo])
|
error = [self _saveFormInfo];
|
||||||
|
if (!error)
|
||||||
error = [co sendMail];
|
error = [co sendMail];
|
||||||
else
|
else
|
||||||
error = [self failedToSaveFormResponse];
|
error = [self failedToSaveFormResponse: [error reason]];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
|
|
|
@ -11,26 +11,18 @@
|
||||||
title="panelTitle"
|
title="panelTitle"
|
||||||
const:popup="YES"
|
const:popup="YES"
|
||||||
const:userDefaultsKeys="SOGoMailComposeMessageType,SOGoMailReplyPlacement,SOGoMailSignature"
|
const:userDefaultsKeys="SOGoMailComposeMessageType,SOGoMailReplyPlacement,SOGoMailSignature"
|
||||||
const:jsFiles="UIxMailToSelection.js,ckeditor/ckeditor.js,SOGoAutoCompletion.js,ContactsUI.js">
|
const:jsFiles="UIxMailToSelection.js,ckeditor/ckeditor.js,SOGoAutoCompletion.js,ContactsUI.js,jquery-ui.js,jquery.fileupload.js,jquery.iframe-transport.js"
|
||||||
|
const:cssFiles="jquery.fileupload.css">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var mailIsReply = <var:string value="isMailReply"/>;
|
var mailIsReply = <var:string value="isMailReply"/>;
|
||||||
var sourceUID = <var:string value="sourceUID"/>;
|
var sourceUID = <var:string value="sourceUID"/>;
|
||||||
var sourceFolder = '<var:string value="sourceFolder" const:escapeHTML="NO"/>';
|
var sourceFolder = '<var:string value="sourceFolder" const:escapeHTML="NO"/>';
|
||||||
var localeCode = '<var:string value="localeCode"/>';
|
var localeCode = '<var:string value="localeCode"/>';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="popupMenu" id="contactsMenu">
|
<div class="popupMenu" id="contactsMenu">
|
||||||
<ul><!-- space --></ul>
|
<ul><!-- space --></ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu" id="attachmentsMenu">
|
|
||||||
<ul>
|
|
||||||
<li><var:string label:value="Open"/></li>
|
|
||||||
<li><var:string label:value="Delete" /></li>
|
|
||||||
<li><var:string label:value="Select All" /></li>
|
|
||||||
<li><!-- separator --></li>
|
|
||||||
<li><var:string label:value="Attach File(s)..." /></li>
|
|
||||||
<li><var:string label:value="Attach Web Page..." /></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="menu" id="optionsMenu">
|
<div class="menu" id="optionsMenu">
|
||||||
<ul class="choiceMenu">
|
<ul class="choiceMenu">
|
||||||
|
@ -101,20 +93,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="rightPanel">
|
<div id="rightPanel">
|
||||||
<form const:href="" name="pageform" enctype="multipart/form-data" autocomplete="off">
|
<form href="save" name="pageform" enctype="multipart/form-data" autocomplete="off">
|
||||||
<input type="hidden" name="priority" id="priority" var:value="priority"/>
|
<input type="hidden" name="priority" id="priority" var:value="priority"/>
|
||||||
<input type="hidden" name="receipt" id="receipt" var:value="receipt"/>
|
<input type="hidden" name="receipt" id="receipt" var:value="receipt"/>
|
||||||
<input type="hidden" name="isHTML" id="isHTML" var:value="isHTML"/>
|
<input type="hidden" name="isHTML" id="isHTML" var:value="isHTML"/>
|
||||||
|
|
||||||
<div id="attachmentsArea">
|
|
||||||
<var:string label:value="Attachments:" />
|
|
||||||
<ul id="attachments">
|
|
||||||
<var:foreach list="attachmentNames" item="attachmentName"
|
|
||||||
><li var:title="attachmentName"><img rsrc:src="attachment.gif"
|
|
||||||
/><var:string value="attachmentName"
|
|
||||||
/></li></var:foreach>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="headerArea">
|
<div id="headerArea">
|
||||||
<span class="headerField" const:id="fromField"><var:string label:value="From" />:</span>
|
<span class="headerField" const:id="fromField"><var:string label:value="From" />:</span>
|
||||||
<var:popup const:name="from" const:id="fromSelect"
|
<var:popup const:name="from" const:id="fromSelect"
|
||||||
|
@ -126,19 +109,23 @@
|
||||||
<var:component className="UIxMailToSelection"
|
<var:component className="UIxMailToSelection"
|
||||||
to="to" cc="cc" bcc="bcc" />
|
to="to" cc="cc" bcc="bcc" />
|
||||||
</div>
|
</div>
|
||||||
<div class="addressListElement" id="subjectRow"
|
<div id="subjectRow">
|
||||||
><span class="headerField"><var:string label:value="Subject"
|
<span class="headerField"><var:string label:value="Subject"/>:</span>
|
||||||
/>:</span
|
<input name="subject" type="text" class="textField" var:value="subject"/>
|
||||||
>
|
</div>
|
||||||
<input name="subject"
|
<div id="fileupload">
|
||||||
type="text"
|
<ul id="attachments">
|
||||||
class="textField"
|
<li class="attachButton"><span class="button fileinput-button"><span><img rsrc:src="title_attachment_14x14.png" /> <var:string label:value="Attach"/></span><input id="fileUpload" type="file" name="attachments" const:multiple="multiple"/></span></li>
|
||||||
var:value="subject"
|
<var:foreach list="attachmentAttrs" item="attachment"
|
||||||
/></div>
|
><li class="progressDone" var:data-filename="attachment.filename">
|
||||||
<!-- separator line --><hr class="fieldSeparator"/>
|
<i class="icon-attachment"><!-- icon --></i><a var:href="attachment.url" target="_new"><var:string value="attachment.filename"/></a><span class="muted">(<var:string value="attachment.size" formatter="sizeFormatter" />)</span>
|
||||||
</div>
|
</li></var:foreach>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div><!-- #headerArea -->
|
||||||
<textarea id="text" name="text" rows="30" var:value="text"></textarea>
|
<textarea id="text" name="text" rows="30" var:value="text"></textarea>
|
||||||
<!-- img rsrc:src="tbird_073_compose.png" alt="screenshot" / -->
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="dropZone" style="display: none;"><!-- dropzone --></div>
|
||||||
</var:component>
|
</var:component>
|
||||||
|
|
|
@ -104,24 +104,6 @@ div#headerArea div.addressList
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: hidden; }
|
overflow-x: hidden; }
|
||||||
|
|
||||||
div#attachmentsArea
|
|
||||||
{ display: none;
|
|
||||||
float: right;
|
|
||||||
width: 200px;
|
|
||||||
padding: 2px 5px 0;
|
|
||||||
margin: auto;
|
|
||||||
border-left: 1px solid #888; }
|
|
||||||
|
|
||||||
hr.fieldSeparator
|
|
||||||
{ background-color: #848284;
|
|
||||||
border: 0;
|
|
||||||
clear: both;
|
|
||||||
color: #848284;
|
|
||||||
height: 1px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
width: 100%; }
|
|
||||||
|
|
||||||
input.currentAttachment
|
input.currentAttachment
|
||||||
{ position: fixed;
|
{ position: fixed;
|
||||||
top: 1em;
|
top: 1em;
|
||||||
|
@ -131,30 +113,42 @@ input.attachment
|
||||||
{ position: absolute;
|
{ position: absolute;
|
||||||
left: -1000px; }
|
left: -1000px; }
|
||||||
|
|
||||||
div#compose_attachments_list
|
#dropZone
|
||||||
{ background-color: #ffffff;
|
{ position: absolute;
|
||||||
margin-left: 0px;
|
background: #000 url('upload_document.png') no-repeat center center;
|
||||||
padding: 2px;
|
opacity: 0.6;
|
||||||
border-bottom: 1px solid #fff;
|
border: 4px dashed #fff;
|
||||||
border-right: 1px solid #fff;
|
left: 0px;
|
||||||
border-top: 2px solid #222;
|
right: 0px;
|
||||||
border-left: 2px solid #222;
|
top: 0px;
|
||||||
-moz-border-top-colors: #9c9a94 #000 transparent;
|
bottom: 0px;
|
||||||
-moz-border-left-colors: #9c9a94 #000 transparent; }
|
z-index: 999; }
|
||||||
|
|
||||||
|
#dropZone div
|
||||||
|
{ position: absolute;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
height: 100px;
|
||||||
|
width: 300px;
|
||||||
|
margin: 60px 0 0 -150px;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fileupload {
|
||||||
|
margin-top: 5px;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.fileinput-button
|
||||||
|
{ display: inline-block;
|
||||||
|
float: none; }
|
||||||
|
|
||||||
UL#attachments
|
UL#attachments
|
||||||
{ cursor: default;
|
{ cursor: default;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
height: 100%;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
border-top: 1px solid #222;
|
|
||||||
border-left: 1px solid #222;
|
|
||||||
background-color: #CCDDEC;
|
|
||||||
background-image: url("input_bg.gif");
|
|
||||||
-moz-border-top-colors: #9c9a94 #000 transparent;
|
|
||||||
-moz-border-left-colors: #9c9a94 #000 transparent;
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
list-style-image: none;
|
list-style-image: none;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -163,11 +157,44 @@ UL#attachments
|
||||||
-khtml-user-select: none; }
|
-khtml-user-select: none; }
|
||||||
|
|
||||||
UL#attachments LI
|
UL#attachments LI
|
||||||
|
{ float: left; }
|
||||||
|
|
||||||
|
UL#attachments LI[data-filename]
|
||||||
{ white-space: nowrap;
|
{ white-space: nowrap;
|
||||||
padding-bottom: 1px; }
|
line-height: 18px;
|
||||||
|
margin: 3px 6px; }
|
||||||
|
|
||||||
|
UL#attachments LI[data-filename] SPAN
|
||||||
|
{ margin-left: 5px; }
|
||||||
|
|
||||||
|
UL#attachments LI[data-filename] A,
|
||||||
|
UL#attachments LI[data-filename] SPAN
|
||||||
|
{ padding-left: 2px;
|
||||||
|
vertical-align: top; }
|
||||||
|
|
||||||
UL#attachments LI IMG
|
UL#attachments LI IMG
|
||||||
{ vertical-align: bottom; }
|
{ vertical-align: top; }
|
||||||
|
|
||||||
|
UL#attachments .icon-attachment
|
||||||
|
{ background: url('attachment.png') no-repeat top left;
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px; }
|
||||||
|
UL#attachments .progress0 .icon-attachment
|
||||||
|
{ background-position: 0px 0px; }
|
||||||
|
UL#attachments .progress1 .icon-attachment
|
||||||
|
{ background-position: -16px 0px; }
|
||||||
|
UL#attachments .progress2 .icon-attachment
|
||||||
|
{ background-position: -32px 0px; }
|
||||||
|
UL#attachments .progress3 .icon-attachment
|
||||||
|
{ background-position: -48px 0px; }
|
||||||
|
UL#attachments .progress4 .icon-attachment
|
||||||
|
{ background-position: -64px 0px; }
|
||||||
|
UL#attachments .progressDone .icon-attachment
|
||||||
|
{ background-position: -80px 0px; }
|
||||||
|
UL#attachments .progressDone .icon-attachment:hover
|
||||||
|
{ background-position: -96px 0px;
|
||||||
|
cursor: pointer; }
|
||||||
|
|
||||||
#pageContent TEXTAREA
|
#pageContent TEXTAREA
|
||||||
{ width: 99%; }
|
{ width: 99%; }
|
||||||
|
@ -176,6 +203,9 @@ TEXTAREA#text
|
||||||
{ display: none;
|
{ display: none;
|
||||||
background: #fff; }
|
background: #fff; }
|
||||||
|
|
||||||
|
#cke_text
|
||||||
|
{ clear: both; }
|
||||||
|
|
||||||
/* Contacts search pane */
|
/* Contacts search pane */
|
||||||
|
|
||||||
DIV#contactsSearch
|
DIV#contactsSearch
|
||||||
|
|
|
@ -134,13 +134,10 @@ function onValidateDone(onSuccess) {
|
||||||
var safetyNet = createElement("div", "javascriptSafetyNet");
|
var safetyNet = createElement("div", "javascriptSafetyNet");
|
||||||
$('pageContent').insert({top: safetyNet});
|
$('pageContent').insert({top: safetyNet});
|
||||||
|
|
||||||
var input = currentAttachmentInput();
|
if (!document.busyAnim) {
|
||||||
if (input)
|
var toolbar = document.getElementById("toolbar");
|
||||||
input.parentNode.removeChild(input);
|
|
||||||
|
|
||||||
var toolbar = document.getElementById("toolbar");
|
|
||||||
if (!document.busyAnim)
|
|
||||||
document.busyAnim = startAnimation(toolbar);
|
document.busyAnim = startAnimation(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
var lastRow = $("lastRow");
|
var lastRow = $("lastRow");
|
||||||
lastRow.down("select").name = "popup_last";
|
lastRow.down("select").name = "popup_last";
|
||||||
|
@ -149,8 +146,6 @@ function onValidateDone(onSuccess) {
|
||||||
|
|
||||||
document.pageform.action = "send";
|
document.pageform.action = "send";
|
||||||
|
|
||||||
AIM.submit($(document.pageform), {'onComplete' : onPostComplete});
|
|
||||||
|
|
||||||
if (typeof onSuccess == 'function')
|
if (typeof onSuccess == 'function')
|
||||||
onSuccess();
|
onSuccess();
|
||||||
|
|
||||||
|
@ -159,7 +154,8 @@ function onValidateDone(onSuccess) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPostComplete(response) {
|
function onPostComplete(http) {
|
||||||
|
var response = http.responseText;
|
||||||
if (response && response.length > 0) {
|
if (response && response.length > 0) {
|
||||||
var jsonResponse = response.evalJSON();
|
var jsonResponse = response.evalJSON();
|
||||||
if (jsonResponse["status"] == "success") {
|
if (jsonResponse["status"] == "success") {
|
||||||
|
@ -192,93 +188,67 @@ function onPostComplete(response) {
|
||||||
|
|
||||||
function clickedEditorSend() {
|
function clickedEditorSend() {
|
||||||
onValidate(function() {
|
onValidate(function() {
|
||||||
document.pageform.submit();
|
triggerAjaxRequest(document.pageform.action,
|
||||||
|
onPostComplete,
|
||||||
|
null,
|
||||||
|
Form.serialize(document.pageform), // excludes the file input
|
||||||
|
{ "Content-type": "application/x-www-form-urlencoded" });
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentAttachmentInput() {
|
function formatBytes(bytes, si) {
|
||||||
var input = null;
|
var thresh = si ? 1000 : 1024;
|
||||||
|
if (bytes < thresh) return bytes + ' B';
|
||||||
var inputs = $("attachmentsArea").getElementsByTagName("input");
|
var units = si ? ['KiB','MiB','GiB'] : ['KB','MB','GB'];
|
||||||
var i = 0;
|
var u = -1;
|
||||||
while (!input && i < inputs.length)
|
do {
|
||||||
if ($(inputs[i]).hasClassName("currentAttachment"))
|
bytes /= thresh;
|
||||||
input = inputs[i];
|
++u;
|
||||||
else
|
} while (bytes >= thresh);
|
||||||
i++;
|
return bytes.toFixed(1) + ' ' + units[u];
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickedEditorAttach() {
|
function createAttachment(file) {
|
||||||
var input = currentAttachmentInput();
|
var list = $('attachments');
|
||||||
if (!input) {
|
var attachment;
|
||||||
var area = $("attachmentsArea");
|
if (list.select('[data-filename="'+file.name+'"]').length == 0) {
|
||||||
|
// File is not already uploaded
|
||||||
|
var attachment = createElement('li', null, ['muted progress0'], null, { 'data-filename': file.name }, list);
|
||||||
|
attachment.appendChild(new Element('i', { 'class': 'icon-attachment' }));
|
||||||
|
var a = createElement('a', null, null, null, {'href': '#', 'target': '_new' }, attachment);
|
||||||
|
|
||||||
if (!area.style.display) {
|
a.appendChild(document.createTextNode(file.name));
|
||||||
area.setStyle({ display: "block" });
|
if (file.size)
|
||||||
onWindowResize(null);
|
attachment.appendChild(new Element('span', { 'class': 'muted' }).update('(' + formatBytes(file.size, true) + ')'));
|
||||||
}
|
|
||||||
var inputs = area.getElementsByTagName("input");
|
|
||||||
var attachmentName = "attachment" + attachmentCount;
|
|
||||||
var newAttachment = createElement("input", attachmentName,
|
|
||||||
"currentAttachment", null,
|
|
||||||
{ type: "file",
|
|
||||||
name: attachmentName },
|
|
||||||
area);
|
|
||||||
attachmentCount++;
|
|
||||||
newAttachment.observe("change",
|
|
||||||
onAttachmentChange.bindAsEventListener(newAttachment));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return attachment;
|
||||||
}
|
|
||||||
|
|
||||||
function onAttachmentChange(event) {
|
|
||||||
if (this.value == "")
|
|
||||||
this.parentNode.removeChild(this);
|
|
||||||
else {
|
|
||||||
this.addClassName("attachment");
|
|
||||||
this.removeClassName("currentAttachment");
|
|
||||||
var list = $("attachments");
|
|
||||||
createAttachment(this, list);
|
|
||||||
clickedEditorAttach(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createAttachment(node, list) {
|
|
||||||
var attachment = createElement("li", null, null, { node: node }, null, list);
|
|
||||||
createElement("img", null, null, { src: ResourcesURL + "/attachment.gif" },
|
|
||||||
null, attachment);
|
|
||||||
|
|
||||||
var filename = node.value;
|
|
||||||
var separator;
|
|
||||||
if (navigator.appVersion.indexOf("Windows") > -1)
|
|
||||||
separator = "\\";
|
|
||||||
else
|
|
||||||
separator = "/";
|
|
||||||
var fileArray = filename.split(separator);
|
|
||||||
var attachmentName = document.createTextNode(fileArray[fileArray.length-1]);
|
|
||||||
attachment.appendChild(attachmentName);
|
|
||||||
attachment.writeAttribute("title", fileArray[fileArray.length-1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickedEditorSave() {
|
function clickedEditorSave() {
|
||||||
var input = currentAttachmentInput();
|
|
||||||
if (input)
|
|
||||||
input.parentNode.removeChild(input);
|
|
||||||
|
|
||||||
var lastRow = $("lastRow");
|
var lastRow = $("lastRow");
|
||||||
lastRow.down("select").name = "popup_last";
|
lastRow.down("select").name = "popup_last";
|
||||||
|
|
||||||
window.shouldPreserve = true;
|
window.shouldPreserve = true;
|
||||||
document.pageform.action = "save";
|
document.pageform.action = "save";
|
||||||
document.pageform.submit();
|
|
||||||
|
|
||||||
if (window.opener && window.opener.open && !window.opener.closed)
|
triggerAjaxRequest(document.pageform.action, function (http) {
|
||||||
window.opener.refreshFolderByType('draft');
|
if (http.readyState == 4) {
|
||||||
|
if (http.status == 200) {
|
||||||
|
if (window.opener && window.opener.open && !window.opener.closed)
|
||||||
|
window.opener.refreshFolderByType('draft');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var response = http.responseText.evalJSON(true);
|
||||||
|
showAlertDialog("Error while saving the draft: " + response.textStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
Form.serialize(document.pageform), // excludes the file input
|
||||||
|
{ "Content-type": "application/x-www-form-urlencoded" });
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -301,10 +271,6 @@ function onTextFocus(event) {
|
||||||
}
|
}
|
||||||
MailEditor.textFirstFocus = false;
|
MailEditor.textFirstFocus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var input = currentAttachmentInput();
|
|
||||||
if (input)
|
|
||||||
input.parentNode.removeChild(input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTextKeyDown(event) {
|
function onTextKeyDown(event) {
|
||||||
|
@ -397,7 +363,6 @@ function onHTMLFocus(event) {
|
||||||
|
|
||||||
function initAddresses() {
|
function initAddresses() {
|
||||||
var addressList = $("addressList");
|
var addressList = $("addressList");
|
||||||
var i = 1;
|
|
||||||
addressList.select("input.textField").each(function (input) {
|
addressList.select("input.textField").each(function (input) {
|
||||||
if (!input.readAttribute("readonly")) {
|
if (!input.readAttribute("readonly")) {
|
||||||
input.addInterface(SOGoAutoCompletionInterface);
|
input.addInterface(SOGoAutoCompletionInterface);
|
||||||
|
@ -424,23 +389,84 @@ function configureDragHandle() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function configureAttachments() {
|
||||||
|
var list = $("attachments");
|
||||||
|
|
||||||
|
if (!list) return;
|
||||||
|
|
||||||
|
list.on('click', 'a', function (event, element) {
|
||||||
|
if (!element.up('li').hasClassName('progressDone'))
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
list.on('click', 'i.icon-attachment', function (event, element) {
|
||||||
|
var item = element.up('li');
|
||||||
|
if (item.hasClassName('progressDone')) {
|
||||||
|
var filename = item.readAttribute('data-filename');
|
||||||
|
var url = "" + window.location;
|
||||||
|
var parts = url.split("/");
|
||||||
|
parts[parts.length-1] = "deleteAttachment?filename=" + encodeURIComponent(filename);
|
||||||
|
url = parts.join("/");
|
||||||
|
triggerAjaxRequest(url, attachmentDeleteCallback, item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var dropzone = jQuery('#dropZone');
|
||||||
|
jQuery('#fileUpload').fileupload({
|
||||||
|
// With singleFileUploads option enabled, the 'add' and 'done' (or 'fail') callbacks
|
||||||
|
// are called once for each file in the selection for XHR file uploads
|
||||||
|
singleFileUploads: true,
|
||||||
|
dataType: 'json',
|
||||||
|
add: function (e, data) {
|
||||||
|
var file = data.files[0];
|
||||||
|
var attachment = createAttachment(file);
|
||||||
|
if (attachment) {
|
||||||
|
file.attachment = attachment;
|
||||||
|
data.submit();
|
||||||
|
}
|
||||||
|
if (dropzone.is(":visible"))
|
||||||
|
dropzone.fadeOut('fast');
|
||||||
|
},
|
||||||
|
done: function (e, data) {
|
||||||
|
var attachment = data.files[0].attachment;
|
||||||
|
var attrs = data.result[data.result.length-1];
|
||||||
|
attachment.className = 'progressDone';
|
||||||
|
attachment.down('a').setAttribute('href', attrs.url);
|
||||||
|
if (window.opener && window.opener.open && !window.opener.closed)
|
||||||
|
window.opener.refreshFolderByType('draft');
|
||||||
|
},
|
||||||
|
fail: function (e, data) {
|
||||||
|
var attachment = data.files[0].attachment;
|
||||||
|
var filename = data.files[0].name;
|
||||||
|
var response = data.xhr().response.evalJSON();
|
||||||
|
showAlertDialog("Error while uploading the file " + filename + ": " + response.textStatus);
|
||||||
|
attachment.remove();
|
||||||
|
},
|
||||||
|
dragover: function (e, data) {
|
||||||
|
if (!dropzone.is(":visible"))
|
||||||
|
dropzone.show();
|
||||||
|
},
|
||||||
|
progress: function (e, data) {
|
||||||
|
var progress = parseInt(data.loaded / data.total * 4, 10);
|
||||||
|
var attachment = data.files[0].attachment;
|
||||||
|
attachment.className = 'muted progress' + progress;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dropzone.on('dragleave', function (e) {
|
||||||
|
dropzone.fadeOut('fast');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initMailEditor() {
|
function initMailEditor() {
|
||||||
if (composeMode != "html" && $("text"))
|
if (composeMode != "html" && $("text"))
|
||||||
$("text").style.display = "block";
|
$("text").style.display = "block";
|
||||||
|
|
||||||
var list = $("attachments");
|
configureAttachments();
|
||||||
if (!list) return;
|
|
||||||
list.multiselect = true;
|
|
||||||
list.on("click", onRowClick);
|
|
||||||
list.attachMenu("attachmentsMenu");
|
|
||||||
var elements = $(list).childNodesWithTag("li");
|
|
||||||
if (elements.length > 0)
|
|
||||||
$("attachmentsArea").setStyle({ display: "block" });
|
|
||||||
|
|
||||||
var textarea = $("text");
|
|
||||||
|
|
||||||
initAddresses();
|
initAddresses();
|
||||||
|
|
||||||
|
var textarea = $("text");
|
||||||
var focusField = textarea;
|
var focusField = textarea;
|
||||||
if (!mailIsReply) {
|
if (!mailIsReply) {
|
||||||
focusField = $("addr_0");
|
focusField = $("addr_0");
|
||||||
|
@ -546,10 +572,6 @@ function onMenuCheckReturnReceipt(event) {
|
||||||
|
|
||||||
function getMenus() {
|
function getMenus() {
|
||||||
return {
|
return {
|
||||||
"attachmentsMenu": [ null, onRemoveAttachments,
|
|
||||||
onSelectAllAttachments,
|
|
||||||
"-",
|
|
||||||
clickedEditorAttach, null],
|
|
||||||
"optionsMenu": [ onMenuCheckReturnReceipt,
|
"optionsMenu": [ onMenuCheckReturnReceipt,
|
||||||
"-",
|
"-",
|
||||||
"priorityMenu" ],
|
"priorityMenu" ],
|
||||||
|
@ -561,27 +583,6 @@ function getMenus() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRemoveAttachments() {
|
|
||||||
var list = $("attachments");
|
|
||||||
var nodes = list.getSelectedNodes();
|
|
||||||
for (var i = nodes.length-1; i > -1; i--) {
|
|
||||||
var input = $(nodes[i]).node;
|
|
||||||
if (input) {
|
|
||||||
input.parentNode.removeChild(input);
|
|
||||||
list.removeChild(nodes[i]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var filename = nodes[i].title;
|
|
||||||
var url = "" + window.location;
|
|
||||||
var parts = url.split("/");
|
|
||||||
parts[parts.length-1] = "deleteAttachment?filename=" + encodeURIComponent(filename);
|
|
||||||
url = parts.join("/");
|
|
||||||
triggerAjaxRequest(url, attachmentDeleteCallback,
|
|
||||||
nodes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function attachmentDeleteCallback(http) {
|
function attachmentDeleteCallback(http) {
|
||||||
if (http.readyState == 4) {
|
if (http.readyState == 4) {
|
||||||
if (isHttpStatus204(http.status)) {
|
if (isHttpStatus204(http.status)) {
|
||||||
|
@ -623,10 +624,6 @@ function onMenuSetPriority(event) {
|
||||||
priorityInput.value = priority;
|
priorityInput.value = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelectAllAttachments() {
|
|
||||||
$("attachments").selectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelectOptions(event) {
|
function onSelectOptions(event) {
|
||||||
if (event.button == 0 || (isWebKit() && event.button == 1)) {
|
if (event.button == 0 || (isWebKit() && event.button == 1)) {
|
||||||
var node = getTarget(event);
|
var node = getTarget(event);
|
||||||
|
@ -645,39 +642,21 @@ function onWindowResize(event) {
|
||||||
var headerarea = $("headerArea");
|
var headerarea = $("headerArea");
|
||||||
var totalwidth = $("rightPanel").getWidth();
|
var totalwidth = $("rightPanel").getWidth();
|
||||||
|
|
||||||
var attachmentsarea = $("attachmentsArea");
|
|
||||||
var attachmentswidth = 0;
|
|
||||||
var subjectfield = headerarea.down("div#subjectRow span.headerField");
|
var subjectfield = headerarea.down("div#subjectRow span.headerField");
|
||||||
var subjectinput = headerarea.down("div#subjectRow input.textField");
|
var subjectinput = headerarea.down("div#subjectRow input.textField");
|
||||||
if (attachmentsarea.style.display) {
|
|
||||||
// Resize attachments list
|
|
||||||
attachmentswidth = attachmentsarea.getWidth();
|
|
||||||
fromfield = $(document).getElementsByClassName('headerField', headerarea)[0];
|
|
||||||
var height = headerarea.getHeight() - fromfield.getHeight() - subjectfield.getHeight() - 10;
|
|
||||||
if (Prototype.Browser.IE)
|
|
||||||
$("attachments").setStyle({ height: (height - 13) + 'px' });
|
|
||||||
else
|
|
||||||
$("attachments").setStyle({ height: height + 'px' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resize subject field
|
// Resize subject field
|
||||||
subjectinput.setStyle({ width: (totalwidth
|
subjectinput.setStyle({ width: (totalwidth
|
||||||
- $(subjectfield).getWidth()
|
- $(subjectfield).getWidth()
|
||||||
- attachmentswidth
|
|
||||||
- 17) + 'px' });
|
- 17) + 'px' });
|
||||||
// Resize from field
|
// Resize from field
|
||||||
$("fromSelect").setStyle({ width: (totalwidth
|
$("fromSelect").setStyle({ width: (totalwidth
|
||||||
- $("fromField").getWidth()
|
- $("fromField").getWidth()
|
||||||
- attachmentswidth
|
|
||||||
- 15) + 'px' });
|
- 15) + 'px' });
|
||||||
|
|
||||||
// Resize address fields
|
// Resize address fields
|
||||||
var addresslist = $('addressList');
|
// var addresslist = $('addressList');
|
||||||
addresslist.setStyle({ width: (totalwidth - attachmentswidth - 10) + 'px' });
|
// addresslist.setStyle({ width: (totalwidth - 10) + 'px' });
|
||||||
|
|
||||||
// Set textarea position
|
|
||||||
var hr = headerarea.select("hr").first();
|
|
||||||
textarea.setStyle({ 'top': hr.offsetTop + 'px' });
|
|
||||||
|
|
||||||
// Resize the textarea (message content)
|
// Resize the textarea (message content)
|
||||||
var offsetTop = $('rightPanel').offsetTop + headerarea.getHeight();
|
var offsetTop = $('rightPanel').offsetTop + headerarea.getHeight();
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -714,7 +714,7 @@ DIV, TEXTAREA, INPUT, SELECT
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
font-size: inherit; }
|
font-size: inherit; }
|
||||||
|
|
||||||
INPUT[type="text"], INPUT[type="password"], INPUT[type="file"],
|
INPUT[type="text"], INPUT[type="password"],
|
||||||
TEXTAREA
|
TEXTAREA
|
||||||
{ border-top: 1px solid #909090;
|
{ border-top: 1px solid #909090;
|
||||||
border-left: 1px solid #909090;
|
border-left: 1px solid #909090;
|
||||||
|
@ -730,7 +730,7 @@ TEXTAREA[disabled], TEXTAREA[readonly]
|
||||||
border-color: #ccc;
|
border-color: #ccc;
|
||||||
color: #9ABCD8; }
|
color: #9ABCD8; }
|
||||||
|
|
||||||
INPUT[type="text"], INPUT[type="password"], INPUT[type="file"], TEXTAREA
|
INPUT[type="text"], INPUT[type="password"], TEXTAREA
|
||||||
{ background: url("input_bg.gif"); }
|
{ background: url("input_bg.gif"); }
|
||||||
|
|
||||||
TEXTAREA
|
TEXTAREA
|
||||||
|
@ -788,7 +788,7 @@ INPUT[name="search"]
|
||||||
* Avoid using DIVS as buttons, they're only helpful when they have multiple
|
* Avoid using DIVS as buttons, they're only helpful when they have multiple
|
||||||
* listeners for "onclick"
|
* listeners for "onclick"
|
||||||
*/
|
*/
|
||||||
A.button {
|
.button, a.button {
|
||||||
padding: 0px 0.5em;
|
padding: 0px 0.5em;
|
||||||
background: transparent url('btn_a_bg.png') no-repeat scroll top right;
|
background: transparent url('btn_a_bg.png') no-repeat scroll top right;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -801,28 +801,30 @@ A.button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
A.button SPAN {
|
.button SPAN {
|
||||||
background: transparent url('btn_span_bg.png') no-repeat;
|
background: transparent url('btn_span_bg.png') no-repeat;
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 13px;
|
line-height: 13px;
|
||||||
|
height: 13px;
|
||||||
padding: 5px 2px 5px 5px;
|
padding: 5px 2px 5px 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-width: 70px;
|
min-width: 70px;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
A.button.actionButton SPAN
|
.button.actionButton SPAN
|
||||||
{ font-weight: bold; }
|
{ font-weight: bold; }
|
||||||
|
|
||||||
A.button:active SPAN
|
.button:active SPAN
|
||||||
{ background-position: bottom left;
|
{ background-position: bottom left;
|
||||||
padding: 6px 2px 4px 5px; }
|
padding: 6px 2px 4px 5px; }
|
||||||
|
|
||||||
A.disabled.button,
|
.disabled.button,
|
||||||
A.disabled.button:active,
|
.disabled.button:active,
|
||||||
A.disabled.button SPAN
|
.disabled.button SPAN
|
||||||
{ color: #999; }
|
{ color: #999; }
|
||||||
|
|
||||||
A.disabled.button:active SPAN
|
.disabled.button:active SPAN
|
||||||
{ background-position: top left;
|
{ background-position: top left;
|
||||||
padding: 5px 2px 5px 5px; }
|
padding: 5px 2px 5px 5px; }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
/*
|
||||||
|
* jQuery File Upload Plugin CSS 1.3.0
|
||||||
|
* https://github.com/blueimp/jQuery-File-Upload
|
||||||
|
*
|
||||||
|
* Copyright 2013, Sebastian Tschan
|
||||||
|
* https://blueimp.net
|
||||||
|
*
|
||||||
|
* Licensed under the MIT license:
|
||||||
|
* http://www.opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
.fileinput-button {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.fileinput-button input {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0;
|
||||||
|
-ms-filter: 'alpha(opacity=0)';
|
||||||
|
font-size: 200px;
|
||||||
|
direction: ltr;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixes for IE < 8 */
|
||||||
|
@media screen\9 {
|
||||||
|
.fileinput-button input {
|
||||||
|
filter: alpha(opacity=0);
|
||||||
|
font-size: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* jQuery Iframe Transport Plugin 1.8.1
|
||||||
|
* https://github.com/blueimp/jQuery-File-Upload
|
||||||
|
*
|
||||||
|
* Copyright 2011, Sebastian Tschan
|
||||||
|
* https://blueimp.net
|
||||||
|
*
|
||||||
|
* Licensed under the MIT license:
|
||||||
|
* http://www.opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*jslint unparam: true, nomen: true */
|
||||||
|
/*global define, window, document */
|
||||||
|
|
||||||
|
(function (factory) {
|
||||||
|
'use strict';
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
// Register as an anonymous AMD module:
|
||||||
|
define(['jquery'], factory);
|
||||||
|
} else {
|
||||||
|
// Browser globals:
|
||||||
|
factory(window.jQuery);
|
||||||
|
}
|
||||||
|
}(function ($) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Helper variable to create unique names for the transport iframes:
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
// The iframe transport accepts four additional options:
|
||||||
|
// options.fileInput: a jQuery collection of file input fields
|
||||||
|
// options.paramName: the parameter name for the file form data,
|
||||||
|
// overrides the name property of the file input field(s),
|
||||||
|
// can be a string or an array of strings.
|
||||||
|
// options.formData: an array of objects with name and value properties,
|
||||||
|
// equivalent to the return data of .serializeArray(), e.g.:
|
||||||
|
// [{name: 'a', value: 1}, {name: 'b', value: 2}]
|
||||||
|
// options.initialIframeSrc: the URL of the initial iframe src,
|
||||||
|
// by default set to "javascript:false;"
|
||||||
|
$.ajaxTransport('iframe', function (options) {
|
||||||
|
if (options.async) {
|
||||||
|
// javascript:false as initial iframe src
|
||||||
|
// prevents warning popups on HTTPS in IE6:
|
||||||
|
/*jshint scripturl: true */
|
||||||
|
var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
|
||||||
|
/*jshint scripturl: false */
|
||||||
|
form,
|
||||||
|
iframe,
|
||||||
|
addParamChar;
|
||||||
|
return {
|
||||||
|
send: function (_, completeCallback) {
|
||||||
|
form = $('<form style="display:none;"></form>');
|
||||||
|
form.attr('accept-charset', options.formAcceptCharset);
|
||||||
|
addParamChar = /\?/.test(options.url) ? '&' : '?';
|
||||||
|
// XDomainRequest only supports GET and POST:
|
||||||
|
if (options.type === 'DELETE') {
|
||||||
|
options.url = options.url + addParamChar + '_method=DELETE';
|
||||||
|
options.type = 'POST';
|
||||||
|
} else if (options.type === 'PUT') {
|
||||||
|
options.url = options.url + addParamChar + '_method=PUT';
|
||||||
|
options.type = 'POST';
|
||||||
|
} else if (options.type === 'PATCH') {
|
||||||
|
options.url = options.url + addParamChar + '_method=PATCH';
|
||||||
|
options.type = 'POST';
|
||||||
|
}
|
||||||
|
// IE versions below IE8 cannot set the name property of
|
||||||
|
// elements that have already been added to the DOM,
|
||||||
|
// so we set the name along with the iframe HTML markup:
|
||||||
|
counter += 1;
|
||||||
|
iframe = $(
|
||||||
|
'<iframe src="' + initialIframeSrc +
|
||||||
|
'" name="iframe-transport-' + counter + '"></iframe>'
|
||||||
|
).bind('load', function () {
|
||||||
|
var fileInputClones,
|
||||||
|
paramNames = $.isArray(options.paramName) ?
|
||||||
|
options.paramName : [options.paramName];
|
||||||
|
iframe
|
||||||
|
.unbind('load')
|
||||||
|
.bind('load', function () {
|
||||||
|
var response;
|
||||||
|
// Wrap in a try/catch block to catch exceptions thrown
|
||||||
|
// when trying to access cross-domain iframe contents:
|
||||||
|
try {
|
||||||
|
response = iframe.contents();
|
||||||
|
// Google Chrome and Firefox do not throw an
|
||||||
|
// exception when calling iframe.contents() on
|
||||||
|
// cross-domain requests, so we unify the response:
|
||||||
|
if (!response.length || !response[0].firstChild) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
response = undefined;
|
||||||
|
}
|
||||||
|
// The complete callback returns the
|
||||||
|
// iframe content document as response object:
|
||||||
|
completeCallback(
|
||||||
|
200,
|
||||||
|
'success',
|
||||||
|
{'iframe': response}
|
||||||
|
);
|
||||||
|
// Fix for IE endless progress bar activity bug
|
||||||
|
// (happens on form submits to iframe targets):
|
||||||
|
$('<iframe src="' + initialIframeSrc + '"></iframe>')
|
||||||
|
.appendTo(form);
|
||||||
|
window.setTimeout(function () {
|
||||||
|
// Removing the form in a setTimeout call
|
||||||
|
// allows Chrome's developer tools to display
|
||||||
|
// the response result
|
||||||
|
form.remove();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
form
|
||||||
|
.prop('target', iframe.prop('name'))
|
||||||
|
.prop('action', options.url)
|
||||||
|
.prop('method', options.type);
|
||||||
|
if (options.formData) {
|
||||||
|
$.each(options.formData, function (index, field) {
|
||||||
|
$('<input type="hidden"/>')
|
||||||
|
.prop('name', field.name)
|
||||||
|
.val(field.value)
|
||||||
|
.appendTo(form);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (options.fileInput && options.fileInput.length &&
|
||||||
|
options.type === 'POST') {
|
||||||
|
fileInputClones = options.fileInput.clone();
|
||||||
|
// Insert a clone for each file input field:
|
||||||
|
options.fileInput.after(function (index) {
|
||||||
|
return fileInputClones[index];
|
||||||
|
});
|
||||||
|
if (options.paramName) {
|
||||||
|
options.fileInput.each(function (index) {
|
||||||
|
$(this).prop(
|
||||||
|
'name',
|
||||||
|
paramNames[index] || options.paramName
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Appending the file input fields to the hidden form
|
||||||
|
// removes them from their original location:
|
||||||
|
form
|
||||||
|
.append(options.fileInput)
|
||||||
|
.prop('enctype', 'multipart/form-data')
|
||||||
|
// enctype must be set as encoding for IE:
|
||||||
|
.prop('encoding', 'multipart/form-data');
|
||||||
|
}
|
||||||
|
form.submit();
|
||||||
|
// Insert the file input fields at their original location
|
||||||
|
// by replacing the clones with the originals:
|
||||||
|
if (fileInputClones && fileInputClones.length) {
|
||||||
|
options.fileInput.each(function (index, input) {
|
||||||
|
var clone = $(fileInputClones[index]);
|
||||||
|
$(input).prop('name', clone.prop('name'));
|
||||||
|
clone.replaceWith(input);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
form.append(iframe).appendTo(document.body);
|
||||||
|
},
|
||||||
|
abort: function () {
|
||||||
|
if (iframe) {
|
||||||
|
// javascript:false as iframe src aborts the request
|
||||||
|
// and prevents warning popups on HTTPS in IE6.
|
||||||
|
// concat is used to avoid the "Script URL" JSLint error:
|
||||||
|
iframe
|
||||||
|
.unbind('load')
|
||||||
|
.prop('src', initialIframeSrc);
|
||||||
|
}
|
||||||
|
if (form) {
|
||||||
|
form.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// The iframe transport returns the iframe content document as response.
|
||||||
|
// The following adds converters from iframe to text, json, html, xml
|
||||||
|
// and script.
|
||||||
|
// Please note that the Content-Type for JSON responses has to be text/plain
|
||||||
|
// or text/html, if the browser doesn't include application/json in the
|
||||||
|
// Accept header, else IE will show a download dialog.
|
||||||
|
// The Content-Type for XML responses on the other hand has to be always
|
||||||
|
// application/xml or text/xml, so IE properly parses the XML response.
|
||||||
|
// See also
|
||||||
|
// https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
|
||||||
|
$.ajaxSetup({
|
||||||
|
converters: {
|
||||||
|
'iframe text': function (iframe) {
|
||||||
|
return iframe && $(iframe[0].body).text();
|
||||||
|
},
|
||||||
|
'iframe json': function (iframe) {
|
||||||
|
return iframe && $.parseJSON($(iframe[0].body).text());
|
||||||
|
},
|
||||||
|
'iframe html': function (iframe) {
|
||||||
|
return iframe && $(iframe[0].body).html();
|
||||||
|
},
|
||||||
|
'iframe xml': function (iframe) {
|
||||||
|
var xmlDoc = iframe && iframe[0];
|
||||||
|
return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
|
||||||
|
$.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
|
||||||
|
$(xmlDoc.body).html());
|
||||||
|
},
|
||||||
|
'iframe script': function (iframe) {
|
||||||
|
return iframe && $.globalEval($(iframe[0].body).text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}));
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Loading…
Reference in New Issue