]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1188 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Thu, 18 Oct 2007 22:55:51 +0000 (22:55 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Thu, 18 Oct 2007 22:55:51 +0000 (22:55 +0000)
62 files changed:
ChangeLog
NEWS
SoObjects/Contacts/SOGoContactGCSFolder.m
SoObjects/Mailer/SOGoMailAccount.m
SoObjects/Mailer/SOGoMailFolder.m
SoObjects/Mailer/SOGoMailObject.h
SoObjects/Mailer/SOGoMailObject.m
SoObjects/SOGo/SOGoFolder.m
SoObjects/SOGo/SOGoUser.h
SoObjects/SOGo/SOGoUser.m
SoObjects/SOGo/SOGoWebAuthenticator.m
UI/Common/English.lproj/Localizable.strings
UI/Common/French.lproj/Localizable.strings
UI/Common/German.lproj/Localizable.strings
UI/Common/UIxAclEditor.h
UI/Common/UIxAclEditor.m
UI/Common/UIxPageFrame.h
UI/Common/UIxPageFrame.m
UI/MailerUI/English.lproj/Localizable.strings
UI/MailerUI/French.lproj/Localizable.strings
UI/MailerUI/German.lproj/Localizable.strings
UI/MailerUI/UIxMailActions.m
UI/MailerUI/UIxMailFolderActions.m
UI/MailerUI/product.plist
UI/MainUI/SOGoRootPage.m
UI/SOGoUI/GNUmakefile
UI/SOGoUI/SOGoACLAdvisory.h
UI/SOGoUI/SOGoACLAdvisory.m
UI/SOGoUI/SOGoFolderAdvisory.h [new file with mode: 0644]
UI/SOGoUI/SOGoFolderAdvisory.m [new file with mode: 0644]
UI/Scheduler/UIxAppointmentEditor.m
UI/Templates/ContactsUI/UIxContactEditor.wox
UI/Templates/ContactsUI/UIxContactsUserFolders.wox
UI/Templates/MailerUI/UIxMailEditor.wox
UI/Templates/MailerUI/UIxMailMainFrame.wox
UI/Templates/MailerUI/UIxMailToSelection.wox
UI/Templates/MailerUI/UIxMailTree.wox [deleted file]
UI/Templates/MailerUI/UIxMailTreeBlockJS.wox [deleted file]
UI/Templates/SOGoFolderEnglishAdditionAdvisory.wox [new file with mode: 0644]
UI/Templates/SOGoFolderEnglishRemovalAdvisory.wox [new file with mode: 0644]
UI/Templates/SOGoFolderFrenchAdditionAdvisory.wox [new file with mode: 0644]
UI/Templates/SOGoFolderFrenchRemovalAdvisory.wox [new file with mode: 0644]
UI/Templates/SOGoFolderGermanAdditionAdvisory.wox [new file with mode: 0644]
UI/Templates/SOGoFolderGermanRemovalAdvisory.wox [new file with mode: 0644]
UI/Templates/SchedulerUI/UIxAttendeesEditor.wox
UI/Templates/SchedulerUI/UIxCalMainView.wox
UI/Templates/SchedulerUI/UIxComponentEditor.wox
UI/Templates/UIxAclEditor.wox
UI/Templates/UIxPageFrame.wox
UI/WebServerResources/ContactsUI.js
UI/WebServerResources/HTMLElement.js
UI/WebServerResources/JavascriptAPIExtensions.js
UI/WebServerResources/MailerUI+dTree.js
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxContactEditor.js
UI/WebServerResources/UIxMailEditor.css
UI/WebServerResources/UIxMailEditor.js
UI/WebServerResources/generic.css
UI/WebServerResources/generic.js
UI/WebServerResources/tablekit.js
UI/WebServerResources/tbtv_sent_17x17.gif

index 1c09f87eb08667f5af11f5b84caceb16344bfe8b..82b6367516926d7f06add76ef8379d4a14a3b9cc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,80 @@
+2007-10-18  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * SoObjects/SOGo/SOGoWebAuthenticator.m ([SOGoWebAuthenticator
+       -userInContext:]): override the super method by returning
+       anonymous if the super returns nil.
+
+       * UI/Common/UIxPageFrame.m ([UIxPageFrame
+       -productLocalizableStrings]): new method that returns the
+       product-specific translation dictionary as a JSON hash.
+       ([UIxPageFrame -commonLocalizableStrings]): same as above but for
+       the "Common" framework.
+       ([UIxPageFrame -setJsFiles:newJSFiles]): new setter that enables
+       the requestor components to require additional Javascript files.
+       This is useful now that all the scripts are loaded at the end of
+       the HTML code.
+       ([UIxPageFrame -additionalJSFiles]): new getter related to the
+       above.
+
+2007-10-17  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * UI/MailerUI/UIxMailActions.m ([UIxMailActions -copyAction]):
+       implemented new web method.
+
+       * SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject
+       -copyToFolderNamed:folderNameinContext:]): new method with the
+       code cut/pasted from -moveToFolderNamed:inContext:.
+       ([SOGoMailObject -moveToFolderNamed:folderNameinContext:]):
+       modified to use the code from -copyToFolderNamed:inContext:, which
+       is common between the two actions.
+
+       * SoObjects/Mailer/SOGoMailAccount.m ([SOGoMailAccount -draftsFolderNameInContext:_ctx])
+       ([SOGoMailAccount -sentFolderNameInContext:])
+       ([SOGoMailAccount -trashFolderNameInContext:]): modified to take
+       the user settings into account.
+
+       * UI/MailerUI/UIxMailFolderActions.m ([UIxMailFolderActions -setAsDraftsFolderAction])
+       ([UIxMailFolderActions -setAsSentFolderAction])
+       ([UIxMailFolderActions -setAsTrashFolderAction]): new web methods
+       that change the purpose of the active folder to "Sent", "Drafts"
+       or "Trash".
+
+       * UI/SOGoUI/SOGoACLAdvisory.m ([SOGoACLAdvisory -subject]): 
+       returns the subject as quoted-printable.
+
+       * UI/SOGoUI/SOGoACLAdvisory.[hm]: added two intermediary classes:
+       SOGoACLAdditionAdvisory and SOGoACLRemovalAdvisory implementing
+       the "aclMethod" method for the subsequent language-dependent
+       subclasses.
+
+       * UI/SOGoUI/SOGoFolderAdvisory.m ([SOGoFolderAdvisory -subject]):
+       returns the subject as quoted-printable.
+
+       * UI/Scheduler/UIxAppointmentEditor.m ([UIxAppointmentEditor
+       -dealloc]): release item, aptStartDate and aptEndDate.
+
+2007-10-16  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder
+       -initWithName:newNameinContainer:newContainer]): the owner of a
+       shared folder is set to "nobody" by default.
+
+       * UI/Common/UIxAclEditor.m ([UIxAclEditor -hasOwner]): new method
+       that returns whether the object has an owner or not.
+
+2007-10-15  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * SoObjects/SOGo/SOGoFolder.m ([SOGoFolder -ocsFolder]): create
+       the folder even if the current user is not its owner.
+
+2007-10-10  Ludovic Marcotte  <ludovic@inverse.ca>
+
+       * We now send advisory emails when folders
+         are created / deleted.
+
+       * Fixed the sending of advisory emails upon
+         ACL changes on folders.
+
 2007-10-10  Ludovic Marcotte  <ludovic@inverse.ca>
 
        * UI/Scheduler/UIxComponentEditor.m
diff --git a/NEWS b/NEWS
index a61bbec9c1e78b7c3c23c93b2d56b329dab354fe..95e1bff6376861934d7ce8feafa077a18f053b3d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+0.9.0-200710XX
+--------------
+- the user can now configure his folders as drafts, trash or sent folder;
+- added the ability the move and copy message across mail folders;
+
 0.9.0-200709XX
 --------------
 - implemented cookie-based identification in the web interface;
index 1a58723ddb2417c42d9667f8fd4138719e39994f..26e0e974cd12c59a63deb8ba5850e6f047f4d78d 100644 (file)
 #import <NGObjWeb/NSException+HTTP.h>
 #import <NGObjWeb/SoObject+SoDAV.h>
 #import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WOResponse.h>
 #import <NGObjWeb/WORequest.h>
 #import <NGExtensions/NSObject+Logs.h>
+#import <NGExtensions/NSString+misc.h>
 #import <EOControl/EOQualifier.h>
 #import <EOControl/EOSortOrdering.h>
 #import <GDLContentStore/GCSFolder.h>
 
   if (filter && [filter length] > 0)
     {
-#warning why we do not use %%%@%% everywhere?
       qs = [NSString stringWithFormat:
                        @"(c_sn isCaseInsensitiveLike: '%@%%') OR "
                      @"(c_givenname isCaseInsensitiveLike: '%@%%') OR "
   NSMutableDictionary *filterData;
   id <DOMNode> parentNode;
   id <DOMNodeList> ranges;
-  NSString *componentName;
 
   parentNode = [filterElement parentNode];
 
   return filters;
 }
 
-- (void) _appendComponentsMatchingFilters: (NSArray *) filters
-                               toResponse: (WOResponse *) response
-{
-  unsigned int count, max;
-  NSDictionary *currentFilter, *contact;
-  NSEnumerator *contacts;
-  NSString *baseURL;
-
-  baseURL = [self baseURLInContext: context];
-
-  max = [filters count];
-  for (count = 0; count < max; count++)
-    {
-      currentFilter = [filters objectAtIndex: count];
-      contacts = [[self lookupContactsWithFilter: [[currentFilter allValues] lastObject]
-                       sortBy: @"c_givenname"
-                       ordering: NSOrderedDescending]
-                  objectEnumerator];
-      
-      while ((contact = [contacts nextObject]))
-      {
-          [self appendObject: contact
-                withBaseURL: baseURL
-                toREPORTResponse: response];
-        }
-    }
-}
-
 - (void) appendObject: (NSDictionary *) object
           withBaseURL: (NSString *) baseURL
      toREPORTResponse: (WOResponse *) r
   [r appendContentString: @"  </D:response>\r\n"];
 }
 
+- (void) _appendComponentsMatchingFilters: (NSArray *) filters
+                               toResponse: (WOResponse *) response
+{
+  unsigned int count, max;
+  NSDictionary *currentFilter, *contact;
+  NSEnumerator *contacts;
+  NSString *baseURL;
+
+  baseURL = [self baseURLInContext: context];
+
+  max = [filters count];
+  for (count = 0; count < max; count++)
+    {
+      currentFilter = [filters objectAtIndex: count];
+      contacts = [[self lookupContactsWithFilter: [[currentFilter allValues] lastObject]
+                       sortBy: @"c_givenname"
+                       ordering: NSOrderedDescending]
+                  objectEnumerator];
+      
+      while ((contact = [contacts nextObject]))
+      {
+          [self appendObject: contact
+                withBaseURL: baseURL
+                toREPORTResponse: response];
+        }
+    }
+}
+
 - (NSArray *) lookupContactsWithFilter: (NSString *) filter
                                 sortBy: (NSString *) sortKey
                               ordering: (NSComparisonResult) sortOrdering
index bf2a7a5878b2f4668b83c0315bbea1a34e4e74ba..eea913c2bf5ec8513719ee7ee8edefde8768ea99 100644 (file)
@@ -32,6 +32,8 @@
 #import <NGExtensions/NSNull+misc.h>
 #import <NGImap4/NGImap4Connection.h>
 
+#import <SoObjects/SOGo/SOGoUser.h>
+
 #import "SOGoMailFolder.h"
 #import "SOGoMailManager.h"
 #import "SOGoDraftsFolder.h"
@@ -333,10 +335,32 @@ static BOOL useAltNamespace = NO;
   return [NSString stringWithFormat: @"folder%@", inboxFolderName];
 }
 
+- (NSString *) _userFolderNameWithPurpose: (NSString *) purpose
+{
+  NSUserDefaults *ud;
+  NSMutableDictionary *mailSettings;
+  NSString *folderName;
+
+  folderName = nil;
+  ud = [[context activeUser] userSettings];
+  mailSettings = [ud objectForKey: @"Mail"];
+  if (mailSettings)
+    folderName
+      = [mailSettings objectForKey: [NSString stringWithFormat: @"%@Folder",
+                                             purpose]];
+
+  return folderName;
+}
+
 - (NSString *) draftsFolderNameInContext: (id) _ctx
 {
-  /* SOGo managed folder */
-  return [NSString stringWithFormat: @"folder%@", draftsFolderName];
+  NSString *folderName;
+
+  folderName = [self _userFolderNameWithPurpose: @"Drafts"];
+  if (!folderName)
+    folderName = draftsFolderName;
+
+  return [NSString stringWithFormat: @"folder%@", folderName];
 }
 
 - (NSString *) sieveFolderNameInContext: (id) _ctx
@@ -346,12 +370,24 @@ static BOOL useAltNamespace = NO;
 
 - (NSString *) sentFolderNameInContext: (id)_ctx
 {
-  return [NSString stringWithFormat: @"folder%@", sentFolderName];
+  NSString *folderName;
+
+  folderName = [self _userFolderNameWithPurpose: @"Sent"];
+  if (!folderName)
+    folderName = sentFolderName;
+
+  return [NSString stringWithFormat: @"folder%@", folderName];
 }
 
 - (NSString *) trashFolderNameInContext: (id)_ctx
 {
-  return [NSString stringWithFormat: @"folder%@", trashFolderName];
+  NSString *folderName;
+
+  folderName = [self _userFolderNameWithPurpose: @"Trash"];
+  if (!folderName)
+    folderName = trashFolderName;
+
+  return [NSString stringWithFormat: @"folder%@", folderName];
 }
 
 - (SOGoMailFolder *) inboxFolderInContext: (id) _ctx
index babd5684fff939f5d3e461d212874272485651c1..892a663c134999ebd87bfdd7be052e3244f49752 100644 (file)
@@ -63,7 +63,7 @@ static BOOL useAltNamespace = NO;
 
   folder = [mailAccount sharedFolderName];
   if (folder && [path hasPrefix: folder])
-    [self setOwner: @"anyone"];
+    [self setOwner: @"nobody"];
   else
     {
       folder = [mailAccount otherUsersFolderName];
@@ -73,7 +73,7 @@ static BOOL useAltNamespace = NO;
          if ([names count] > 1)
            [self setOwner: [names objectAtIndex: 1]];
          else
-           [self setOwner: @"anyone"];
+           [self setOwner: @"nobody"];
        }
     }
 }
@@ -585,7 +585,7 @@ static BOOL useAltNamespace = NO;
   return userPath;
 }
 
-- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid;
+- (NSString *) httpURLForAdvisoryToUser: (NSString *) uid
 {
   SOGoUser *user;
   NSString *otherUsersPath, *url;
@@ -609,7 +609,7 @@ static BOOL useAltNamespace = NO;
   return url;
 }
 
-- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid;
+- (NSString *) resourceURLForAdvisoryToUser: (NSString *) uid
 {
   NSURL *selfURL, *userURL;
 
index c81e4103db2c4d36ba0f7fea176674160f916c2b..80e0953805ef0ce6c57526081e6a4f4313a4883f 100644 (file)
@@ -89,6 +89,8 @@
 
 - (BOOL)isDeletionAllowed;
 - (NSException *) trashInContext:(id)_ctx;
+- (NSException *) copyToFolderNamed: (NSString *) folderName
+                          inContext: (id)_ctx;
 - (NSException *) moveToFolderNamed: (NSString *) folderName
                           inContext: (id)_ctx;
 
index c0b4929ff75a4fc88a7aff4a906a1f125bdb156b..cd2e8d481fa7954ef537815a1751d7468c2f658a 100644 (file)
@@ -888,13 +888,12 @@ static BOOL debugSoParts       = NO;
   return nil;
 }
 
-- (NSException *) moveToFolderNamed: (NSString *) folderName
+- (NSException *) copyToFolderNamed: (NSString *) folderName
                           inContext: (id)_ctx
 {
   SOGoMailAccounts *destFolder;
   NSEnumerator *folders;
   NSString *currentFolderName, *reason;
-  NSException    *error;
 
   // TODO: check for safe HTTP method
 
@@ -924,18 +923,27 @@ static BOOL debugSoParts       = NO;
   [destFolder flushMailCaches];
 
   /* a) copy */
-  
-  error = [self davCopyToTargetObject: destFolder
-               newName: @"fakeNewUnusedByIMAP4" /* autoassigned */
-               inContext:_ctx];
-  if (error != nil) return error;
 
-  /* b) mark deleted */
+  return [self davCopyToTargetObject: destFolder
+              newName: @"fakeNewUnusedByIMAP4" /* autoassigned */
+              inContext:_ctx];
+}
+
+- (NSException *) moveToFolderNamed: (NSString *) folderName
+                          inContext: (id)_ctx
+{
+  NSException    *error;
+
+  if (![self copyToFolderNamed: folderName
+            inContext: _ctx])
+    {
+      /* b) mark deleted */
   
-  error = [[self imap4Connection] markURLDeleted: [self imap4URL]];
-  if (error != nil) return error;
+      error = [[self imap4Connection] markURLDeleted: [self imap4URL]];
+      if (error != nil) return error;
 
-  [self flushMailCaches];
+      [self flushMailCaches];
+    }
   
   return nil;
 }
index db7256ef73effa32cc270763eb47f8c5c748306f..28697447645fb8a41052af2ee7d8d32db3742034 100644 (file)
@@ -31,6 +31,7 @@
 #import <NGObjWeb/SoObject+SoDAV.h>
 #import <NGObjWeb/SoSelectorInvocation.h>
 #import <NGObjWeb/WOContext+SoObjects.h>
+#import <NGObjWeb/WOApplication.h>
 #import <NGExtensions/NSNull+misc.h>
 #import <NGExtensions/NSObject+Logs.h>
 #import <EOControl/EOQualifier.h>
@@ -41,6 +42,7 @@
 #import <GDLContentStore/GCSFolderType.h>
 #import <GDLContentStore/NSURL+GCS.h>
 #import <SaxObjC/XMLNamespaces.h>
+#import <UI/SOGoUI/SOGoFolderAdvisory.h>
 
 #import "NSArray+Utilities.h"
 #import "NSString+Utilities.h"
@@ -235,7 +237,7 @@ static NSString *defaultUserID = @"<default>";
       ocsFolder = [self ocsFolderForPath: [self ocsPath]];
       userLogin = [[context activeUser] login];
       if (!ocsFolder
-         && [userLogin isEqualToString: [self ownerInContext: context]]
+/*       && [userLogin isEqualToString: [self ownerInContext: context]] */
          && [self folderIsMandatory]
          && [self create])
        ocsFolder = [self ocsFolderForPath: [self ocsPath]];
@@ -255,15 +257,35 @@ static NSString *defaultUserID = @"<default>";
   return @"";
 }
 
+- (void) sendFolderAdvisoryTemplate: (NSString *) template
+{
+  NSString *language, *pageName;
+  SOGoUser *user;
+  SOGoFolderAdvisory *page;
+  WOApplication *app;
+
+  user = [SOGoUser userWithLogin: [[context activeUser] login] roles: nil];
+  language = [user language];
+  pageName = [NSString stringWithFormat: @"SOGoFolder%@%@Advisory",
+                      language, template];
+
+  app = [WOApplication application];
+  page = [app pageWithName: pageName inContext: context];
+  [page setFolderObject: self];
+  [page setRecipientUID: [[context activeUser] login]];
+  [page send];
+}
+
 - (BOOL) create
 {
   NSException *result;
 
-//   [self dieHard];
   result = [[self folderManager] createFolderOfType: [self folderType]
                                 withName: displayName
                                  atPath: ocsPath];
 
+  if (!result) [self sendFolderAdvisoryTemplate: @"Addition"];
+
   return (result == nil);
 }
 
@@ -271,12 +293,17 @@ static NSString *defaultUserID = @"<default>";
 {
   NSException *error;
 
+  // We just fetch our displayName since our table will use it!
+  [self displayName];
+  
   if ([nameInContainer isEqualToString: @"personal"])
     error = [NSException exceptionWithHTTPStatus: 403
                         reason: @"folder 'personal' cannot be deleted"];
   else
     error = [[self folderManager] deleteFolderAtPath: ocsPath];
 
+  if (!error) [self sendFolderAdvisoryTemplate: @"Removal"];
+
   return error;
 }
 
index 335e371ec01cc42c016f2a4f5fed5a59abe6f7e0..97711c5630f2964490868da915e9ef716d2d7fe7 100644 (file)
@@ -48,6 +48,12 @@ extern NSString *SOGoWeekStartJanuary1;
 extern NSString *SOGoWeekStartFirst4DayWeek;
 extern NSString *SOGoWeekStartFirstFullWeek;
 
+@interface SoUser (SOGoExtension)
+
+- (NSString *) language;
+
+@end
+
 @interface SOGoUser : SoUser
 {
   NSString *currentPassword;
index 0197ca9399eb66691049ddddfd9836124c09e3db..9cd0c562b54ab02f7ae469c11b45e9cd500e5925 100644 (file)
@@ -57,6 +57,27 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
 
 @end
 
+@implementation SoUser (SOGoExtension)
+
+- (NSString *) language
+{
+  NSArray *bLanguages;
+  WOContext *context;
+  NSString *language;
+
+  context = [[WOApplication application] context];
+  bLanguages = [[context request] browserLanguages];
+  if ([bLanguages count] > 0)
+    language = [bLanguages objectAtIndex: 0];
+
+  if (![language length])
+    language = defaultLanguage;
+
+  return language;
+}
+
+@end
+
 @implementation SOGoUser
 
 + (void) initialize
index 36a962826ec49efe664d327817b61695c556fd88..68c80259263efa4ab703b015d804026b516a48af 100644 (file)
 //           && [_pwd isEqualToString: @"freebusy"]));
 }
 
+- (SOGoUser *) userInContext: (WOContext *)_ctx
+{
+  static SOGoUser *anonymous = nil;
+  SOGoUser *user;
+
+  if (!anonymous)
+    anonymous
+      = [[SOGoUser alloc] initWithLogin: @"anonymous"
+                         roles: [NSArray arrayWithObject: SoRole_Anonymous]];
+
+  user = (SOGoUser *) [super userInContext: _ctx];
+  if (!user)
+    user = anonymous;
+
+  return user;
+}
+
 - (NSString *) passwordInContext: (WOContext *) context
 {
   NSArray *creds;
index dec4c8c7e2b35c5e87525af6211eb7c90cf7b4bf..1ae7857ef3857007c8a73bb4061e19d9515b4c2c 100644 (file)
@@ -44,3 +44,5 @@
 "Unable to rename that folder!" = "Unable to rename that folder!";
 "You have already subscribed to that folder!"
        = "You have already subscribed to that folder!";
+"The user rights cannot be edited for this object!"
+        = "The user rights cannot be edited for this object!";
index 5e1e6ce0cb566c830c8dc909e87beec545a6e828..2de07b5c9be2c93fbdddbe0e92abfe502b84f3fe 100644 (file)
@@ -41,3 +41,5 @@
 "Unable to rename that folder!" = "Impossible de renommer ce dossier.";
 "You have already subscribed to that folder!"
        = "Vous êtes déja abonné à ce dossier.";
+"The user rights cannot be edited for this object!"
+        = "Les droits sur cet objet ne peuvent pas être édités.";
index a7feaba384c35be2c6933133958b959bc6c7f706..68e8862e3ff3e9013df6d1ade813098e3dabf15c 100644 (file)
@@ -38,3 +38,5 @@
 "Unable to rename that folder!" = "Unable to rename that folder!";
 "You have already subscribed to that folder!"
        = "You have already subscribed to that folder!";
+"The user rights cannot be edited for this object!"
+        = "The user rights cannot be edited for this object!";
index f8104ceedb56dcaacd1e7564b8c83cfeca35e196..41f418e7c9430b3b50fde84b08ce5a684450e1a9 100644 (file)
@@ -43,6 +43,7 @@
 - (NSString *) currentUser;
 
 - (NSString *) ownerName;
+- (BOOL) hasOwner;
 
 @end
 
index e6dccdfe06225e0c3ba883ad5d68560925cdb1e2..123bd49610b24e07a4ff875e840e69602dfbdfd2 100644 (file)
   return [self _displayNameForUID: ownerLogin];
 }
 
+- (BOOL) hasOwner
+{
+  NSString *ownerLogin;
+
+  ownerLogin = [[self clientObject] ownerInContext: context];
+
+  return (![ownerLogin isEqualToString: @"nobody"]);
+}
+
 - (NSString *) defaultUserID
 {
   if (!defaultUserID)
index 1faf08cbb66ef69ba86551d6d795c6bd197fee0d..1c3094a56d81d4403475d8bccc8dd17e47581574 100644 (file)
@@ -25,6 +25,9 @@
 #import <SOGoUI/UIxComponent.h>
 #import <NGObjWeb/WEClientCapabilities.h>
 
+@class NSString;
+@class NSMutableArray;
+
 @interface WOComponent (PopupExtension)
 
 - (BOOL) isPopup;
   NSString *toolbar;
   id item;
   BOOL isPopup;
+  NSMutableArray *additionalJSFiles;
 }
 
+- (NSString *) commonLocalizableStrings;
+- (NSString *) productLocalizableStrings;
+
 - (NSString *) pageJavaScriptURL;
 - (NSString *) productJavaScriptURL;
 - (BOOL) hasPageSpecificJavaScript;
 - (BOOL) hasProductSpecificJavaScript;
 
+- (NSArray *) additionalJSFiles;
+
 - (NSString *) pageCSSURL;
 - (NSString *) productCSSURL;
 - (BOOL) hasPageSpecificCSS;
 - (BOOL) hasProductSpecificCSS;
 
-- (NSString *) productFrameworkName;
-
 - (void) setPopup: (BOOL) popup;
 - (BOOL) isPopup;
 
index a5e6989a83c7e362a86af1c612e2cc2fff9f0203..447572b0b6c0f971d7ba01d6de684053af29ad56 100644 (file)
 #import <NGObjWeb/SoComponent.h>
 #import <NGObjWeb/WOComponent.h>
 
+#import <SoObjects/SOGo/SOGoUser.h>
+#import <SoObjects/SOGo/NSDictionary+Utilities.h>
+
 #import <SOGoUI/UIxComponent.h>
-#import <SOGo/SOGoUser.h>
 
 #import <Main/build.h>
 
 {
   if ((self = [super init]))
     {
+      item = nil;
+      title = nil;
       toolbar = nil;
+      additionalJSFiles = nil;
     }
 
   return self;
@@ -46,8 +51,8 @@
 {
   [item release];
   [title release];
-  if (toolbar)
-    [toolbar release];
+  [toolbar release];
+  [additionalJSFiles release];
   [super dealloc];
 }
 
@@ -55,7 +60,7 @@
 
 - (void) setTitle: (NSString *) _value
 {
-  ASSIGNCOPY(title, _value);
+  ASSIGN (title, _value);
 }
 
 - (NSString *) title
@@ -68,7 +73,7 @@
 
 - (void) setItem: (id) _item
 {
-  ASSIGN(item, _item);
+  ASSIGN (item, _item);
 }
 
 - (id) item
 
 /* page based JavaScript */
 
+- (NSString *) _stringsForFramework: (NSString *) framework
+{
+  NSString *language, *frameworkName;
+  id table;
+
+  frameworkName = [NSString stringWithFormat: @"%@.SOGo",
+                           (framework ? framework : [self frameworkName])];
+  language = [[context activeUser] language];
+  table
+    = [[self resourceManager] stringTableWithName: @"Localizable"
+                             inFramework: frameworkName
+                             languages: [NSArray arrayWithObject: language]];
+
+  /* table is not really an NSDictionary but a hackish variation thereof */
+  return [[NSDictionary dictionaryWithDictionary: table] jsonRepresentation];
+}
+
+- (NSString *) commonLocalizableStrings
+{
+  return [NSString stringWithFormat: @"var clabels = %@;",
+                  [self _stringsForFramework: nil]];
+}
+
+- (NSString *) productLocalizableStrings
+{
+  NSString *frameworkName;
+
+  frameworkName = [[context page] frameworkName];
+
+  return [NSString stringWithFormat: @"var labels = %@;",
+                  [self _stringsForFramework: frameworkName]];
+}
+
 - (NSString *) pageJavaScriptURL
 {
   WOComponent *page;
   return [self urlForResourceFilename: fwJSFilename];
 }
 
-- (NSString *) productFrameworkName
-{
-  WOComponent *page;
-
-  page = [context page];
-
-  return [NSString stringWithFormat: @"%@.SOGo", [page frameworkName]];
-}
-
 - (BOOL) hasPageSpecificJavaScript
 {
   return ([[self pageJavaScriptURL] length] > 0);
   return ([[self productJavaScriptURL] length] > 0);
 }
 
+- (void) setJsFiles: (NSString *) newJSFiles
+{
+  NSEnumerator *jsFiles;
+  NSString *currentFile, *filename;
+
+  [additionalJSFiles release];
+  additionalJSFiles = [NSMutableArray new];
+
+  jsFiles = [[newJSFiles componentsSeparatedByString: @","] objectEnumerator];
+  while ((currentFile = [jsFiles nextObject]))
+    {
+      filename = [self urlForResourceFilename:
+                        [currentFile stringByTrimmingSpaces]];
+      [additionalJSFiles addObject: filename];
+    }
+}
+
+- (NSArray *) additionalJSFiles
+{
+  return additionalJSFiles;
+}
+
 - (NSString *) pageCSSURL
 {
   WOComponent *page;
   NSLog(@"User agent = %@", [cc userAgent]);
   //NSLog(@"Browser major version = %i", [cc majorVersion]);
 
-  return ( 
-         ([[cc userAgentType] isEqualToString: @"IE"] && [cc majorVersion] >= 7) ||
-         ([[cc userAgentType] isEqualToString: @"Mozilla"] && [cc majorVersion] >= 5) ||
-         ([[cc userAgentType] isEqualToString: @"Safari"] && [cc majorVersion] >= 4)
+  return (([[cc userAgentType] isEqualToString: @"IE"]
+          && [cc majorVersion] >= 7)
+         || ([[cc userAgentType] isEqualToString: @"Mozilla"]
+             && [cc majorVersion] >= 5)
+         || ([[cc userAgentType] isEqualToString: @"Safari"]
+             && [cc majorVersion] >= 4)
          //      ([[cc userAgentType] isEqualToString: @"Konqueror"])
           );
 }
   return [cc isMacBrowser];
 }
 
-
 @end /* UIxPageFrame */
index b8d4e8b9a444edeffc964c425018d51e5442d8a9..cf14bf58d65518fef06c0d7d5e1341158621b99b 100644 (file)
 
 "MoveTo"        = "Move &hellip;";
 
-"error_missingsubject"      = "Missing Subject";
-"error_missingrecipients"   = "Missing Recipients";
-"error_validationfailed"    = "Validation failed";
+/* Address Popup menu */
+"Add to Address Book..." = "Add to Address Book...";
+"Compose Mail To" = "Compose Mail To";
+"Create Filter From Message..." = "Create Filter From Message...";
+
+/* Mailbox popup menus */
+"Open in New Mail Window" = "Open in New Mail Window";
+"Copy Folder Location" = "Copy Folder Location";
+"Subscribe..." = "Subscribe...";
+"Mark Folder Read..." = "Mark Folder Read...";
+"New Folder..." = "New Folder...";
+"Compact This Folder" = "Compact This Folder";
+"Search Messages..." = "Search Messages...";
+"Sharing..." = "Sharing...";
+"New Subfolder..." = "New Subfolder...";
+"Rename Folder..." = "Rename Folder...";
+"Delete Folder" = "Delete Folder";
+"Use This Folder For" = "Use This Folder For";
+"Get Messages for Account" = "Get Messages for Account";
+
+/* Use This Folder menu */
+"Sent Messages" = "Sent Messages";
+"Drafts" = "Drafts";
+"Deleted Messages" = "Deleted Messages";
+
+/* Message list popup menu */
+"Open Message In New Window" = "Open Message In New Window";
+"Reply to Sender Only" = "Reply to Sender Only";
+"Reply to All" = "Reply to All";
+"Forward" = "Forward";
+"Edit As New..." = "Edit As New...";
+"Move To" = "Move To";
+"Copy To" = "Copy To";
+"Label" = "Label";
+"Mark" = "Mark";
+"Save As..." = "Save As...";
+"Print Preview" = "Print Preview";
+"View Message Source" = "View Message Source";
+"Print..." = "Print...";
+"Delete Message" = "Delete Message";
+
+"This Folder" = "This Folder";
+
+/* Label popup menu */
+"None" = "None";
+"Important" = "Important";
+"Work" = "Work";
+"Personal" = "Personal";
+"To Do" = "To Do";
+"Later" = "Later";
+
+/* Mark popup menu */
+"As Read" = "As Read";
+"Thread As Read" = "Thread As Read";
+"As Read By Date..." = "As Read By Date...";
+"All Read" = "All Read";
+"Flag" = "Flag";
+"As Junk" = "As Junk";
+"As Not Junk" = "As Not Junk";
+"Run Junk Mail Controls" = "Run Junk Mail Controls";
 
 /* Folder operations */
 "Name :" = "Name :";
 "Please select a message." = "Please select a message.";
 "Please select a message to print." = "Please select a message to print.";
 "Please select only one message to print." = "Please select only one message to print.";
+
+"You need to choose a non-virtual folder!" = "You need to choose a non-virtual folder!";
+"You need to choose a root subfolder!" = "You need to choose a root subfolder!";
+
+"Moving a message into its own folder is impossible!"
+= "Moving a message into its own folder is impossible!";
+"Copying a message into its own folder is impossible!"
+= "Copying a message into its own folder is impossible!";
index 3fed423bceea5bb1a050aeb16022833d717d3446..b68ab6bb3e641b18269b59863b15a761291acb63 100644 (file)
 "New Subfolder..." = "Nouveau sous-dossier...";
 "Rename Folder..." = "Renommer le dossier...";
 "Delete Folder" = "Supprimer le dossier...";
+"Use This Folder For" = "Utiliser ce dossier pour";
 "Get Messages for Account" = "Relever les messages de ce compte";
 
+/* Use This Folder menu */
+"Sent Messages" = "les message envoyés";
+"Drafts" = "les brouillons";
+"Deleted Messages" = "les message effacés";
+
 /* Message list popup menu */
 "Open Message In New Window" = "Ouvrir dans une nouvelle fenétre";
 "Reply to Sender Only" = "Répondre à l'expéditeur";
 "Print..." = "Imprimer...";
 "Delete Message" = "Supprimer le message";
 
+"This Folder" = "Ce dossier-ci";
+
 /* Label popup menu */
 "None" = "Aucune";
 "Important" = "Important";
 "Please select a message." = "Veuillez sélectionner un message.";
 "Please select a message to print." = "Veuillez sélectionner un message à imprimer.";
 "Please select only one message to print." = "Veuillez ne sélectionner qu'un seul message à imprimer.";
+
+"You need to choose a non-virtual folder!" = "Vous devez choisir un dossier non-virtuel.";
+"You need to choose a root subfolder!" = "Vous devez choisir un sous-dossier de la racine.";
+
+"Moving a message into its own folder is impossible!"
+= "Le déplacement d'un message dans son propre dossier est impossible.";
+"Copying a message into its own folder is impossible!"
+= "La copie d'un message dans son propre dossier est impossible.";
index ad9ec372c5dee79e9cf1cc5afe547203bcd5f33a..31b91fe6bb2c46bbfb872b911ee641f2227405fe 100644 (file)
 "New Subfolder..." = "Neuer Unterordner...";
 "Rename Folder..." = "Umbenennen...";
 "Delete Folder" = "Löschen";
+"Use This Folder For" = "Use This Folder For";
 "Get Messages for Account" = "Neue Nachrichten empfangen";
 
+/* Use This Folder menu */
+"Sent Messages" = "Sent Messages";
+"Drafts" = "Drafts";
+"Deleted Messages" = "Deleted Messages";
+
 /* Message list popup menu */
 "Open Message In New Window" = "In neuem Fenster õffnen";
 "Reply to Sender Only" = "Antworten nur an Absender";
 "Print..." = "Drucken...";
 "Delete Message" = "Lõschen";
 
+"This Folder" = "This Folder";
+
 /* Label popup menu */
 "None" = "Aucune";
 "Important" = "Important";
 "Please select a message." = "Sie müssen eine Nachricht auswählen.";
 "Please select a message to print." = "Sie müssen eine Nachricht zum Drucken auswählen.";
 "Please select only one message to print." = "Bitte wählen Sie nur eine Nachricht zum Drucken aus.";
+
+"You need to choose a non-virtual folder!" = "You need to choose a non-virtual folder!";
+"You need to choose a root subfolder!" = "You need to choose a root subfolder!";
+
+"Moving a message into its own folder is impossible!"
+= "Moving a message into its own folder is impossible!";
+"Copying a message into its own folder is impossible!"
+= "Copying a message into its own folder is impossible!";
index 3ebf019c01e9406325c92169cf278a50330b8f52..7b6d9e13e9dd1bff90cab7878777d000d5fab98e 100644 (file)
   NSString *destinationFolder;
   id response;
 
-  destinationFolder = [[context request] formValueForKey: @"tofolder"];
+  destinationFolder = [[context request] formValueForKey: @"folder"];
   if ([destinationFolder length] > 0)
     {
       response = [[self clientObject] moveToFolderNamed: destinationFolder
   return response;
 }
 
+- (id) copyAction
+{
+  NSString *destinationFolder;
+  id response;
+
+  destinationFolder = [[context request] formValueForKey: @"folder"];
+  if ([destinationFolder length] > 0)
+    {
+      response = [[self clientObject] copyToFolderNamed: destinationFolder
+                                     inContext: context];
+      if (!response)
+       response = [self responseWith204];
+    }
+  else
+    response = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
+                           reason: @"No destination folder given"];
+
+  return response;
+}
+
 /* active message */
 
 - (id) markMessageUnreadAction 
index 84d7fa95e5981399d532b5cb34e3b85bd63c5710..ade6ed8d22debadc13b93e18caf0c8ad225fb521 100644 (file)
 #import <Foundation/NSDictionary.h>
 #import <Foundation/NSEnumerator.h>
 #import <Foundation/NSURL.h>
+#import <Foundation/NSUserDefaults.h>
 
 #import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
 #import <NGObjWeb/WOResponse.h>
 #import <NGObjWeb/WORequest.h>
 #import <NGImap4/NGImap4Connection.h>
@@ -35,6 +37,7 @@
 #import <SoObjects/Mailer/SOGoTrashFolder.h>
 #import <SoObjects/Mailer/SOGoMailAccount.h>
 #import <SoObjects/SOGo/NSObject+Utilities.h>
+#import <SoObjects/SOGo/SOGoUser.h>
 
 #import <UI/Common/WODirectAction+SOGo.h>
 
   return response;
 }
 
+- (WOResponse *) _setFolderPurpose: (NSString *) purpose
+{
+  SOGoMailFolder *co;
+  WOResponse *response;
+  NSUserDefaults *ud;
+  NSMutableDictionary *mailSettings;
+
+  co = [self clientObject];
+  if ([NSStringFromClass ([co class]) isEqualToString: @"SOGoMailFolder"])
+    {
+      ud = [[context activeUser] userSettings];
+      mailSettings = [ud objectForKey: @"Mail"];
+      if (!mailSettings)
+       {
+         mailSettings = [NSMutableDictionary new];
+         [mailSettings autorelease];
+       }
+      [ud setObject: mailSettings forKey: @"Mail"];
+      [mailSettings setObject: [co relativeImap4Name]
+                   forKey: [NSString stringWithFormat: @"%@Folder",
+                                     purpose]];
+      [ud synchronize];
+      response = [self responseWith204];
+    }
+  else
+    {
+      response = [self responseWithStatus: 500];
+      [response
+       appendContentString: @"Unable to change the purpose of this folder."];
+    }
+
+  return response;
+}
+
+- (WOResponse *) setAsDraftsFolderAction
+{
+  return [self _setFolderPurpose: @"Drafts"];
+}
+
+- (WOResponse *) setAsSentFolderAction
+{
+  return [self _setFolderPurpose: @"Sent"];
+}
+
+- (WOResponse *) setAsTrashFolderAction
+{
+  return [self _setFolderPurpose: @"Trash"];
+}
+
 - (WOResponse *) expungeAction 
 {
   NSException *error;
index 73ed08099722bff43437e7af3aad936a5f69981c..083bbe4030a5b850c993175e7482008b76e70381 100644 (file)
          actionClass = "UIxMailFolderActions";
          actionName = "deleteFolder";
        };
+       setAsDraftsFolder = {
+         protectedBy = "View";
+         actionClass = "UIxMailFolderActions";
+         actionName = "setAsDraftsFolder";
+       };
+       setAsSentFolder = {
+         protectedBy = "View";
+         actionClass = "UIxMailFolderActions";
+         actionName = "setAsSentFolder";
+       };
+       setAsTrashFolder = {
+         protectedBy = "View";
+         actionClass = "UIxMailFolderActions";
+         actionName = "setAsTrashFolder";
+       };
        userRights = {
          protectedBy = "ReadAcls";
          pageName    = "UIxMailUserRightsEditor";
          actionClass = "UIxMailActions";
          actionName  = "move";
        };
+       copy = {
+         protectedBy = "View";
+         actionClass = "UIxMailActions";
+         actionName  = "copy";
+       };
        trash = {
          protectedBy = "View";
          actionClass = "UIxMailActions";
index 9749d13b5a3e4f9095ce10bc7e5141c2eae09e16..8b82c41010a6f7ef9a261cfdfad7fc50e4f79199 100644 (file)
@@ -89,7 +89,7 @@
   NSString *login, *oldLocation;
 
   login = [[context activeUser] login];
-  if ([login isEqualToString: @"anonymous"])
+  if (!login || [login isEqualToString: @"anonymous"])
     response = self;
   else
     {
index 034e2764c445e2ca9448233f589f690b8dee7896..4928232219dfd284c2a1a87fce993c4fc0fe7a99 100644 (file)
@@ -27,8 +27,8 @@ libSOGoUI_OBJC_FILES +=               \
        SOGoAptFormatter.m      \
        SOGoJSStringFormatter.m \
        WOContext+UIx.m         \
-       \
-       SOGoACLAdvisory.m
+       SOGoACLAdvisory.m \
+       SOGoFolderAdvisory.m
 
 # make
 
index c6ac9b664524ba1b91dc08f79e3e10cbeb33c035..762b89a1d05dd78719890505ddbf75f977f9b5d6 100644 (file)
 
 @end
 
-@interface SOGoACLEnglishAdditionAdvisory : SOGoACLAdvisory
+@interface SOGoACLAdditionAdvisory : SOGoACLAdvisory
+
+- (NSString *) aclMethod;
+
+@end
+
+@interface SOGoACLRemovalAdvisory : SOGoACLAdvisory
+
+- (NSString *) aclMethod;
+
+@end
+
+@interface SOGoACLEnglishAdditionAdvisory : SOGoACLAdditionAdvisory
 @end
 
-@interface SOGoACLEnglishRemovalAdvisory : SOGoACLAdvisory
+@interface SOGoACLFrenchAdditionAdvisory : SOGoACLAdditionAdvisory
 @end
 
-@interface SOGoACLFrenchAdditionAdvisory : SOGoACLAdvisory
+@interface SOGoACLGermanAdditionAdvisory : SOGoACLAdditionAdvisory
 @end
 
-@interface SOGoACLFrenchRemovalAdvisory : SOGoACLAdvisory
+@interface SOGoACLEnglishRemovalAdvisory : SOGoACLRemovalAdvisory
 @end
 
-@interface SOGoACLGermanAdditionAdvisory : SOGoACLAdvisory
+@interface SOGoACLFrenchRemovalAdvisory : SOGoACLRemovalAdvisory
 @end
 
-@interface SOGoACLGermanRemovalAdvisory : SOGoACLAdvisory
+@interface SOGoACLGermanRemovalAdvisory : SOGoACLRemovalAdvisory
 @end
 
 #endif /* SOGOACLADVISORY_H */
index 12c2be31b3514139237f688a72d2a591ceb95d50..b52ebcc193f60b97e151133b8256522a46dddec0 100644 (file)
@@ -31,6 +31,7 @@
 #import <SoObjects/SOGo/SOGoObject.h>
 #import <SoObjects/SOGo/LDAPUserManager.h>
 #import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
+#import <SoObjects/SOGo/NSString+Utilities.h>
 
 #import "SOGoACLAdvisory.h"
 
   subject = [[self generateResponse] contentAsString];
   isSubject = NO;
 
-  return [subject stringByTrimmingSpaces];
+  return [[subject stringByTrimmingSpaces] asQPSubjectString: @"utf-8"];
 }
 
 - (NSString *) body
 - (NSString *) aclMethod
 {
   [self subclassResponsibility: _cmd];
-
+  
   return nil;
 }
 
 
 @end
 
-@implementation SOGoACLEnglishAdditionAdvisory
+@implementation SOGoACLAdditionAdvisory
+
+- (NSString *) aclMethod { return @"add"; }
+
 @end
 
-@implementation SOGoACLEnglishRemovalAdvisory
+@implementation SOGoACLRemovalAdvisory
+
+- (NSString *) aclMethod { return @"remove"; }
+
 @end
 
-@implementation SOGoACLFrenchAdditionAdvisory
+@implementation SOGoACLEnglishAdditionAdvisory
 @end
 
-@implementation SOGoACLFrenchRemovalAdvisory
+@implementation SOGoACLFrenchAdditionAdvisory
 @end
 
 @implementation SOGoACLGermanAdditionAdvisory
 @end
 
+@implementation SOGoACLEnglishRemovalAdvisory
+@end
+
+@implementation SOGoACLFrenchRemovalAdvisory
+@end
+
 @implementation SOGoACLGermanRemovalAdvisory
 @end
diff --git a/UI/SOGoUI/SOGoFolderAdvisory.h b/UI/SOGoUI/SOGoFolderAdvisory.h
new file mode 100644 (file)
index 0000000..3485d4c
--- /dev/null
@@ -0,0 +1,68 @@
+/* SOGoFolderAdvisory.h - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Ludovic Marcotte <ludovic@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SOGOFOLDERADVISORY_H
+#define SOGOFOLDERADVISORY_H
+
+#import "UIxComponent.h"
+#import "../../SoObjects/SOGo/SOGoFolder.h"
+
+@interface SOGoFolderAdvisory : UIxComponent
+{
+  NSString *recipientUID;
+  SOGoFolder *folderObject;
+  BOOL isSubject;
+  BOOL isBody;
+}
+
+- (void) setFolderObject: (SOGoFolder *) theFolder;
+- (void) setRecipientUID: (NSString *) newRecipientUID;
+- (void) send;
+
+- (BOOL) isSubject;
+- (BOOL) isBody;
+
+- (NSString *) subject;
+- (NSString *) body;
+- (NSString *) folderMethod;
+
+@end
+
+@interface SOGoFolderEnglishAdditionAdvisory : SOGoFolderAdvisory
+@end
+
+@interface SOGoFolderEnglishRemovalAdvisory : SOGoFolderAdvisory
+@end
+
+@interface SOGoFolderFrenchAdditionAdvisory : SOGoFolderAdvisory
+@end
+
+@interface SOGoFolderFrenchRemovalAdvisory : SOGoFolderAdvisory
+@end
+
+@interface SOGoFolderGermanAdditionAdvisory : SOGoFolderAdvisory
+@end
+
+@interface SOGoFolderGermanRemovalAdvisory : SOGoFolderAdvisory
+@end
+
+#endif /* SOGOFOLDERADVISORY_H */
diff --git a/UI/SOGoUI/SOGoFolderAdvisory.m b/UI/SOGoUI/SOGoFolderAdvisory.m
new file mode 100644 (file)
index 0000000..45a1e74
--- /dev/null
@@ -0,0 +1,232 @@
+/* SOGoFolderAdvisory.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Ludovic Marcotte <ludovic@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSURL.h>
+
+#import <NGObjWeb/WOResponse.h>
+#import <NGExtensions/NGHashMap.h>
+#import <NGMail/NGMimeMessage.h>
+#import <NGMime/NGMimeBodyPart.h>
+#import <NGMime/NGMimeMultipartBody.h>
+
+#import <SoObjects/SOGo/SOGoMailer.h>
+#import <SoObjects/SOGo/SOGoUser.h>
+#import <SoObjects/SOGo/SOGoObject.h>
+#import <SoObjects/SOGo/LDAPUserManager.h>
+#import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
+#import <SoObjects/SOGo/NSString+Utilities.h>
+
+#import "SOGoFolderAdvisory.h"
+
+@implementation SOGoFolderAdvisory
+
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      recipientUID = nil;
+      folderObject = nil;
+      isSubject = NO;
+      isBody = NO;
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [recipientUID release];
+  [folderObject release];
+  [super dealloc];
+}
+
+- (void) setFolderObject: (SOGoFolder *) theFolder
+{
+  ASSIGN(folderObject, theFolder);
+}
+
+- (void) setRecipientUID: (NSString *) newRecipientUID
+{
+  ASSIGN (recipientUID, newRecipientUID);
+}
+
+- (BOOL) isSubject
+{
+  return isSubject;
+}
+
+- (BOOL) isBody
+{
+  return isBody;
+}
+
+- (NSString *) displayName
+{
+  return [folderObject displayName];
+}
+
+- (NSString *) httpFolderURL
+{
+  NSString *absoluteString;
+  NSMutableString *url;
+
+#warning the url returned by SOGoMail may be empty, we need to handle that
+  absoluteString = [[folderObject soURL] absoluteString];
+  url = [NSMutableString stringWithString: absoluteString];
+
+  if (![url hasSuffix: @"/"])
+    [url appendString: @"/"];
+
+  return url;
+}
+
+- (NSString *) subject
+{
+  NSString *subject;
+
+  isSubject = YES;
+  subject = [[self generateResponse] contentAsString];
+  isSubject = NO;
+
+  return [[subject stringByTrimmingSpaces] asQPSubjectString: @"utf-8"];
+}
+
+- (NSString *) body
+{
+  NSString *body;
+
+  isBody = YES;
+  body = [[self generateResponse] contentAsString];
+  isBody = NO;
+
+  return [body stringByTrimmingSpaces];
+}
+
+- (NSString *) folderMethod
+{
+  [self subclassResponsibility: _cmd];
+  
+  return nil;
+}
+
+- (NGMimeBodyPart *) _textPart
+{
+  NGMutableHashMap *headerMap;
+  NGMimeBodyPart *part;
+  NSData *body;
+
+  headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
+  [headerMap setObject: @"text/plain; charset=utf-8" forKey: @"content-type"];
+
+  part = [NGMimeBodyPart bodyPartWithHeader: headerMap];
+  body = [[self body] dataUsingEncoding: NSUTF8StringEncoding];
+  [part setBody: [self body]];
+
+  return part;
+}
+
+- (NGMimeBodyPart *) _sogoNotificationPart
+{
+  NGMutableHashMap *headerMap;
+  NGMimeBodyPart *part;
+  NSData *body;
+
+  /* calendar part */
+  headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
+  [headerMap setObject: [NSString stringWithFormat:
+                                   @"%@; method=%@; type=%@; charset=%@",
+                                 @"application/x-sogo-notification",
+                                 [self folderMethod], [folderObject folderType],
+                                 @"utf-8"]
+            forKey: @"content-type"];
+
+  part = [NGMimeBodyPart bodyPartWithHeader: headerMap];
+  body = [[self httpFolderURL] dataUsingEncoding: NSUTF8StringEncoding];
+  [part setBody: body];
+
+  return part;
+}
+
+- (void) send
+{
+  NSString *recipient, *date;
+  NGMutableHashMap *headerMap;
+  NGMimeMessage *message;
+  NGMimeMultipartBody *body;
+  SOGoUser *activeUser;
+  NSDictionary *identity;
+  NSString *from, *fullMail;
+
+  activeUser = [context activeUser];
+  identity = [activeUser primaryIdentity];
+  from = [identity objectForKey: @"email"];
+  fullMail = [NSString stringWithFormat: @"%@ <%@>",
+                      [identity objectForKey: @"fullName"], from];
+
+  recipient = [[LDAPUserManager sharedUserManager]
+               getFullEmailForUID: recipientUID];
+
+  headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
+  [headerMap setObject: @"multipart/alternative" forKey: @"content-type"];
+  [headerMap setObject: fullMail forKey: @"From"];
+  [headerMap setObject: recipient forKey: @"To"];
+  date = [[NSCalendarDate date] rfc822DateString];
+  [headerMap setObject: date forKey: @"Date"];
+  [headerMap setObject: [self subject] forKey: @"Subject"];
+  message = [NGMimeMessage messageWithHeader: headerMap];
+
+  body = [[NGMimeMultipartBody alloc] initWithPart: message];
+  [body addBodyPart: [self _textPart]];
+  [body addBodyPart: [self _sogoNotificationPart]];
+  [message setBody: body];
+  [body release];
+
+  [[SOGoMailer sharedMailer] sendMimePart: message
+                            toRecipients: [NSArray arrayWithObject: recipient]
+                            sender: from];
+}
+
+@end
+
+@implementation SOGoFolderEnglishAdditionAdvisory
+- (NSString *) folderMethod { return @"add"; }
+@end
+
+@implementation SOGoFolderEnglishRemovalAdvisory
+- (NSString *) folderMethod { return @"remove"; }
+@end
+
+@implementation SOGoFolderFrenchAdditionAdvisory
+- (NSString *) folderMethod { return @"add"; }
+@end
+
+@implementation SOGoFolderFrenchRemovalAdvisory
+- (NSString *) folderMethod { return @"remove"; }
+@end
+
+@implementation SOGoFolderGermanAdditionAdvisory
+- (NSString *) folderMethod { return @"add"; }
+@end
+
+@implementation SOGoFolderGermanRemovalAdvisory
+- (NSString *) folderMethod { return @"remove"; }
+@end
index 4f4fb0bb36c0787f7ac66936f7497822b56d686b..de4e0c4722bffef7142a4d26930e0627125ab332 100644 (file)
@@ -20,8 +20,6 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#warning WE LEAK IVARS LIKE CRAZY HERE
-
 #include <math.h>
 
 #import <NGObjWeb/SoObject.h>
 
 - (void) dealloc
 {
-  RELEASE(repeat);
+  [item release];
+  [repeat release];
+  [aptStartDate release];
+  [aptEndDate release];
   [super dealloc];
 }
 
   else
     text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]];
 
-  NSLog(@"itemRepeatText: %@", text);
-
   return text;
 }
 
 - (void) setItem: (NSString *) newItem
 {
-  item = newItem;
+  ASSIGN (item, newItem);
 }
 
 - (NSString *) item
 //   return reminder;
 // }
 
+- (NSString *) reminder
+{
+  return @"";
+}
+
+- (void) setReminder: (NSString *) newReminder
+{
+}
+
 - (NSString *) itemReminderText
 {
   NSString *text;
 
 - (void) setRepeat: (NSString *) newRepeat
 {
-  ASSIGN(repeat, newRepeat);
-}
-
-- (NSString *) reminder
-{
-  return @"";
-}
-
-- (void) setReminder: (NSString *) newReminder
-{
+  ASSIGN (repeat, newRepeat);
 }
 
 /* actions */
   NSCalendarDate *startDate, *endDate;
   NSString *duration;
   unsigned int minutes;
+  iCalRecurrenceRule *rule;
 
   event = (iCalEvent *) [[self clientObject] component: NO];
   if (event)
   // We initialize our repeat ivars
   if ([event hasRecurrenceRules])
     {
-      iCalRecurrenceRule *rule;
-      
       repeat = @"CUSTOM";
 
       rule = [[event recurrenceRules] lastObject];
 
       if ([rule frequency] == iCalRecurrenceFrequenceWeekly)
        {
-         if ([rule repeatInterval] == 1) repeat = @"WEEKLY";
-         else if ([rule repeatInterval] == 2) repeat = @"BI-WEEKLY";
+         if ([rule repeatInterval] == 1)
+           repeat = @"WEEKLY";
+         else if ([rule repeatInterval] == 2)
+           repeat = @"BI-WEEKLY";
        }
       else if ([rule frequency] == iCalRecurrenceFrequenceDaily)
        {
-         if ([rule byDayMask] == (iCalWeekDayMonday|iCalWeekDayTuesday|iCalWeekDayWednesday|iCalWeekDayThursday|iCalWeekDayFriday)) repeat = @"EVERY WEEKDAY";
-         else if (![rule byDayMask]) repeat = @"DAILY";
+         if ([rule byDayMask] == (iCalWeekDayMonday
+                                  | iCalWeekDayTuesday
+                                  | iCalWeekDayWednesday
+                                  | iCalWeekDayThursday
+                                  | iCalWeekDayFriday))
+           repeat = @"EVERY WEEKDAY";
+         else if (![rule byDayMask])
+           repeat = @"DAILY";
        }
-      else if ([rule frequency] == iCalRecurrenceFrequenceMonthly && [rule repeatInterval] == 1) repeat = @"MONTHLY";
-      else if ([rule frequency] == iCalRecurrenceFrequenceYearly && [rule repeatInterval] == 1) repeat = @"YEARLY";
+      else if ([rule frequency] == iCalRecurrenceFrequenceMonthly
+              && [rule repeatInterval] == 1)
+       repeat = @"MONTHLY";
+      else if ([rule frequency] == iCalRecurrenceFrequenceYearly
+              && [rule repeatInterval] == 1)
+       repeat = @"YEARLY";
     }
   else
-    {
-      DESTROY(repeat);
-    }
+    DESTROY(repeat);
 
   return self;
 }
 {
   SOGoAppointmentObject *clientObject;
   int nbrDays;
+  iCalRecurrenceRule *rule;
 
   clientObject = [self clientObject];
   event = (iCalEvent *) [clientObject component: YES];
 
   // We remove any repeat rules
   if (!repeat && [event hasRecurrenceRules])
+    [event removeAllRecurrenceRules];
+  else if (!([repeat caseInsensitiveCompare: @"-"] == NSOrderedSame
+            || [repeat caseInsensitiveCompare: @"CUSTOM"] == NSOrderedSame))
     {
-      [event removeAllRecurrenceRules];
-    }
-  else if (!([repeat caseInsensitiveCompare: @"-"] == NSOrderedSame || [repeat caseInsensitiveCompare: @"CUSTOM"] == NSOrderedSame))
-    {
-      iCalRecurrenceRule *rule;
-
-      rule = [[iCalRecurrenceRule alloc] init];
+      rule = [iCalRecurrenceRule new];
 
+      [rule setInterval: @"1"];
       if ([repeat caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame)
        {
          [rule setFrequency: iCalRecurrenceFrequenceWeekly];
        }
       else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
        {
-         [rule setByDayMask: (iCalWeekDayMonday|iCalWeekDayTuesday|iCalWeekDayWednesday|iCalWeekDayThursday|iCalWeekDayFriday)];
+         [rule setByDayMask: (iCalWeekDayMonday
+                              |iCalWeekDayTuesday
+                              |iCalWeekDayWednesday
+                              |iCalWeekDayThursday
+                              |iCalWeekDayFriday)];
          [rule setFrequency: iCalRecurrenceFrequenceDaily];
-         [rule setInterval: @"1"];
        }
       else
-       {
-         [rule setFrequency: (iCalRecurrenceFrequency)[rule valueForFrequency: repeat]];
-         [rule setInterval: @"1"];
-       }
+       [rule setFrequency:
+               (iCalRecurrenceFrequency) [rule valueForFrequency: repeat]];
       [event setRecurrenceRules: [NSArray arrayWithObject: rule]];
-      RELEASE(rule);
+      [rule release];
     }
 }
 
index c1c1b45e75a465f68d1e07463a3f89b77ce7a273..af3e3b0250615789d7fa739e25fbff490cb98486 100644 (file)
           onclick="window.close(); return false;" />
       </div>
     </form>
-
-    <script type="text/javascript">
-      initEditorForm();
-    </script>
   </var:component>
index b6b95e63ef4a82946323332c1db010a7308cc024..9b5e19f2bfe154bb1e5bb5b64468eb7efb0a1681 100644 (file)
   className="UIxPageFrame"
   title="name"
   const:toolbar="none"
-  const:popup="YES">
-  <script type="text/javascript" rsrc:src="dtree.js"><!-- space --></script>
+  const:popup="YES"
+  const:jsFiles="dtree.js">
   <var:component className="UIxContactsFilterPanel" qualifier="qualifier" />
-  <div id="folders">
-  </div>
+  <div id="folders"><!-- space --></div>
   <div id="buttons">
     <input type="submit" id="addButton" class="button" label:value="Add..."/>
   </div>
index bfc1976a6e5975232bf8fd481ca65cd24d705db6..bbc56eb074f5544b4cd379a875ea6ced8c115e09 100644 (file)
@@ -9,7 +9,8 @@
   xmlns:label="OGo:label"
   className="UIxPageFrame"
   title="panelTitle"
-  const:popup="YES">
+  const:popup="YES"
+  const:jsFiles="UIxMailToSelection.js">
   <div class="menu" id="attachmentsMenu">
     <ul>
       <li><var:string label:value="Open"/></li>
index e389db9c074c036c1838ad5ab5c8499fd652d8ed..8f5012c92ba9c3bd7cc9b595cd3cee56533fe198 100644 (file)
@@ -7,11 +7,9 @@
   xmlns:label="OGo:label"
   className="UIxPageFrame"
   title="title"
-  >
-  <script type="text/javascript" rsrc:src="dtree.js"><!-- space --></script>
-  <script type="text/javascript" rsrc:src="MailerUI+dTree.js"><!-- space --></script>
+  const:jsFiles="dtree.js,MailerUI+dTree.js">
   <script type="text/javascript">
-    var mailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>'.evalJSON(true);
+    var textMailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>';
   </script>
   <div class="menu" id="accountIconMenu">
     <ul>
       <li><var:string label:value="Rename Folder..." /></li>
       <li><var:string label:value="Compact This Folder" /></li>
       <li><var:string label:value="Delete Folder" /></li>
+      <li><var:string label:value="Use This Folder For" /></li>
       <li><!-- separator --></li>
       <li><var:string label:value="Search Messages..." /></li>
       <li><var:string label:value="Sharing..." /></li>
     </ul>
   </div>
+
+  <div class="menu" id="folderTypeMenu">
+    <ul>
+      <li><var:string label:value="Sent Messages" /></li>
+      <li><var:string label:value="Drafts" /></li>
+      <li><var:string label:value="Deleted Messages" /></li>
+    </ul>
+  </div>
   
   <div class="menu" id="addressMenu">
     <ul>
index ebde46970387e979dd69c51fd13e66ec65cfd18e..7aa986ce354b8cd05950d43e0837d847d9b93e01 100644 (file)
@@ -8,7 +8,6 @@
     <script type="text/javascript">
       var currentIndex = <var:string value="currentIndex" />;
     </script>
-    <script type="text/javascript" rsrc:src="UIxMailToSelection.js"><!-- space --></script>
 
     <div id="addressList"
       ><var:foreach list="addressLists" item="addressList"
diff --git a/UI/Templates/MailerUI/UIxMailTree.wox b/UI/Templates/MailerUI/UIxMailTree.wox
deleted file mode 100644 (file)
index 97cb46b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" standalone="yes"?>
-  <div id="folderTreeContent"
-    xmlns="http://www.w3.org/1999/xhtml"
-    xmlns:var="http://www.skyrix.com/od/binding"
-    xmlns:const="http://www.skyrix.com/od/constant"
-    xmlns:rsrc="OGo:url"
-    xmlns:label="OGo:label">
-    <!-- TODO: extend treeview to use CSS -->
-
-    <script type="text/javascript" rsrc:src="dtree.js"><!-- space --></script>
-    <script type="text/javascript">
-      d = new dTree('d');
-      d.config.folderLlinks = true;
-      d.config.hideRoot = true;
-
-      d.icon.root = '<var:string rsrc:value="tbtv_account_17x17.gif" />';
-      d.icon.folder = '<var:string rsrc:value="tbtv_leaf_corner_17x17.gif" />';
-      d.icon.folderOpen        = '<var:string rsrc:value="tbtv_leaf_corner_17x17.gif" />';
-      d.icon.node = '<var:string rsrc:value="tbtv_leaf_corner_17x17.gif" />';
-      d.icon.line = '<var:string rsrc:value="tbtv_line_17x17.gif" />';
-      d.icon.join = '<var:string rsrc:value="tbtv_junction_17x17.gif" />';
-      d.icon.joinBottom        = '<var:string rsrc:value="tbtv_corner_17x17.gif" />';
-      d.icon.plus = '<var:string rsrc:value="tbtv_plus_17x17.gif" />';
-      d.icon.plusBottom        = '<var:string rsrc:value="tbtv_corner_plus_17x17.gif" />';
-      d.icon.minus = '<var:string rsrc:value="tbtv_minus_17x17.gif" />';
-      d.icon.minusBottom = '<var:string rsrc:value="tbtv_corner_minus_17x17.gif" />';
-      d.icon.nlPlus = '<var:string rsrc:value="tbtv_corner_plus_17x17.gif" />';
-      d.icon.nlMinus = '<var:string rsrc:value="tbtv_corner_minus_17x17.gif" />';
-      d.icon.empty = '<var:string rsrc:value="empty.gif" />';
-
-      d.add(0, -1, '');
-      <var:foreach list="flattenedNodes" item="item"
-       ><var:component className="UIxMailTreeBlockJS"
-         const:treeObjectName="d"
-         var:item="item"
-          /></var:foreach>
-      document.write(d);
-    </script>
-  </div>
diff --git a/UI/Templates/MailerUI/UIxMailTreeBlockJS.wox b/UI/Templates/MailerUI/UIxMailTreeBlockJS.wox
deleted file mode 100644 (file)
index d63a075..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" standalone="yes"?>
-  <container 
-    xmlns="http://www.w3.org/1999/xhtml"
-    xmlns:var="http://www.skyrix.com/od/binding"
-    xmlns:const="http://www.skyrix.com/od/constant"
-    ><var:string value="treeObjectName" />.add(<var:string value="item.serial" />, <var:string value="item.parent" />, '<var:string value="item.title" />', <var:string value="item.hasChildren" />, '#', '<var:string value="item.name" />', '<var:string value="item.folderType" />', '', '', '<var:string value="iconName" />', '<var:string value="iconName" />');
-  </container>
diff --git a/UI/Templates/SOGoFolderEnglishAdditionAdvisory.wox b/UI/Templates/SOGoFolderEnglishAdditionAdvisory.wox
new file mode 100644 (file)
index 0000000..f02d5b0
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" standalone="yes"?>
+<!DOCTYPE container>
+<container 
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label">
+
+<var:if condition="isSubject">
+    <var:string value="displayName"/> has been created
+</var:if>
+
+<var:if condition="isBody">
+The <var:string value="displayName"/> folder has been created. 
+
+You can access this resource remotely by using the following URL:  
+
+<var:string value="httpFolderURL"/>
+</var:if>
+
+</container>
diff --git a/UI/Templates/SOGoFolderEnglishRemovalAdvisory.wox b/UI/Templates/SOGoFolderEnglishRemovalAdvisory.wox
new file mode 100644 (file)
index 0000000..467c926
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" standalone="yes"?>
+<!DOCTYPE container>
+<container 
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label">
+
+<var:if condition="isSubject">
+    <var:string value="displayName"/> has been deleted
+</var:if>
+
+<var:if condition="isBody">
+The <var:string value="displayName"/> folder has been deleted. 
+
+The following URL is now no longer active:  
+
+<var:string value="httpFolderURL"/>
+</var:if>
+
+</container>
diff --git a/UI/Templates/SOGoFolderFrenchAdditionAdvisory.wox b/UI/Templates/SOGoFolderFrenchAdditionAdvisory.wox
new file mode 100644 (file)
index 0000000..a174f26
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" standalone="yes"?>
+<!DOCTYPE container>
+<container 
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label">
+
+  <var:if condition="isSubject">
+    <var:string value="displayName"/> a été créé
+  </var:if>
+
+  <var:if condition="isBody">
+Le dossier <var:string value="displayName"/> a été créé.
+
+Vous pouvez accéder à distance à ce dossier avec le lien suivant:
+
+    <var:string value="httpFolderURL"/>
+  </var:if>
+</container>
diff --git a/UI/Templates/SOGoFolderFrenchRemovalAdvisory.wox b/UI/Templates/SOGoFolderFrenchRemovalAdvisory.wox
new file mode 100644 (file)
index 0000000..4789a02
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" standalone="yes"?>
+<!DOCTYPE container>
+<container 
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label">
+
+<var:if condition="isSubject">
+    <var:string value="displayName"/> a été supprimé
+</var:if>
+
+<var:if condition="isBody">
+Le dossier <var:string value="displayName"/> a été supprimé.
+
+Le lien suivant n'est plus actif:
+
+<var:string value="httpFolderURL"/>
+</var:if>
+
+</container>
diff --git a/UI/Templates/SOGoFolderGermanAdditionAdvisory.wox b/UI/Templates/SOGoFolderGermanAdditionAdvisory.wox
new file mode 100644 (file)
index 0000000..f02d5b0
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" standalone="yes"?>
+<!DOCTYPE container>
+<container 
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label">
+
+<var:if condition="isSubject">
+    <var:string value="displayName"/> has been created
+</var:if>
+
+<var:if condition="isBody">
+The <var:string value="displayName"/> folder has been created. 
+
+You can access this resource remotely by using the following URL:  
+
+<var:string value="httpFolderURL"/>
+</var:if>
+
+</container>
diff --git a/UI/Templates/SOGoFolderGermanRemovalAdvisory.wox b/UI/Templates/SOGoFolderGermanRemovalAdvisory.wox
new file mode 100644 (file)
index 0000000..467c926
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" standalone="yes"?>
+<!DOCTYPE container>
+<container 
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label">
+
+<var:if condition="isSubject">
+    <var:string value="displayName"/> has been deleted
+</var:if>
+
+<var:if condition="isBody">
+The <var:string value="displayName"/> folder has been deleted. 
+
+The following URL is now no longer active:  
+
+<var:string value="httpFolderURL"/>
+</var:if>
+
+</container>
index a2e393f3d2e53253dd0f1bcf30d0d6b86c9b201f..80ecdd083adbc5d3c104068414322d8f06517e32 100644 (file)
@@ -9,8 +9,8 @@
   xmlns:label="OGo:label"
   className="UIxPageFrame"
   const:toolbar="none"
-  const:popup="YES">
-  <script type="text/javascript" rsrc:src="skycalendar.js"><!-- space --></script>
+  const:popup="YES"
+  const:jsFiles="skycalendar.js">
   <div id="attendeesView">
     <div id="freeBusyViewButtons">
       <var:string label:value="Suggest time slot:"/>
index 19ac4c815066d0a6198f8e9e2b992304a3051f14..8359c472dd3eb611b2917a06bfd603dbd4bb0513 100644 (file)
@@ -7,8 +7,7 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label"
   className="UIxPageFrame"
-  title="title"
-  >
+  title="title">
   <script type="text/javascript">
     var UTCOffset = <var:string value="userUTCOffset"/>;
   </script>
index afa92bf3c3cae544b70edb601c6739961f5029f3..52caee9cb1b83f7459fa17307d7469c825acd4a4 100644 (file)
@@ -10,9 +10,8 @@
   className="UIxPageFrame"
   const:popup="YES"
   title="name"
-  var:toolbar="toolbar">
-  <script type="text/javascript" rsrc:src="skycalendar.js"><!-- space --></script>
-  <script type="text/javascript" rsrc:src="UIxComponentEditor.js"><!-- space --></script>
+  var:toolbar="toolbar"
+  const:jsFiles="skycalendar.js,UIxComponentEditor.js">
 
   <script type="text/javascript">
     var activeCalendar = '<var:string value="clientObject.container.nameInContainer"/>';
index 58ba94c90077a8bac3a85ade4e051d2e13380f5d..b967c8974d2fc131b684a09d82db52931a3f9f2a 100644 (file)
        <input type="hidden" name="action" value="saveAcls"/>
        <input type="hidden" id="userUIDS" name="userUIDS"
          var:value="userUIDS"/>
-       <label><var:string label:value="Owner:"/><br/>
-         <span class="value"><strong><var:string value="ownerName"/></strong></span></label><br/>
+       <var:if condition="hasOwner">
+         <label><var:string label:value="Owner:"/><br/>
+           <span class="value"><strong><var:string value="ownerName"/></strong></span></label><br/>
+       </var:if>
       </div>
       <input id="defaultRolesBtn" type="button"
        class="button" label:value="Default Roles"/>
index 627c099479589ef185148577837439f2217fcd72..837071a8575e1e600cc347f115991710643a7f58 100644 (file)
       </head>
 
       <body var:class="bodyClasses"
-      ><var:if condition="isCompatibleBrowser"
-       ><script type="text/javascript">
-         var ApplicationBaseURL = '<var:string value="applicationPath" />';
-         var ResourcesURL = '/SOGo.woa/WebServerResources';
+       ><var:if condition="isCompatibleBrowser"
+         >
          <var:if condition="shortUserNameForDisplay" const:value="anonymous"
            const:negate="YES"
-           >var UserFolderURL = '<var:string value="userFolderPath" />';
-         var UserLogin = '<var:string value="shortUserNameForDisplay" />';</var:if>
-       </script>
-       <script type="text/javascript" rsrc:src="events.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="prototype.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="tablekit.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="tablekit-trueresize.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="JavascriptAPIExtensions.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="HTMLElement.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="HTMLInputElement.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="HTMLTableElement.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="HTMLUListElement.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="generic.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="SOGoDragAndDrop.js"><!-- space required --></script>
-       <script type="text/javascript" rsrc:src="SOGoDragHandles.js"><!-- space required --></script>
-       <var:if condition="hasProductSpecificJavaScript"><script type="text/javascript"
-           var:src="productJavaScriptURL"><!-- space required --></script></var:if>
-       <var:if condition="hasPageSpecificJavaScript"><script type="text/javascript"
-           var:src="pageJavaScriptURL"><!-- space required --></script></var:if>
-       <var:js-stringtable
-         var:framework="productFrameworkName"
-         const:identifier="labels" />
-       <var:js-stringtable
-         const:identifier="clabels" />
-       <var:if condition="shortUserNameForDisplay" const:value="anonymous"
-         const:negate="YES"
-         ><var:if condition="shortUserNameForDisplay" const:value="wrongusernamepassword"
-         const:negate="YES"
-          ><var:if condition="isPopup" const:negate="YES"
-           ><var:if condition="context.isUIxDebugEnabled"
-             ><div id="logConsole"><!-- space --></div></var:if>
-           <div id="linkBanner" class="linkbanner">
-             <a id="logoff" var:href="logoffPath"
-               ><var:string label:value="Sign Out" /></a>
-             <a var:href="relativeCalendarPath"
-               ><var:string label:value="Calendar" /></a> |
-             <a var:href="relativeContactsPath"
-               ><var:string label:value="Address Book" /></a> |
-             <a var:href="relativeMailPath"
-               ><var:string label:value="Mail" /></a> |
-             <a var:href="relativePreferencesPath"
-               ><var:string label:value="Preferences" /></a>
-             <var:if condition="context.isUIxDebugEnabled"
-               >| <a href="#"><var:string
-                   label:value="Log Console (dev.)" /></a
-                 ></var:if>
-           </div>
-         </var:if
-           ><var:component className="UIxToolbar" var:toolbar="toolbar"
-           />
-       </var:if></var:if>
-       
-       <div class="pageContent"
-         ><var:component-content
-           /></div>
+           ><var:if condition="shortUserNameForDisplay" const:value="wrongusernamepassword"
+             const:negate="YES"
+             ><var:if condition="isPopup" const:negate="YES"
+               ><var:if condition="context.isUIxDebugEnabled"
+                 ><div id="logConsole"><!-- space --></div></var:if>
+               <div id="linkBanner" class="linkbanner">
+                 <a id="logoff" var:href="logoffPath"
+                   ><var:string label:value="Sign Out" /></a>
+                 <a var:href="relativeCalendarPath"
+                   ><var:string label:value="Calendar" /></a> |
+                 <a var:href="relativeContactsPath"
+                   ><var:string label:value="Address Book" /></a> |
+                 <a var:href="relativeMailPath"
+                   ><var:string label:value="Mail" /></a> |
+                 <a var:href="relativePreferencesPath"
+                   ><var:string label:value="Preferences" /></a>
+                 <var:if condition="context.isUIxDebugEnabled"
+                   >| <a href="#"><var:string
+                       label:value="Log Console (dev.)" /></a
+                     ></var:if>
+               </div>
+             </var:if
+               ><var:component className="UIxToolbar" var:toolbar="toolbar"
+               />
+           </var:if></var:if>
+         
+         <div id="pageContent"><var:component-content/></div>
+
+         <script type="text/javascript">
+           var ApplicationBaseURL = '<var:string value="applicationPath" />';
+           var ResourcesURL = '/SOGo.woa/WebServerResources';
+           <var:if condition="shortUserNameForDisplay" const:value="anonymous"
+             const:negate="YES"
+             >var UserFolderURL = '<var:string value="userFolderPath" />';
+             var UserLogin = '<var:string value="shortUserNameForDisplay" />';</var:if>
+           <var:string value="commonLocalizableStrings" const:escapeHTML="NO"/>
+           <var:string value="productLocalizableStrings" const:escapeHTML="NO"/>
+         </script>
+         <script type="text/javascript" rsrc:src="events.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="prototype.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="tablekit.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="tablekit-trueresize.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="JavascriptAPIExtensions.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="HTMLElement.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="HTMLInputElement.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="HTMLTableElement.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="HTMLUListElement.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="generic.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="SOGoDragAndDrop.js"><!-- space --></script>
+         <script type="text/javascript" rsrc:src="SOGoDragHandles.js"><!-- space --></script>
+         <var:if condition="hasProductSpecificJavaScript"><script type="text/javascript"
+             var:src="productJavaScriptURL"><!-- space --></script></var:if>
+         <var:if condition="hasPageSpecificJavaScript"><script type="text/javascript"
+             var:src="pageJavaScriptURL"><!-- space --></script></var:if>
+         <var:foreach list="additionalJSFiles" item="item"
+           ><script type="text/javascript" var:src="item"><!-- space --></script>
+         </var:foreach>
         </var:if>
         <var:if condition="isCompatibleBrowser" const:negate="YES">
-    <div id="loginScreen">
-      <img id="splash" rsrc:src="lori-login.jpg"/><br/><br/>
-      <p><var:string label:value="browserNotCompatible"/></p>
-      <p class="browser"><a href="http://www.getfirefox.com/"><img rsrc:src="browser_firefox.gif"/><var:string label:value="Download"/> Firefox</a></p>
-    <var:if condition="isIE7Compatible">
-      <p><var:string label:value="alternativeBrowsers"/></p>
-      <p class="browser"><a href="http://www.microsoft.com/ie/download/"><img rsrc:src="browser_ie.gif"/><var:string label:value="Download"/> Internet Explorer 7</a></p>
-      <p class="browser"><a href="http://www.apple.com/safari/download/"><img rsrc:src="browser_safari.gif"/><var:string label:value="Download"/> Safari 3</a></p>
-    </var:if>
-    <var:if condition="isMac">
-      <p><var:string label:value="alternativeBrowserSafari"/></p>
-      <p class="browser"><a href="http://www.apple.com/safari/download/"><img rsrc:src="browser_safari.gif"/><var:string label:value="Download"/> Safari 3</a></p>
-    </var:if>
-    </div>
-  </var:if>
+         <div id="loginScreen">
+           <img id="splash" rsrc:src="lori-login.jpg"/><br/><br/>
+           <p><var:string label:value="browserNotCompatible"/></p>
+           <p class="browser"><a href="http://www.getfirefox.com/"><img rsrc:src="browser_firefox.gif"/><var:string label:value="Download"/> Firefox</a></p>
+           <var:if condition="isIE7Compatible">
+             <p><var:string label:value="alternativeBrowsers"/></p>
+             <p class="browser"><a href="http://www.microsoft.com/ie/download/"><img rsrc:src="browser_ie.gif"/><var:string label:value="Download"/> Internet Explorer 7</a></p>
+             <p class="browser"><a href="http://www.apple.com/safari/download/"><img rsrc:src="browser_safari.gif"/><var:string label:value="Download"/> Safari 3</a></p>
+           </var:if>
+           <var:if condition="isMac">
+             <p><var:string label:value="alternativeBrowserSafari"/></p>
+             <p class="browser"><a href="http://www.apple.com/safari/download/"><img rsrc:src="browser_safari.gif"/><var:string label:value="Download"/> Safari 3</a></p>
+           </var:if>
+         </div>
+       </var:if>
        <noscript>
          <div class="javascriptPopupBackground">
          </div>
index 15921f4e161187f7f3c698d5cccea07855f78356..ba7d525e4210d64f8e5d866e63b7b55c15734830 100644 (file)
@@ -301,7 +301,7 @@ function onMenuWriteToContact(event) {
    var emailCell = contactRow.down('td', 1);
 
    if (!emailCell.firstChild) { // .nodeValue is the contact email address
-     window.alert(labels["The selected contact has no email address."].decodeEntities());
+     window.alert(labels["The selected contact has no email address."]);
      return false;
    }
 
@@ -343,7 +343,7 @@ function onToolbarWriteToSelectedContacts(event) {
   }
   
   if (rowsWithEmail == 0) {
-    window.alert(labels["The selected contact has no email address."].decodeEntities());
+    window.alert(labels["The selected contact has no email address."]);
   }
   else if (document.body.hasClassName("popup"))
     window.close();
@@ -708,10 +708,16 @@ function folderRenameCallback(http) {
 function onMenuSharing(event) {
    var folders = $("contactFolders");
    var selected = folders.getSelectedNodes()[0];
-   var title = this.innerHTML;
-   var url = URLForFolderID(selected.getAttribute("id"));
+   var owner = selected.getAttribute("owner");
+   if (owner == "nobody")
+     window.alert(clabels["The user rights cannot be"
+                         + " edited for this object!"]);
+   else {
+     var title = this.innerHTML;
+     var url = URLForFolderID(selected.getAttribute("id"));
 
-   openAclWindow(url + "/acls", title);
+     openAclWindow(url + "/acls", title);
+   }
 }
 
 function getMenus() {
index c3c3bd35b889065a8a0a7d93bb179e9783c95215..378670cab3f331b715d7f21107133e03a517e9d4 100644 (file)
@@ -127,12 +127,12 @@ Element.addMethods({
     
     var menuTop = Event.pointerY(event);
     var menuLeft = Event.pointerX(event);
-    var heightDiff = (window.innerHeight
+    var heightDiff = (window.height()
                      - (menuTop + popup.offsetHeight));
     if (heightDiff < 0)
       menuTop += heightDiff;
     
-    var leftDiff = (window.innerWidth
+    var leftDiff = (window.width()
                    - (menuLeft + popup.offsetWidth));
     if (leftDiff < 0)
       menuLeft -= popup.offsetWidth;
@@ -148,7 +148,8 @@ Element.addMethods({
   attachMenu: function(element, menuName) {
     element = $(element);
     element.sogoContextMenu = $(menuName);
-    Event.observe(element, "contextmenu", element.onContextMenu.bindAsEventListener(element));
+    Event.observe(element, "contextmenu",
+                 element.onContextMenu.bindAsEventListener(element));
   },
 
   select: function(element) {
index 96943b882c2b4fbf208d8ad0f881a6d6f049d470..18fe2bd6b50b5a604fa188d28dcfa78244c03c5d 100644 (file)
@@ -351,3 +351,21 @@ String.prototype.cssSafeString = function() {
 
   return newString;
 }
+
+window.width = function() {
+  if (window.innerWidth)
+    return window.innerWidth;
+  else if (document.body && document.body.offsetWidth)
+    return document.body.offsetWidth;
+  else
+    return 0;
+}
+
+window.height = function() {
+  if (window.innerHeight)
+    return window.innerHeight;
+  else if (document.body && document.body.offsetHeight)
+    return document.body.offsetHeight;
+  else
+    return 0;
+}
index c5bed5158c9545b588553f2404061ef5776913f9..42e60b4fbe76061ec291eed4016f0e1da8f437d9 100644 (file)
@@ -24,14 +24,7 @@ var MailerUIdTreeExtension = {
    },
    _addFolder: function (parent, folder) {
       var thisCounter = this.elementCounter;
-      var fullName = "";
-      var currentFolder = folder;
-      while (currentFolder.parentFolder) {
-        fullName = "/folder" + currentFolder.name + fullName;
-        currentFolder = currentFolder.parentFolder;
-      }
-      fullName = "/" + currentFolder.name + fullName;
-      this._addFolderNode(parent, folder.name, fullName, folder.type);
+      this._addFolderNode(parent, folder.name, folder.fullName(), folder.type);
       for (var i = 0; i < folder.children.length; i++)
       this._addFolder(thisCounter, folder.children[i]);
    },
index 265552f4ad3dca8d70345fe0fdd0ccfa91057d48..f70c792eb343b8229d3c36764ae8fef408bf2268 100644 (file)
@@ -1,6 +1,9 @@
 /* JavaScript for SOGoMail */
 var accounts = {};
 var mailboxTree;
+var mailAccounts;
+if (typeof textMailAccounts != 'undefined')
+  mailAccounts = textMailAccounts.evalJSON(true);
 
 var currentMessages = new Array();
 var maxCachedMessages = 20;
@@ -11,6 +14,8 @@ var currentMailboxType = "";
 var usersRightsWindowHeight = 320;
 var usersRightsWindowWidth = 400;
 
+var pageContent;
+
 /* mail list */
 
 function openMessageWindow(msguid, url) {
@@ -52,7 +57,7 @@ function openAddressbook(sender) {
   urlstr = ApplicationBaseURL + "/../Contacts/?popup=YES";
   var w = window.open(urlstr, "Addressbook",
                      "width=640,height=400,resizable=1,scrollbars=1,toolbar=0,"
-                     + "location=0,directories=0,status=0,menubar=0,copyhistory=0");
+                     + "location=no,directories=0,status=0,menubar=0,copyhistory=0");
   w.focus();
 
   return false;
@@ -60,10 +65,14 @@ function openAddressbook(sender) {
 
 function onMenuSharing(event) {
   var folderID = document.menuTarget.getAttribute("dataname");
-  var urlstr = URLForFolderID(folderID) + "/acls";
-  preventDefault(event);
-
-  openAclWindow(urlstr);
+  var type = document.menuTarget.getAttribute("datatype");
+  if (type == "additional")
+    window.alert(clabels["The user rights cannot be"
+                        + " edited for this object!"]);
+  else {
+    var urlstr = URLForFolderID(folderID) + "/acls";
+    openAclWindow(urlstr);
+  }
 }
 
 /* mail list DOM changes */
@@ -192,31 +201,6 @@ function ml_lowlight(sender) {
 }
 
 
-/* folder operations */
-
-function ctxFolderAdd(sender) {
-  var folderName;
-   
-  folderName = prompt("Foldername: ");
-  if (folderName == undefined)
-    return false;
-  if (folderName == "")
-    return false;
-   
-  // TODO: should use a form-POST or AJAX
-  window.location.href = "createFolder?name=" + escape(folderName);
-  return false;
-}
-
-function ctxFolderDelete(sender) {
-  if (!confirm("Delete current folder?"))
-    return false;
-   
-  // TODO: should use a form-POST or AJAX
-  window.location.href = "deleteFolder";
-  return false;
-}
-
 /* bulk delete of messages */
 
 function uixDeleteSelectedMessages(sender) {
@@ -351,12 +335,34 @@ function onMailboxTreeItemClick(event) {
   Event.stop(event);
 }
 
-function onMailboxMenuMove() {
-  window.alert("unimplemented");
+function _onMailboxMenuAction(menuEntry, error, actionName) {
+  var targetMailbox = menuEntry.mailbox.fullName();
+
+  if (targetMailbox == currentMailbox)
+    window.alert(labels[error]);
+  else {
+    var message;
+    if (document.menuTarget.tagName == "DIV")
+      message = currentMessages[currentMailbox];
+    else
+      message = document.menuTarget.getAttribute("id").substr(4);
+
+    var urlstr = (URLForFolderID(currentMailbox) + "/" + message
+                 + "/" + actionName + "?folder=" + targetMailbox);
+    triggerAjaxRequest(urlstr, folderRefreshCallback, currentMailbox);
+  }
+}
+
+function onMailboxMenuMove(event) {
+  _onMailboxMenuAction(this,
+                      "Moving a message into its own folder is impossible!",
+                      "move");
 }
 
-function onMailboxMenuCopy() {
-  window.alert("unimplemented");
+function onMailboxMenuCopy(event) {
+  _onMailboxMenuAction(this,
+                      "Copying a message into its own folder is impossible!",
+                      "copy");
 }
 
 function refreshMailbox() {
@@ -439,6 +445,7 @@ function openMailboxAtIndex(event) {
 }
 
 function messageListCallback(http) {
+  var div = $('mailboxContent');
   var table = $('messageList');
   
   if (http.readyState == 4
@@ -456,7 +463,6 @@ function messageListCallback(http) {
     }
     else {
       // Add table
-      var div = $('mailboxContent');
       div.update(http.responseText);
       table = $('messageList');
       configureMessageListEvents(table);
@@ -467,11 +473,15 @@ function messageListCallback(http) {
     var selected = http.callbackData;
     if (selected) {
       var row = $("row_" + selected);
-      if (row)
+      if (row) {
        row.select();
+       div.scrollTop = row.rowIndex * row.getHeight(); // scroll to selected message
+      }
       else
        $("messageContent").update();
     }
+    else
+      div.scrollTop = 0;
     
     if (sorting["attribute"] && sorting["attribute"].length > 0) {
       var sortHeader = $(sorting["attribute"] + "Header");
@@ -1127,8 +1137,10 @@ function updateMailboxTreeInPage() {
   var tree = $("mailboxTree");
   var nodes = document.getElementsByClassName("node", tree);
   for (i = 0; i < nodes.length; i++) {
-    Event.observe(nodes[i], "click", onMailboxTreeItemClick.bindAsEventListener(nodes[i]));
-    Event.observe(nodes[i], "contextmenu", onFolderMenuClick.bindAsEventListener(nodes[i]));
+    Event.observe(nodes[i], "click",
+                 onMailboxTreeItemClick.bindAsEventListener(nodes[i]));
+    Event.observe(nodes[i], "contextmenu",
+                 onFolderMenuClick.bindAsEventListener(nodes[i]));
     if (!inboxFound
        && nodes[i].parentNode.getAttribute("datatype") == "inbox") {
       openInbox(nodes[i]);
@@ -1145,7 +1157,10 @@ function mailboxMenuNode(type, name) {
   var image = document.createElement("img");
   image.src = ResourcesURL + "/" + icon;
   newNode.appendChild(image);
-  newNode.appendChild(document.createTextNode(" " + name));
+  var displayName = MailerUIdTreeExtension.folderNames[type];
+  if (!displayName)
+    displayName = name;
+  newNode.appendChild(document.createTextNode(" " + displayName));
 
   return newNode;
 }
@@ -1156,12 +1171,13 @@ function generateMenuForMailbox(mailbox, prefix, callback) {
   menuDIV.setAttribute("id", prefix + "Submenu");
   var menu = document.createElement("ul");
   menuDIV.appendChild(menu);
+  pageContent.appendChild(menuDIV);
 
   var callbacks = new Array();
   if (mailbox.type != "account") {
     var newNode = document.createElement("li");
     newNode.mailbox = mailbox;
-    newNode.appendChild(document.createTextNode("coucou"));
+    newNode.appendChild(document.createTextNode(labels["This Folder"]));
     menu.appendChild(newNode);
     menu.appendChild(document.createElement("li"));
     callbacks.push(callback);
@@ -1175,11 +1191,8 @@ function generateMenuForMailbox(mailbox, prefix, callback) {
     menu.appendChild(newNode);
     if (child.children.length > 0) {
       var newPrefix = prefix + submenuCount;
-      var newSubmenu = generateMenuForMailbox(child,
-                                             newPrefix,
-                                             callback);
-      document.body.appendChild(newSubmenu);
-      callbacks.push(newPrefix + "Submenu");
+      var newSubmenuId = generateMenuForMailbox(child, newPrefix, callback);
+      callbacks.push(newSubmenuId);
       submenuCount++;
     }
     else {
@@ -1189,7 +1202,7 @@ function generateMenuForMailbox(mailbox, prefix, callback) {
   }
   initMenu(menuDIV, callbacks);
 
-  return menuDIV;
+  return menuDIV.getAttribute("id");
 }
 
 function updateMailboxMenus() {
@@ -1203,23 +1216,23 @@ function updateMailboxMenus() {
       menuDIV.parentNode.removeChild(menuDIV);
 
     menuDIV = document.createElement("div");
-    document.body.appendChild(menuDIV);
+    pageContent = $("pageContent");
+    pageContent.appendChild(menuDIV);
 
     var menu = document.createElement("ul");
     menuDIV.appendChild(menu);
 
     $(menuDIV).addClassName("menu");
     menuDIV.setAttribute("id", menuId);
-      
+
     var submenuIds = new Array();
     for (var i = 0; i < mailAccounts.length; i++) {
       var menuEntry = mailboxMenuNode("account", mailAccounts[i]);
       menu.appendChild(menuEntry);
       var mailbox = accounts[mailAccounts[i]];
-      var newSubmenu = generateMenuForMailbox(mailbox,
+      var newSubmenuId = generateMenuForMailbox(mailbox,
                                              key, mailboxActions[key]);
-      document.body.appendChild(newSubmenu);
-      submenuIds.push(newSubmenu.getAttribute("id"));
+      submenuIds.push(newSubmenuId);
     }
     initMenu(menuDIV, submenuIds);
   }
@@ -1338,6 +1351,34 @@ function onMenuEmptyTrash(event) {
     deleteCachedMessage(folderID + "/" + msgID);
 }
 
+function _onMenuChangeToXXXFolder(event, folder) {
+  var type = document.menuTarget.getAttribute("datatype");
+  if (type == "additional")
+    window.alert(labels["You need to choose a non-virtual folder!"]);
+  else {
+    var folderID = document.menuTarget.getAttribute("dataname");
+    var number = folderID.split("/").length;
+    if (number > 3)
+      window.alert(labels["You need to choose a root subfolder!"]);
+    else {
+      var urlstr = URLForFolderID(folderID) + "/setAs" + folder + "Folder";
+      triggerAjaxRequest(urlstr, folderOperationCallback);
+    }
+  }
+}
+
+function onMenuChangeToDraftsFolder(event) {
+  return _onMenuChangeToXXXFolder(event, "Drafts");
+}
+
+function onMenuChangeToSentFolder(event) {
+  return _onMenuChangeToXXXFolder(event, "Sent");
+}
+
+function onMenuChangeToTrashFolder(event) {
+  return _onMenuChangeToXXXFolder(event, "Trash");
+}
+
 function folderOperationCallback(http) {
   if (http.readyState == 4
       && isHttpStatus204(http.status))
@@ -1373,7 +1414,9 @@ function getMenus() {
                                       onMenuCreateFolder,
                                       onMenuRenameFolder,
                                       onMenuExpungeFolder,
-                                      onMenuDeleteFolder, "-", null,
+                                      onMenuDeleteFolder,
+                                      "folderTypeMenu",
+                                      "-", null,
                                       onMenuSharing);
   menus["addressMenu"] = new Array(newContactFromEmail, newEmailTo, null);
   menus["messageListMenu"] = new Array(onMenuOpenMessage, "-",
@@ -1395,6 +1438,10 @@ function getMenus() {
                                          null, onMenuViewMessageSource,
                                          null, onPrintCurrentMessage,
                                          onMenuDeleteMessage);
+  menus["folderTypeMenu"] = new Array(onMenuChangeToSentFolder,
+                                     onMenuChangeToDraftsFolder,
+                                     onMenuChangeToTrashFolder);
+  
   menus["label-menu"] = new Array(null, "-", null , null, null, null , null,
                                  null);
   menus["mark-menu"] = new Array(null, null, null, null, "-", null, "-",
@@ -1425,6 +1472,18 @@ Mailbox.prototype.dump = function(indent) {
   }
 }
 
+Mailbox.prototype.fullName = function() {
+  var fullName = "";
+
+  var currentFolder = this;
+  while (currentFolder.parentFolder) {
+    fullName = "/folder" + currentFolder.name + fullName;
+    currentFolder = currentFolder.parentFolder;
+  }
+
+  return "/" + currentFolder.name + fullName;
+}
+
 Mailbox.prototype.findMailboxByName = function(name) {
   var mailbox = null;
 
index 2a92d8f7e04edf818e26bbcfa2a63e0210f5330d..2ace43224c8368cad64f2da5253161feb3994e85 100644 (file)
@@ -990,7 +990,7 @@ function selectYearInMenu(menu, month) {
   var entries = menu.childNodes[1].childNodes;
   for (i = 0; i < entries.length; i++) {
     var entry = entries[i];
-    if (entry instanceof HTMLLIElement) {
+    if (entry.tagName == "LI") {
       var entryMonth = entry.innerHTML;
       if (entryMonth == month)
         entry.addClassName("currentMonth");
index b17720dad2d6011977514acda9a381e7d976c3be..23f334a7ae7dcad2384faa19a8ef5f8f9cd5180b 100644 (file)
@@ -99,8 +99,8 @@ function showCoords(node) {
   node = $("givenName");
   window.alert("x: " + node.cascadeLeftOffset()
                + ";y: " + node.cascadeTopOffset()
-               + ";width: " + window.innerWidth
-               + ";height: " + window.innerHeight);
+               + ";width: " + window.width()
+               + ";height: " + window.height());
 }
 
 function onFnKeyDown() {
@@ -133,3 +133,5 @@ function initEditorForm() {
   $("sn").onkeyup = onFnNewValue;
   $("givenName").onkeyup = onFnNewValue;
 }
+
+addEvent(window, 'load', initEditorForm);
index 26f7793b454c863eecf4f336c73141819195543b..9863b23dd8dc7c1218d5248a750e6a6f7ef558e7 100644 (file)
@@ -113,7 +113,7 @@ UL#attachments LI
 UL#attachments LI IMG
 { vertical-align: bottom; }
 
-DIV.pageContent TEXTAREA
+#pageContent TEXTAREA
 { position: absolute;
   left: 0em;
   right: 0em;
index d4374c8e1c8478f6d52f9721d894d1d85dede5e3..2fdc38526ebb7a8065b0af8f508dc45de7adaa33 100644 (file)
@@ -289,7 +289,6 @@ function onSelectAllAttachments() {
 
 function onWindowResize(event) {
   var textarea = document.pageform.text;
-  var windowheight = (typeof self.innerHeight == "number" ? self.innerHeight : document.body.clientHeight);
   var rowheight = (Element.getHeight(textarea) / textarea.rows);
   var headerarea = $("headerArea");
 
@@ -299,31 +298,31 @@ function onWindowResize(event) {
   var textareaoffset = textarea.offsetTop;
 
   // Resize the textarea (message content)
-  textarea.rows = Math.round((windowheight - textareaoffset) / rowheight);
+  textarea.rows = Math.round((window.height() - textareaoffset) / rowheight);
   
   var attachmentsarea = $("attachmentsArea");
   var attachmentswidth = 0;
   if (attachmentsarea.style.display)
     attachmentswidth = attachmentsarea.getWidth();
-  var windowwidth = (typeof self.innerWidth == "number" ? self.innerWidth : document.body.clientWidth);
-  var subjectfield = $(document).getElementsByClassName('headerField', $('subjectRow'))[0];
-  var subjectinput = $(document).getElementsByClassName('textField', $('subjectRow'))[0];
+  var subjectfield = $(document).getElementsByClassName('headerField',
+                                                       $('subjectRow'))[0];
+  var subjectinput = $(document).getElementsByClassName('textField',
+                                                       $('subjectRow'))[0];
 
   // Resize subject field
-  subjectinput.setStyle({ width: (windowwidth
+  subjectinput.setStyle({ width: (window.width()
                                  - $(subjectfield).getWidth()
                                  - attachmentswidth
-                                 - 4 - 30
-                                 ) + 'px' });
+                                 - 4 - 30) + 'px' });
 
   // Resize address fields
   var addresslist = $('addressList');
   var firstselect = document.getElementsByClassName('headerField', addresslist)[0];
-  var inputwidth = windowwidth - $(firstselect).getWidth() - attachmentswidth - 24 - 30;
+  var inputwidth = ($(this).width() - $(firstselect).getWidth()
+                   - attachmentswidth - 24 - 30);
   var addresses = document.getElementsByClassName('textField', addresslist);
-  for (var i = 0; i < addresses.length; i++) {
+  for (var i = 0; i < addresses.length; i++)
     addresses[i].setStyle({ width: inputwidth + 'px' });
-  }
 }
 
 function onMailEditorClose(event) {
index b38d529e28b555f3ca8d8508b74ba9c8f2020734..a8e6e15223d9014ca1143df83786d8dfdf77917b 100644 (file)
@@ -33,7 +33,7 @@ IMG#progressIndicator
   margin-top: 1.5em;
   margin-right: 1em; }
 
-DIV.pageContent
+DIV#pageContent
 { /* position: absolute;
   background-color: #ffa;
   top: 3em;
index 9fa35dedb528e6afcfd199dae2e896ab4252b903..fc73a4aec98b4464675c9f79b39becd629c79979 100644 (file)
@@ -108,26 +108,26 @@ function getElementsByClassName2(_tag, _class, _scope) {
 
 function createElement(tagName, id, classes, attributes, htmlAttributes,
                       parentNode) {
-   var newElement = $(document.createElement(tagName));
-   if (id)
-      newElement.setAttribute("id", id);
-   if (classes) {
-      if (typeof(classes) == "string")
-        newElement.addClassName(classes);
-      else
-        for (var i = 0; i < classes.length; i++)
-           newElement.addClassName(classes[i]);
-   }
-   if (attributes)
-      for (var i in attributes)
-        newElement[i] = attributes[i];
-   if (htmlAttributes)
-      for (var i in htmlAttributes)
-        newElement.setAttribute(i, htmlAttributes[i]);
-   if (parentNode)
-      parentNode.appendChild(newElement);
+  var newElement = $(document.createElement(tagName));
+  if (id)
+    newElement.setAttribute("id", id);
+  if (classes) {
+    if (typeof(classes) == "string")
+      newElement.addClassName(classes);
+    else
+      for (var i = 0; i < classes.length; i++)
+       newElement.addClassName(classes[i]);
+  }
+  if (attributes)
+    for (var i in attributes)
+      newElement[i] = attributes[i];
+  if (htmlAttributes)
+    for (var i in htmlAttributes)
+      newElement.setAttribute(i, htmlAttributes[i]);
+  if (parentNode)
+    parentNode.appendChild(newElement);
 
-   return $(newElement);
+  return $(newElement);
 }
 
 function ml_stripActionInURL(url) {
@@ -143,21 +143,21 @@ function ml_stripActionInURL(url) {
 }
 
 function URLForFolderID(folderID) {
-   var folderInfos = folderID.split(":");
-   var url;
-   if (folderInfos.length > 1) {
-      url = UserFolderURL + "../" + folderInfos[0];
-      if (!folderInfos[1].startsWith('/'))
-        url += '/';
-      url += folderInfos[1];
-   }
-   else
-      url = ApplicationBaseURL + folderInfos[0];
+  var folderInfos = folderID.split(":");
+  var url;
+  if (folderInfos.length > 1) {
+    url = UserFolderURL + "../" + folderInfos[0];
+    if (!folderInfos[1].startsWith('/'))
+      url += '/';
+    url += folderInfos[1];
+  }
+  else
+    url = ApplicationBaseURL + folderInfos[0];
    
-   if (url[url.length-1] == '/')
-      url = url.substr(0, url.length-1);
+  if (url[url.length-1] == '/')
+    url = url.substr(0, url.length-1);
 
-   return url;
+  return url;
 }
 
 function extractEmailAddress(mailTo) {
@@ -176,49 +176,49 @@ function extractEmailAddress(mailTo) {
 function extractEmailName(mailTo) {
   var emailName = "";
 
-   var tmpMailTo = mailTo.replace("&lt;", "<");
-   tmpMailTo = tmpMailTo.replace("&gt;", ">");
+  var tmpMailTo = mailTo.replace("&lt;", "<");
+  tmpMailTo = tmpMailTo.replace("&gt;", ">");
 
-   var emailNamere = /([       ]+)?(.+)\ </;
-   if (emailNamere.test(tmpMailTo)) {
-      emailNamere.exec(tmpMailTo);
-      emailName = RegExp.$2;
-   }
+  var emailNamere = /([        ]+)?(.+)\ </;
+  if (emailNamere.test(tmpMailTo)) {
+    emailNamere.exec(tmpMailTo);
+    emailName = RegExp.$2;
+  }
    
-   return emailName;
+  return emailName;
 }
 
 function sanitizeMailTo(dirtyMailTo) {
-   var emailName = extractEmailName(dirtyMailTo);
-   var email = "" + extractEmailAddress(dirtyMailTo);
+  var emailName = extractEmailName(dirtyMailTo);
+  var email = "" + extractEmailAddress(dirtyMailTo);
    
-   var mailto = "";
-   if (emailName && emailName.length > 0)
-      mailto = emailName + ' <' + email + '>';
-   else
-      mailto = email;
+  var mailto = "";
+  if (emailName && emailName.length > 0)
+    mailto = emailName + ' <' + email + '>';
+  else
+    mailto = email;
 
-   return mailto;
+  return mailto;
 }
 
 function openUserFolderSelector(callback, type) {
-   var urlstr = ApplicationBaseURL;
-   if (! urlstr.endsWith('/'))
-      urlstr += '/';
-   urlstr += ("../../" + UserLogin + "/Contacts/userFolders");
-   var w = window.open(urlstr, "_blank",
-                      "width=322,height=250,resizable=1,scrollbars=0");
-   w.opener = window;
-   window.userFolderCallback = callback;
-   window.userFolderType = type;
-   w.focus();
+  var urlstr = ApplicationBaseURL;
+  if (! urlstr.endsWith('/'))
+    urlstr += '/';
+  urlstr += ("../../" + UserLogin + "/Contacts/userFolders");
+  var w = window.open(urlstr, "_blank",
+                     "width=322,height=250,resizable=1,scrollbars=0,location=0");
+  w.opener = window;
+  window.userFolderCallback = callback;
+  window.userFolderType = type;
+  w.focus();
 }
 
 function openContactWindow(url, wId) {
   if (!wId)
     wId = "" + (new Date().getTime());
   var w = window.open(url, wId,
-                     "width=450,height=600,resizable=0");
+                     "width=450,height=600,resizable=0,location=0");
   w.focus();
 
   return w;
@@ -237,10 +237,10 @@ function openMailComposeWindow(url, wId) {
 }
 
 function openMailTo(senderMailTo) {
-   var mailto = sanitizeMailTo(senderMailTo);
-   if (mailto.length > 0)
-      openMailComposeWindow(ApplicationBaseURL
-                           + "/../Mail/compose?mailto=" + mailto);
+  var mailto = sanitizeMailTo(senderMailTo);
+  if (mailto.length > 0)
+    openMailComposeWindow(ApplicationBaseURL
+                         + "/../Mail/compose?mailto=" + mailto);
 
   return false; /* stop following the link */
 }
@@ -282,26 +282,26 @@ function triggerAjaxRequest(url, callback, userdata) {
     http.url = url;
     http.onreadystatechange
       = function() {
-        //log ("state changed (" + http.readyState + "): " + url);
-        try {
-          if (http.readyState == 4
-              && activeAjaxRequests > 0) {
-                if (!http.aborted) {
-                  http.callbackData = userdata;
-                  callback(http);
-                }
-                activeAjaxRequests -= 1;
-                checkAjaxRequestsState();
-              }
-        }
-        catch( e ) {
-          activeAjaxRequests -= 1;
-          checkAjaxRequestsState();
-          log("AJAX Request, Caught Exception: " + e.name);
-          log(e.message);
-         log(backtrace());
-        }
-      };
+      //log ("state changed (" + http.readyState + "): " + url);
+      try {
+       if (http.readyState == 4
+           && activeAjaxRequests > 0) {
+         if (!http.aborted) {
+           http.callbackData = userdata;
+           callback(http);
+         }
+         activeAjaxRequests -= 1;
+         checkAjaxRequestsState();
+       }
+      }
+      catch( e ) {
+       activeAjaxRequests -= 1;
+       checkAjaxRequestsState();
+       log("AJAX Request, Caught Exception: " + e.name);
+       log(e.message);
+       log(backtrace());
+      }
+    };
     http.send(null);
   }
   else {
@@ -396,7 +396,7 @@ function refreshOpener() {
 
 function parseQueryString() {
   var queryArray, queryDict
-  var key, value, s, idx;
+    var key, value, s, idx;
   queryDict.length = 0;
   
   queryDict  = new Array();
@@ -487,17 +487,17 @@ function isNodeSelected(node) {
 }
 
 function acceptMultiSelect(node) {
-   var response = false;
-   var attribute = node.getAttribute('multiselect');
-   if (attribute && attribute.length > 0) {
-      log("node '" + node.getAttribute("id")
-         + "' is still using old-stylemultiselect!");
-      response = (attribute.toLowerCase() == 'yes');
-   }
-   else
-      response = node.multiselect;
+  var response = false;
+  var attribute = node.getAttribute('multiselect');
+  if (attribute && attribute.length > 0) {
+    log("node '" + node.getAttribute("id")
+       + "' is still using old-stylemultiselect!");
+    response = (attribute.toLowerCase() == 'yes');
+  }
+  else
+    response = node.multiselect;
 
-   return response;
+  return response;
 }
 
 function onRowClick(event) {
@@ -567,33 +567,33 @@ function onRowClick(event) {
 // var acceptClick = false;
 
 function popupMenu(event, menuId, target) {
-   document.menuTarget = target;
-
-   if (document.currentPopupMenu)
-      hideMenu(document.currentPopupMenu);
-
-   var popup = $(menuId);
-   var menuTop =  Event.pointerY(event);
-   var menuLeft = Event.pointerX(event);
-   var heightDiff = (window.innerHeight
-                    - (menuTop + popup.offsetHeight));
-   if (heightDiff < 0)
-      menuTop += heightDiff;
+  document.menuTarget = target;
+
+  if (document.currentPopupMenu)
+    hideMenu(document.currentPopupMenu);
+
+  var popup = $(menuId);
+  var menuTop = Event.pointerY(event);
+  var menuLeft = Event.pointerX(event);
+  var heightDiff = (window.height()
+                   - (menuTop + popup.offsetHeight));
+  if (heightDiff < 0)
+    menuTop += heightDiff;
   
-   var leftDiff = (window.innerWidth
-                  - (menuLeft + popup.offsetWidth));
-   if (leftDiff < 0)
-      menuLeft -= popup.offsetWidth;
+  var leftDiff = (window.width()
+                 - (menuLeft + popup.offsetWidth));
+  if (leftDiff < 0)
+    menuLeft -= popup.offsetWidth;
 
-   popup.setStyle({ top: menuTop + "px",
-                   left: menuLeft + "px",
-                   visibility: "visible" });
+  popup.setStyle({ top: menuTop + "px",
+       left: menuLeft + "px",
+       visibility: "visible" });
 
-   document.currentPopupMenu = popup;
+  document.currentPopupMenu = popup;
 
-   Event.observe(document.body, "click", onBodyClickMenuHandler);
+  Event.observe(document.body, "click", onBodyClickMenuHandler);
 
-   preventDefault(event);
+  preventDefault(event);
 }
 
 function getParentMenu(node) {
@@ -614,11 +614,11 @@ function getParentMenu(node) {
 }
 
 function onBodyClickMenuHandler(event) {
-   document.body.menuTarget = null;
-   hideMenu(document.currentPopupMenu);
-   Event.stopObserving(document.body, "click", onBodyClickMenuHandler);
+  document.body.menuTarget = null;
+  hideMenu(document.currentPopupMenu);
+  Event.stopObserving(document.body, "click", onBodyClickMenuHandler);
 
-   preventDefault(event);
+  preventDefault(event);
 }
 
 function hideMenu(menuNode) {
@@ -632,9 +632,16 @@ function hideMenu(menuNode) {
   menuNode.setStyle({ visibility: "hidden" });
   //   menuNode.hide();
   if (menuNode.parentMenuItem) {
-    menuNode.parentMenuItem.setAttribute('class', 'submenu');
+    Event.stopObserving(menuNode.parentMenuItem, "mouseover",
+                       onMouseEnteredSubmenu);
+    Event.stopObserving(menuNode, "mouseover", onMouseEnteredSubmenu);
+    Event.stopObserving(menuNode.parentMenuItem, "mouseout", onMouseLeftSubmenu);
+    Event.stopObserving(menuNode, "mouseout", onMouseLeftSubmenu);
+    Event.stopObserving(menuNode.parentMenu, "mouseover",
+                       onMouseEnteredParentMenu);
+    $(menuNode.parentMenuItem).removeClassName("submenu-selected");
+    menuNode.parentMenuItem.mouseInside = false;
     menuNode.parentMenuItem = null;
-    Event.stopObserving(menuNode, 'mousemove', checkDropDown);
     menuNode.parentMenu.submenuItem = null;
     menuNode.parentMenu.submenu = null;
     menuNode.parentMenu = null;
@@ -678,20 +685,20 @@ function parseQueryParameters(url) {
 }
 
 function initLogConsole() {
-    var logConsole = $("logConsole");
-    if (logConsole) {
-       logConsole.highlighted = false;
-       Event.observe(logConsole, "dblclick", onLogDblClick, false);
-       logConsole.innerHTML = "";
-       Event.observe(window, "keydown", onBodyKeyDown);
-    }
+  var logConsole = $("logConsole");
+  if (logConsole) {
+    logConsole.highlighted = false;
+    Event.observe(logConsole, "dblclick", onLogDblClick, false);
+    logConsole.innerHTML = "";
+    Event.observe(window, "keydown", onBodyKeyDown);
+  }
 }
 
 function onBodyKeyDown(event) {
-    if (event.keyCode == 27) {
-       toggleLogConsole();
-       preventDefault(event);
-    }
+  if (event.keyCode == 27) {
+    toggleLogConsole();
+    preventDefault(event);
+  }
 }
 
 function onLogDblClick(event) {
@@ -708,7 +715,7 @@ function toggleLogConsole(event) {
     logConsole.setStyle({ display: '' });
   }
   if (event)
-      preventDefault(event);
+    preventDefault(event);
 }
 
 function log(message) {
@@ -719,100 +726,95 @@ function log(message) {
   }
   var logConsole = logWindow.document.getElementById("logConsole");
   if (logConsole) {
-      logConsole.highlighted = !logConsole.highlighted;
-      if (message == '\c') {
-       logConsole.innerHTML = "";
-       return;
-      }
-      var logMessage = message.replace("<", "&lt;", "g");
-      logMessage = logMessage.replace(" ", "&nbsp;", "g");
-      logMessage = logMessage.replace("\r\n", "<br />\n", "g");
-      logMessage = logMessage.replace("\n", "<br />\n", "g");
-      logMessage += '<br />' + "\n";
-      if (logConsole.highlighted)
-         logMessage = '<div class="highlighted">' + logMessage + '</div>';
-      logConsole.innerHTML += logMessage;
+    logConsole.highlighted = !logConsole.highlighted;
+    if (message == '\c') {
+      logConsole.innerHTML = "";
+      return;
+    }
+    var logMessage = message.replace("<", "&lt;", "g");
+    logMessage = logMessage.replace(" ", "&nbsp;", "g");
+    logMessage = logMessage.replace("\r\n", "<br />\n", "g");
+    logMessage = logMessage.replace("\n", "<br />\n", "g");
+    logMessage += '<br />' + "\n";
+    if (logConsole.highlighted)
+      logMessage = '<div class="highlighted">' + logMessage + '</div>';
+    logConsole.innerHTML += logMessage;
   }
 }
 
 function backtrace() {
-   var func = backtrace.caller;
-   var str = "backtrace:\n";
+  var func = backtrace.caller;
+  var str = "backtrace:\n";
 
-   while (func)
-   {
+  while (func)
+    {
       if (func.name)
-      {
-         str += "  " + func.name;
-         if (this)
+       {
+         str += "  " + func.name;
+         if (this)
             str += " (" + this + ")";
-      }
+       }
       else
-         str += "[anonymous]\n";
+       str += "[anonymous]\n";
 
       str += "\n";
       func = func.caller;
-   }
-   str += "--\n";
-
-   return str;
-}
-
-function dropDownSubmenu(event) {
-   var node = this;
-   if (this.submenu && this.submenu != "") {
-      log ("submenu: " + this.submenu);
-      var submenuNode = $(this.submenu);
-      var parentNode = getParentMenu(node);
-      if (parentNode.submenu)
-        hideMenu(parentNode.submenu);
-      submenuNode.parentMenuItem = node;
-      submenuNode.parentMenu = parentNode;
-      parentNode.submenuItem = node;
-      parentNode.submenu = submenuNode;
-      
-      var menuTop = (node.offsetTop - 2);
-      
-      var heightDiff = (window.innerHeight
-                       - (menuTop + submenuNode.offsetHeight));
-      if (heightDiff < 0)
-        menuTop += heightDiff;
-      
-      var menuLeft = parentNode.offsetWidth - 3;
-      if (window.innerWidth
-         < (menuLeft + submenuNode.offsetWidth
-            + parentNode.cascadeLeftOffset()))
-        menuLeft = - submenuNode.offsetWidth + 3;
-      
-      Event.observe(parentNode, "mousemove", checkDropDown);
-      node.setAttribute('class', 'submenu-selected');
-      submenuNode.setStyle({ top: menuTop + "px",
-                                    left: menuLeft + "px",
-                                    visibility: "visible" });
-   }
-}
-
-function checkDropDown(event) {
-  var parentMenu = getParentMenu(event.target);
-  var submenuItem = parentMenu.submenuItem;
-  if (submenuItem) {
-    var menuX = event.clientX - parentMenu.cascadeLeftOffset();
-    var menuY = event.clientY - parentMenu.cascadeTopOffset();
-    var itemX = submenuItem.offsetLeft;
-    var itemY = submenuItem.offsetTop - 75;
-
-    if (menuX >= itemX
-        && menuX < itemX + submenuItem.offsetWidth
-        && (menuY < itemY
-            || menuY > (itemY + submenuItem.offsetHeight))) {
-      hideMenu(parentMenu.submenu);
-      parentMenu.submenu = null;
-      parentMenu.submenuItem = null;
-      Event.stopObserving(parentMenu, 'mousemove', checkDropDown);
     }
+  str += "--\n";
+
+  return str;
+}
+
+function popupSubmenu(event) {
+  if (this.submenu && this.submenu != "") {
+    var submenuNode = $(this.submenu);
+    var parentNode = getParentMenu(this);
+    if (parentNode.submenu)
+      hideMenu(parentNode.submenu);
+    submenuNode.parentMenuItem = this;
+    submenuNode.parentMenu = parentNode;
+    parentNode.submenuItem = this;
+    parentNode.submenu = submenuNode;
+
+    var menuTop = (parentNode.offsetTop - 2 + this.offsetTop);
+    if (window.height() < (menuTop + submenuNode.offsetHeight))
+      menuTop = window.height() - submenuNode.offsetHeight - 3;
+    var menuLeft = (parentNode.offsetLeft + parentNode.offsetWidth - 3);
+    if (window.width()
+       < (menuLeft + submenuNode.offsetWidth))
+      menuLeft = parentNode.offsetLeft - submenuNode.offsetWidth + 3;
+
+    this.mouseInside = true;
+    Event.observe(this, "mouseover",
+                 onMouseEnteredSubmenu.bindAsEventListener(this));
+    Event.observe(submenuNode, "mouseover",
+                 onMouseEnteredSubmenu.bindAsEventListener(submenuNode));
+    Event.observe(this, "mouseout", onMouseLeftSubmenu.bindAsEventListener(this));
+    Event.observe(submenuNode, "mouseout",
+                 onMouseLeftSubmenu.bindAsEventListener(submenuNode));
+    Event.observe(parentNode, "mouseover",
+                 onMouseEnteredParentMenu.bindAsEventListener(parentNode));
+    $(this).addClassName("submenu-selected");
+    submenuNode.setStyle({ top: menuTop + "px",
+         left: menuLeft + "px",
+         visibility: "visible" });
+    preventDefault(event);
   }
 }
 
+function onMouseEnteredParentMenu(event) {
+  if (this.submenuItem && !this.submenuItem.mouseInside)
+    hideMenu(this.submenu);
+}
+
+function onMouseEnteredSubmenu(event) {
+  $(this).mouseInside = true;
+}
+
+function onMouseLeftSubmenu(event) {
+  $(this).mouseInside = false;
+}
+
 /* search field */
 function popupSearchMenu(event) {
   var menuId = this.getAttribute("menuid");
@@ -832,8 +834,8 @@ function popupSearchMenu(event) {
     var popup = $(menuId);
     offset = Position.positionedOffset(this);
     popup.setStyle({ top: this.offsetHeight + "px",
-                   left: (offset[0] + 3) + "px",
-                           visibility: "visible" });
+         left: (offset[0] + 3) + "px",
+         visibility: "visible" });
   
     document.currentPopupMenu = popup;
     Event.observe(document.body, "click", onBodyClickMenuHandler);
@@ -867,39 +869,39 @@ function onSearchChange() {
 }
 
 function configureSearchField() {
-   var searchValue = $("searchValue");
-   var searchOptions = $("searchOptions");
-
-   if (!searchValue) return;
-
-   Event.observe(searchValue, "mousedown",
-                onSearchMouseDown.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "click",
-                popupSearchMenu.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "blur",
-                onSearchBlur.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "focus",
-                onSearchFocus.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "keydown",
-                onSearchKeyDown.bindAsEventListener(searchValue));
-
-   if (!searchOptions) return;
+  var searchValue = $("searchValue");
+  var searchOptions = $("searchOptions");
+
+  if (!searchValue) return;
+
+  Event.observe(searchValue, "mousedown",
+               onSearchMouseDown.bindAsEventListener(searchValue));
+  Event.observe(searchValue, "click",
+               popupSearchMenu.bindAsEventListener(searchValue));
+  Event.observe(searchValue, "blur",
+               onSearchBlur.bindAsEventListener(searchValue));
+  Event.observe(searchValue, "focus",
+               onSearchFocus.bindAsEventListener(searchValue));
+  Event.observe(searchValue, "keydown",
+               onSearchKeyDown.bindAsEventListener(searchValue));
+
+  if (!searchOptions) return;
    
-   // Set the checkmark to the first option
-   var firstOption = searchOptions.down('li');
-   firstOption.addClassName("_chosen");
-   searchOptions.chosenNode = firstOption;
+  // Set the checkmark to the first option
+  var firstOption = searchOptions.down('li');
+  firstOption.addClassName("_chosen");
+  searchOptions.chosenNode = firstOption;
 }
 
 function onSearchMouseDown(event) {
-   var superNode = this.parentNode.parentNode.parentNode;
-   relX = (Event.pointerX(event) - superNode.offsetLeft - this.offsetLeft);
-   relY = (Event.pointerY(event) - superNode.offsetTop - this.offsetTop);
+  var superNode = this.parentNode.parentNode.parentNode;
+  relX = (Event.pointerX(event) - superNode.offsetLeft - this.offsetLeft);
+  relY = (Event.pointerY(event) - superNode.offsetTop - this.offsetTop);
 
-   if (relY < 24) {
-      event.cancelBubble = true;
-      event.returnValue = false;
-   }
+  if (relY < 24) {
+    event.cancelBubble = true;
+    event.returnValue = false;
+  }
 }
 
 function onSearchFocus() {
@@ -915,9 +917,9 @@ function onSearchFocus() {
 }
 
 function onSearchBlur(event) {
-   var ghostPhrase = this.getAttribute("ghost-phrase");
+  var ghostPhrase = this.getAttribute("ghost-phrase");
 
-   if (!this.value) {
+  if (!this.value) {
     this.setAttribute("modified", "");
     this.setStyle({ color: "#aaa" });
     this.value = ghostPhrase;
@@ -939,16 +941,16 @@ function onSearchKeyDown(event) {
 }
 
 function onSearchFormSubmit(event) {
-   var searchValue = $("searchValue");
-   var searchCriteria = $("searchCriteria");
-   var ghostPhrase = searchValue.getAttribute('ghost-phrase');
+  var searchValue = $("searchValue");
+  var searchCriteria = $("searchCriteria");
+  var ghostPhrase = searchValue.getAttribute('ghost-phrase');
    
-   if (searchValue.value == ghostPhrase) return;
+  if (searchValue.value == ghostPhrase) return;
 
-   search["criteria"] = searchCriteria.value;
-   search["value"] = searchValue.value;
+  search["criteria"] = searchCriteria.value;
+  search["value"] = searchValue.value;
 
-   refreshCurrentFolder();
+  refreshCurrentFolder();
 }
 
 function initCriteria() {
@@ -972,67 +974,67 @@ function initCriteria() {
 
 /* toolbar buttons */
 function popupToolbarMenu(node, menuId) {
-   if (document.currentPopupMenu)
-      hideMenu(document.currentPopupMenu);
+  if (document.currentPopupMenu)
+    hideMenu(document.currentPopupMenu);
 
-   var popup = $(menuId);
-   var top = ($(node).getStyle('top') || 0) + node.offsetHeight - 2;
-   popup.setStyle({ top: top + "px",
-                   left: $(node).cascadeLeftOffset() + "px",
-                   visibility: "visible" });
+  var popup = $(menuId);
+  var top = ($(node).getStyle('top') || 0) + node.offsetHeight - 2;
+  popup.setStyle({ top: top + "px",
+       left: $(node).cascadeLeftOffset() + "px",
+       visibility: "visible" });
 
-   document.currentPopupMenu = popup;
-   Event.observe(document.body, "click", onBodyClickMenuHandler);
+  document.currentPopupMenu = popup;
+  Event.observe(document.body, "click", onBodyClickMenuHandler);
 }
 
 /* contact selector */
 
 function folderSubscriptionCallback(http) {
-   if (http.readyState == 4) {
-      if (isHttpStatus204(http.status)) {
-        if (http.callbackData)
-           http.callbackData["method"](http.callbackData["data"]);
-      }
-      else
-        window.alert(clabels["Unable to subscribe to that folder!"].decodeEntities());
-      document.subscriptionAjaxRequest = null;
-   }
-   else
-      log ("folderSubscriptionCallback Ajax error");
+  if (http.readyState == 4) {
+    if (isHttpStatus204(http.status)) {
+      if (http.callbackData)
+       http.callbackData["method"](http.callbackData["data"]);
+    }
+    else
+      window.alert(clabels["Unable to subscribe to that folder!"]);
+    document.subscriptionAjaxRequest = null;
+  }
+  else
+    log ("folderSubscriptionCallback Ajax error");
 }
 
 function subscribeToFolder(refreshCallback, refreshCallbackData) {
-   var folderData = refreshCallbackData["folder"].split(":");
-   var username = folderData[0];
-   var folderPath = folderData[1];
-   if (username != UserLogin) {
-      var url = (UserFolderURL + "../" + username
-                 + folderPath + "/subscribe");
-      if (document.subscriptionAjaxRequest) {
-        document.subscriptionAjaxRequest.aborted = true;
-        document.subscriptionAjaxRequest.abort();
-      }
+  var folderData = refreshCallbackData["folder"].split(":");
+  var username = folderData[0];
+  var folderPath = folderData[1];
+  if (username != UserLogin) {
+    var url = (UserFolderURL + "../" + username
+              + folderPath + "/subscribe");
+    if (document.subscriptionAjaxRequest) {
+      document.subscriptionAjaxRequest.aborted = true;
+      document.subscriptionAjaxRequest.abort();
+    }
 
-      var rfCbData = { method: refreshCallback, data: refreshCallbackData };
-      document.subscriptionAjaxRequest = triggerAjaxRequest(url,
-                                                           folderSubscriptionCallback,
-                                                           rfCbData);
-   }
-   else
-      refreshCallbackData["window"].alert(clabels["You cannot subscribe to a folder that you own!"]
-                  .decodeEntities());
+    var rfCbData = { method: refreshCallback, data: refreshCallbackData };
+    document.subscriptionAjaxRequest = triggerAjaxRequest(url,
+                                                         folderSubscriptionCallback,
+                                                         rfCbData);
+  }
+  else
+    refreshCallbackData["window"].alert(clabels["You cannot subscribe to a folder that you own!"]
+                                       );
 }
 
 function folderUnsubscriptionCallback(http) {
-   if (http.readyState == 4) {
-      if (isHttpStatus204(http.status)) {
-        if (http.callbackData)
-           http.callbackData["method"](http.callbackData["data"]);
-      }
-      else
-        window.alert(clabels["Unable to unsubscribe from that folder!"].decodeEntities());
-      document.unsubscriptionAjaxRequest = null;
-   }
+  if (http.readyState == 4) {
+    if (isHttpStatus204(http.status)) {
+      if (http.callbackData)
+       http.callbackData["method"](http.callbackData["data"]);
+    }
+    else
+      window.alert(clabels["Unable to unsubscribe from that folder!"]);
+    document.unsubscriptionAjaxRequest = null;
+  }
 }
 
 function unsubscribeFromFolder(folder, refreshCallback, refreshCallbackData) {
@@ -1056,7 +1058,7 @@ function unsubscribeFromFolder(folder, refreshCallback, refreshCallbackData) {
                             rfCbData);
     }
     else
-      window.alert(clabels["You cannot unsubscribe from a folder that you own!"].decodeEntities());
+      window.alert(clabels["You cannot unsubscribe from a folder that you own!"]);
   }
 }
 
@@ -1095,16 +1097,16 @@ function initTabs() {
     
     firstTab = null;
     for (var i = 0; i < nodes.length; i++) {
-       var currentNode = nodes[i];
-       if (currentNode.tagName == 'LI') {
-           if (!firstTab)
-               firstTab = i;
-           Event.observe(currentNode, "mousedown",
-                         onTabMouseDown.bindAsEventListener(currentNode));
-           Event.observe(currentNode, "click",
-                         onTabClick.bindAsEventListener(currentNode));
-           //$(currentNode.getAttribute("target")).hide();
-       }
+      var currentNode = nodes[i];
+      if (currentNode.tagName == 'LI') {
+       if (!firstTab)
+         firstTab = i;
+       Event.observe(currentNode, "mousedown",
+                     onTabMouseDown.bindAsEventListener(currentNode));
+       Event.observe(currentNode, "click",
+                     onTabClick.bindAsEventListener(currentNode));
+       //$(currentNode.getAttribute("target")).hide();
+      }
     }
 
     nodes[firstTab].addClassName("first");
@@ -1118,39 +1120,44 @@ function initTabs() {
 }
 
 function initMenus() {
-   var menus = getMenus();
-   if (menus) {
-      for (var menuID in menus) {
-        var menuDIV = $(menuID);
-        if (menuDIV)
-           initMenu(menuDIV, menus[menuID]);
-      }
-   }
+  var menus = getMenus();
+  if (menus) {
+    for (var menuID in menus) {
+      var menuDIV = $(menuID);
+      if (menuDIV)
+       initMenu(menuDIV, menus[menuID]);
+    }
+  }
 }
 
 function initMenu(menuDIV, callbacks) {
-   var lis = $(menuDIV.childNodesWithTag("ul")[0]).childNodesWithTag("li");
-   for (var j = 0; j < lis.length; j++) {
-      var node = $(lis[j]);
-      Event.observe(node, "mousedown", listRowMouseDownHandler, false);
-      var callback = callbacks[j];
-      if (callback) {
-        if (typeof(callback) == "string") {
-           if (callback == "-")
-              node.addClassName("separator");
-           else {
-              node.submenu = callback;
-              node.addClassName("submenu");
-              Event.observe(node, "mouseover", dropDownSubmenu);
-           }
-        }
-        else
-           Event.observe(node, "mouseup",
-                         $(callback).bindAsEventListener(node));
+  var lis = $(menuDIV.childNodesWithTag("ul")[0]).childNodesWithTag("li");
+  for (var j = 0; j < lis.length; j++) {
+    var node = $(lis[j]);
+    Event.observe(node, "mousedown",
+                 listRowMouseDownHandler.bindAsEventListener(node),
+                 false);
+    var callback = callbacks[j];
+    if (callback) {
+      if (typeof(callback) == "string") {
+       if (callback == "-") {
+         node.addClassName("separator");
+         node.appendChild(document.createElement("hr"));
+       }
+       else {
+         node.submenu = callback;
+         node.addClassName("submenu");
+         Event.observe(node, "mouseover",
+                       popupSubmenu.bindAsEventListener(node));
+       }
       }
       else
-        node.addClassName("disabled");
-   }
+       Event.observe(node, "mouseup",
+                     $(callback).bindAsEventListener(node));
+    }
+    else
+      node.addClassName("disabled");
+  }
 }
 
 function onTabMouseDown(event) {
@@ -1174,25 +1181,25 @@ function openAclWindow(url) {
 }
 
 function getUsersRightsWindowHeight() {
-   return usersRightsWindowHeight;
+  return usersRightsWindowHeight;
 }
 
 function getUsersRightsWindowWidth() {
-   return usersRightsWindowWidth;
+  return usersRightsWindowWidth;
 }
 
 function getTopWindow() {
-   var topWindow = null;
-   var currentWindow = window;
-   while (!topWindow) {
-      if (currentWindow.document.body.hasClassName("popup")
-         && currentWindow.opener)
-        currentWindow = currentWindow.opener;
-      else
-        topWindow = currentWindow;
-   }
+  var topWindow = null;
+  var currentWindow = window;
+  while (!topWindow) {
+    if (currentWindow.document.body.hasClassName("popup")
+       && currentWindow.opener)
+      currentWindow = currentWindow.opener;
+    else
+      topWindow = currentWindow;
+  }
 
-   return topWindow;
+  return topWindow;
 }
 
 function onTabClick(event) {
@@ -1287,12 +1294,12 @@ function indexColor(number) {
     var currentValue = number;
     var index = 0;
     while (currentValue) {
-       if (currentValue & 1)
-          colorTable[index]++;
-       if (index == 3)
-         index = 0;
-       currentValue >>= 1;
-       index++;
+      if (currentValue & 1)
+       colorTable[index]++;
+      if (index == 3)
+       index = 0;
+      currentValue >>= 1;
+      index++;
     }
     
     color = ("#"
@@ -1305,18 +1312,18 @@ function indexColor(number) {
 }
 
 function loadPreferences() {
-   var url = UserFolderURL + "jsonDefaults";
-   var http = createHTTPClient();
-   http.open("GET", url, false);
-   http.send("");
-   if (http.status == 200)
-      userDefaults = http.responseText.evalJSON(true);
+  var url = UserFolderURL + "jsonDefaults";
+  var http = createHTTPClient();
+  http.open("GET", url, false);
+  http.send("");
+  if (http.status == 200)
+    userDefaults = http.responseText.evalJSON(true);
 
-   url = UserFolderURL + "jsonSettings";
-   http.open("GET", url, false);
-   http.send("");
-   if (http.status == 200)
-      userSettings = http.responseText.evalJSON(true);
+  url = UserFolderURL + "jsonSettings";
+  http.open("GET", url, false);
+  http.send("");
+  if (http.status == 200)
+    userSettings = http.responseText.evalJSON(true);
 }
 
 function onLoadHandler(event) {
@@ -1332,35 +1339,22 @@ function onLoadHandler(event) {
   initTabs();
   configureDragHandles();
   configureLinkBanner();
-  translateLabels();
   var progressImage = $("progressIndicator");
   if (progressImage)
     progressImage.parentNode.removeChild(progressImage);
   Event.observe(document.body, "contextmenu", onBodyClickContextMenu);
 }
 
-function translateLabels() {
-  if (typeof labels != "undefined") {
-    for (var key in labels)
-      labels[key] = labels[key].decodeEntities();
-  }
-
-  if (typeof clabels != "undefined") {
-    for (var key in clabels)
-      clabels[key] = clabels[key].decodeEntities();
-  }
-}
-
 function onBodyClickContextMenu(event) {
-   preventDefault(event);
+  preventDefault(event);
 }
 
 function configureSortableTableHeaders(table) {
-   var headers = $(table).getElementsByClassName("sortableTableHeader");
-   for (var i = 0; i < headers.length; i++) {
-      var header = headers[i];
-      Event.observe(header, "click", onHeaderClick.bindAsEventListener(header))
-   }
+  var headers = $(table).getElementsByClassName("sortableTableHeader");
+  for (var i = 0; i < headers.length; i++) {
+    var header = headers[i];
+    Event.observe(header, "click", onHeaderClick.bindAsEventListener(header))
+      }
 }
 
 function onLinkBannerClick() {
@@ -1369,13 +1363,13 @@ function onLinkBannerClick() {
 }
 
 function onPreferencesClick(event) {
-   var urlstr = UserFolderURL + "preferences";
-   var w = window.open(urlstr, "_blank",
-                      "width=430,height=250,resizable=0,scrollbars=0");
-   w.opener = window;
-   w.focus();
+  var urlstr = UserFolderURL + "preferences";
+  var w = window.open(urlstr, "_blank",
+                     "width=430,height=250,resizable=0,scrollbars=0,location=0");
+  w.opener = window;
+  w.focus();
 
-   preventDefault(event);
+  preventDefault(event);
 }
 
 function configureLinkBanner() {
@@ -1383,13 +1377,13 @@ function configureLinkBanner() {
   if (linkBanner) {
     var anchors = linkBanner.childNodesWithTag("a");
     for (var i = 1; i < 3; i++) {
-       Event.observe(anchors[i], "mousedown", listRowMouseDownHandler);
-       Event.observe(anchors[i], "click", onLinkBannerClick);
+      Event.observe(anchors[i], "mousedown", listRowMouseDownHandler);
+      Event.observe(anchors[i], "click", onLinkBannerClick);
     }
     Event.observe(anchors[4], "mousedown", listRowMouseDownHandler);
     Event.observe(anchors[4], "click", onPreferencesClick);
     if (anchors.length > 5)
-       Event.observe(anchors[5], "click", toggleLogConsole);
+      Event.observe(anchors[5], "click", toggleLogConsole);
   }
 }
 
@@ -1402,10 +1396,10 @@ function createFolder(name, okCB, notOkCB) {
     }
     var url = ApplicationBaseURL + "/createFolder?name=" + name;
     document.newFolderAjaxRequest
-       = triggerAjaxRequest(url, createFolderCallback,
-                           {name: name,
-                            okCB: okCB,
-                            notOkCB: notOkCB});
+      = triggerAjaxRequest(url, createFolderCallback,
+                          {name: name,
+                           okCB: okCB,
+                           notOkCB: notOkCB});
   }
 }
 
@@ -1428,7 +1422,7 @@ function createFolderCallback(http) {
 addEvent(window, 'load', onLoadHandler);
 
 function parent$(element) {
-   return this.opener.document.getElementById(element);
+  return this.opener.document.getElementById(element);
 }
 
 /* stubs */
@@ -1442,5 +1436,5 @@ function getMenus() {
 }
 
 function onHeaderClick(event) {
-   window.alert("generic headerClick");
+  window.alert("generic headerClick");
 }
index cda8431a44ece941ea752adbeca504ba9136f556..b51ae9e31c9258819d2af5fd8453f2a1724a26b2 100644 (file)
-/*\r
-*\r
-* Copyright (c) 2007 Andrew Tetlaw & Millstream Web Software\r
-* http://www.millstream.com.au/view/code/tablekit/\r
-* Version: 1.2.1 2007-03-11\r
-* \r
-* Permission is hereby granted, free of charge, to any person\r
-* obtaining a copy of this software and associated documentation\r
-* files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy,\r
-* modify, merge, publish, distribute, sublicense, and/or sell copies\r
-* of the Software, and to permit persons to whom the Software is\r
-* furnished to do so, subject to the following conditions:\r
-* \r
-* The above copyright notice and this permission notice shall be\r
-* included in all copies or substantial portions of the Software.\r
-* \r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
-* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
-* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-* SOFTWARE.\r
-* * \r
-*/\r
-\r
-// Use the TableKit class constructure if you'd prefer to init your tables as JS objects\r
-var TableKit = Class.create();\r
-\r
-TableKit.prototype = {\r
-       initialize : function(elm, options) {\r
-               var table = $(elm);\r
-               if(table.tagName !== "TABLE") {\r
-                       return;\r
-               }\r
-               TableKit.register(table,Object.extend(TableKit.options,options || {}));\r
-               this.id = table.id;\r
-               var op = TableKit.option('sortable resizable editable', this.id);\r
-               if(op.sortable) {\r
-                       TableKit.Sortable.init(table);\r
-               } \r
-               if(op.resizable) {\r
-                       TableKit.Resizable.init(table);\r
-               }\r
-               if(op.editable) {\r
-                       TableKit.Editable.init(table);\r
-               }\r
-       },\r
-       sort : function(column, order) {\r
-               TableKit.Sortable.sort(this.id, column, order);\r
-       },\r
-       resizeColumn : function(column, w) {\r
-               TableKit.Resizable.resize(this.id, column, w);\r
-       },\r
-       editCell : function(row, column) {\r
-               TableKit.Editable.editCell(this.id, row, column);\r
-       }\r
-};\r
-\r
-Object.extend(TableKit, {\r
-       getBodyRows : function(table) {\r
-               table = $(table);\r
-               var id = table.id;\r
-               if(!TableKit.rows[id]) {\r
-                       TableKit.rows[id] = (table.tHead && table.tHead.rows.length > 0) ? $A(table.tBodies[0].rows) : $A(table.rows).without(table.rows[0]);\r
-               }\r
-               return TableKit.rows[id];\r
-       },\r
-       getHeaderCells : function(table, cell) {\r
-               if(!table) { table = $(cell).up('table'); }\r
-               var id = table.id;\r
-               if(!TableKit.heads[id]) {\r
-                       //TableKit.heads[id] = $A((table.tHead && table.tHead.rows.length > 0) ? table.tHead.rows[table.tHead.rows.length-1].cells : table.rows[0].cells);\r
-                       TableKit.heads[id] = $A((table.tHead && table.tHead.rows.length > 0) ? table.tHead.rows[0].cells : table.rows[0].cells);\r
-               }\r
-               return TableKit.heads[id];\r
-       },\r
-       getCellIndex : function(cell) {\r
-               return $A(cell.parentNode.cells).indexOf(cell);\r
-       },\r
-       getRowIndex : function(row) {\r
-               return $A(row.parentNode.rows).indexOf(row);\r
-       },\r
-       getCellText : function(cell, refresh) {\r
-               if(!cell) { return ""; }\r
-               TableKit.registerCell(cell);\r
-               var data = TableKit.cells[cell.id];\r
-               if(refresh || data.refresh || !data.textContent) {\r
-                       data.textContent = cell.textContent ? cell.textContent : cell.innerText;\r
-                       data.refresh = false;\r
-               }\r
-               return data.textContent;\r
-       },\r
-       register : function(table, options) {\r
-               if(!table.id) {\r
-                       TableKit._tblcount += 1;\r
-                       table.id = "tablekit-table-" + TableKit._tblcount;\r
-               }\r
-               var id = table.id;\r
-               TableKit.tables[id] = TableKit.tables[id] ? Object.extend(TableKit.tables[id], options || {}) : Object.extend({sortable:false,resizable:false,editable:false}, options || {});\r
-       },\r
-       registerCell : function(cell) {\r
-               if(!cell.id) {\r
-                       TableKit._cellcount += 1;\r
-                       cell.id = "tablekit-cell-" + TableKit._cellcount;\r
-               }\r
-               if(!TableKit.cells[cell.id]) {\r
-                       TableKit.cells[cell.id] = {textContent : '', htmlContent : '', active : false};\r
-               }\r
-       },\r
-       isSortable : function(table) {\r
-               return TableKit.tables[table.id] ? TableKit.tables[table.id].sortable : false;\r
-       },\r
-       isResizable : function(table) {\r
-               return TableKit.tables[table.id] ? TableKit.tables[table.id].resizable : false;\r
-       },\r
-       isEditable : function(table) {\r
-               return TableKit.tables[table.id] ? TableKit.tables[table.id].editable : false;\r
-       },\r
-       setup : function(o) {\r
-               Object.extend(TableKit.options, o || {} );\r
-       },\r
-       option : function(s, id, o1, o2) {\r
-               o1 = o1 || TableKit.options;\r
-               o2 = o2 || (id ? (TableKit.tables[id] ? TableKit.tables[id] : {}) : {});\r
-               var key = id + s;\r
-               if(!TableKit._opcache[key]){\r
-                       TableKit._opcache[key] = $A($w(s)).inject([],function(a,v){\r
-                               a.push(a[v] = o2[v] || o1[v]);\r
-                               return a;\r
-                       });\r
-               }\r
-               return TableKit._opcache[key];\r
-       },\r
-       e : function(event) {\r
-               return event || window.event;\r
-       },\r
-       tables : {},\r
-       _opcache : {},\r
-       cells : {},\r
-       rows : {},\r
-       heads : {},\r
-       options : {\r
-               autoLoad : true,\r
-               stripe : true,\r
-               sortable : true,\r
-               resizable : true,\r
-               editable : true,\r
-               rowEvenClass : 'roweven',\r
-               rowOddClass : 'rowodd',\r
-               sortableSelector : ['table.sortable'],\r
-               columnClass : 'sortcol',\r
-               descendingClass : 'sortdesc',\r
-               ascendingClass : 'sortasc',\r
-               noSortClass : 'nosort',\r
-               sortFirstAscendingClass : 'sortfirstasc',\r
-               sortFirstDecendingClass : 'sortfirstdesc',\r
-               resizableSelector : ['table.resizable'],\r
-               minWidth : 10,\r
-               showHandle : true,\r
-               resizeOnHandleClass : 'resize-handle-active',\r
-               editableSelector : ['table.editable'],\r
-               formClassName : 'editable-cell-form',\r
-               noEditClass : 'noedit',\r
-               editAjaxURI : '/',\r
-               editAjaxOptions : {}\r
-       },\r
-       _tblcount : 0,\r
-       _cellcount : 0,\r
-       load : function() {\r
-               if(TableKit.options.autoLoad) {\r
-                       if(TableKit.options.sortable) {\r
-                               $A(TableKit.options.sortableSelector).each(function(s){\r
-                                       $$(s).each(function(t) {\r
-                                               TableKit.Sortable.init(t);\r
-                                       });\r
-                               });\r
-                       }\r
-                       if(TableKit.options.resizable) {\r
-                               $A(TableKit.options.resizableSelector).each(function(s){\r
-                                       $$(s).each(function(t) {\r
-                                               TableKit.Resizable.init(t);\r
-                                       });\r
-                               });\r
-                       }\r
-                       if(TableKit.options.editable) {\r
-                               $A(TableKit.options.editableSelector).each(function(s){\r
-                                       $$(s).each(function(t) {\r
-                                               TableKit.Editable.init(t);\r
-                                       });\r
-                               });\r
-                       }\r
-               }\r
-       }\r
-});\r
-\r
-TableKit.Rows = {\r
-       stripe : function(table) {\r
-               var rows = TableKit.getBodyRows(table);\r
-               rows.each(function(r,i) {\r
-                       TableKit.Rows.addStripeClass(table,r,i);\r
-               });\r
-       },\r
-       addStripeClass : function(t,r,i) {\r
-               t = t || r.up('table');\r
-               var op = TableKit.option('rowEvenClass rowOddClass', t.id);\r
-               var css = ((i+1)%2 === 0 ? op[0] : op[1]);\r
-               // using prototype's assClassName/RemoveClassName was not efficient for large tables, hence:\r
-               var cn = r.className.split(/\s+/);\r
-               var newCn = [];\r
-               for(var x = 0, l = cn.length; x < l; x += 1) {\r
-                       if(cn[x] !== op[0] && cn[x] !== op[1]) { newCn.push(cn[x]); }\r
-               }\r
-               newCn.push(css);\r
-               r.className = newCn.join(" ");\r
-       }\r
-};\r
-\r
-TableKit.Sortable = {\r
-       init : function(elm, options){\r
-               var table = $(elm);\r
-               if(table.tagName !== "TABLE") {\r
-                       return;\r
-               }\r
-               TableKit.register(table,Object.extend(options || {},{sortable:true}));\r
-               var sortFirst;\r
-               var cells = TableKit.getHeaderCells(table);\r
-               var op = TableKit.option('noSortClass columnClass sortFirstAscendingClass sortFirstDecendingClass', table.id);\r
-               cells.each(function(c){\r
-                       c = $(c);\r
-                       if(!c.hasClassName(op.noSortClass)) {\r
-                               Event.observe(c, 'mousedown', TableKit.Sortable._sort);\r
-                               c.addClassName(op.columnClass);\r
-                               if(c.hasClassName(op.sortFirstAscendingClass) || c.hasClassName(op.sortFirstDecendingClass)) {\r
-                                       sortFirst = c;\r
-                               }\r
-                       }\r
-               });\r
-\r
-               if(sortFirst) {\r
-                       if(sortFirst.hasClassName(op.sortFirstAscendingClass)) {\r
-                               TableKit.Sortable.sort(table, sortFirst, 1);\r
-                       } else {\r
-                               TableKit.Sortable.sort(table, sortFirst, -1);\r
-                       }\r
-               } else { // just add row stripe classes\r
-                       TableKit.Rows.stripe(table);\r
-               }\r
-       },\r
-       reload : function(table) {\r
-               table = $(table);\r
-               var cells = TableKit.getHeaderCells(table);\r
-               var op = TableKit.option('noSortClass columnClass', table.id);\r
-               cells.each(function(c){\r
-                       c = $(c);\r
-                       if(!c.hasClassName(op.noSortClass)) {\r
-                               Event.stopObserving(c, 'mousedown', TableKit.Sortable._sort);\r
-                               c.removeClassName(op.columnClass);\r
-                       }\r
-               });\r
-               TableKit.Sortable.init(table);\r
-       },\r
-       _sort : function(e) {\r
-               if(TableKit.Resizable._onHandle) {return;}\r
-               e = TableKit.e(e);\r
-               Event.stop(e);\r
-               var cell = Event.element(e);\r
-               while(!(cell.tagName && cell.tagName.match(/td|th/gi))) {\r
-                       cell = cell.parentNode;\r
-               }\r
-               TableKit.Sortable.sort(null, cell);\r
-       },\r
-       sort : function(table, index, order) {\r
-               var cell;\r
-               if(typeof index === 'number') {\r
-                       if(!table || (table.tagName && table.tagName !== "TABLE")) {\r
-                               return;\r
-                       }\r
-                       table = $(table);\r
-                       index = Math.min(table.rows[0].cells.length, index);\r
-                       index = Math.max(1, index);\r
-                       index -= 1;\r
-                       cell = (table.tHead && table.tHead.rows.length > 0) ? $(table.tHead.rows[table.tHead.rows.length-1].cells[index]) : $(table.rows[0].cells[index]);\r
-               } else {\r
-                       cell = $(index);\r
-                       table = table ? $(table) : cell.up('table');\r
-                       index = TableKit.getCellIndex(cell);\r
-               }\r
-               var op = TableKit.option('noSortClass descendingClass ascendingClass', table.id);\r
-               \r
-               if(cell.hasClassName(op.noSortClass)) {return;} \r
-               \r
-               order = order ? order : (cell.hasClassName(op.descendingClass) ? 1 : -1);\r
-               var rows = TableKit.getBodyRows(table);\r
-\r
-               if(cell.hasClassName(op.ascendingClass) || cell.hasClassName(op.descendingClass)) {\r
-                       rows.reverse(); // if it was already sorted we just need to reverse it.\r
-               } else {\r
-                       var datatype = TableKit.Sortable.getDataType(cell,index,table);\r
-                       var tkst = TableKit.Sortable.types;\r
-                       rows.sort(function(a,b) {\r
-                               return order * tkst[datatype].compare(TableKit.getCellText(a.cells[index]),TableKit.getCellText(b.cells[index]));\r
-                       });\r
-               }\r
-               var tb = table.tBodies[0];\r
-               var tkr = TableKit.Rows;\r
-               rows.each(function(r,i) {\r
-                       tb.appendChild(r);\r
-                       tkr.addStripeClass(table,r,i);\r
-               });\r
-               var hcells = TableKit.getHeaderCells(null, cell);\r
-               $A(hcells).each(function(c,i){\r
-                       c = $(c);\r
-                       c.removeClassName(op.ascendingClass);\r
-                       c.removeClassName(op.descendingClass);\r
-                       if(index === i) {\r
-                               if(order === 1) {\r
-                                       c.removeClassName(op.descendingClass);\r
-                                       c.addClassName(op.ascendingClass);\r
-                               } else {\r
-                                       c.removeClassName(op.ascendingClass);\r
-                                       c.addClassName(op.descendingClass);\r
-                               }\r
-                       }\r
-               });\r
-       },\r
-       types : {},\r
-       detectors : [],\r
-       addSortType : function() {\r
-               $A(arguments).each(function(o){\r
-                       TableKit.Sortable.types[o.name] = o;\r
-               });\r
-       },\r
-       getDataType : function(cell,index,table) {\r
-               cell = $(cell);\r
-               index = (index || index === 0) ? index : TableKit.getCellIndex(cell);\r
-               \r
-               var colcache = TableKit.Sortable._coltypecache;\r
-               var cache = colcache[table.id] ? colcache[table.id] : (colcache[table.id] = {});\r
-               \r
-               if(!cache[index]) {\r
-                       var t = '';\r
-                       // first look for a data type id on the heading row cell\r
-                       if(cell.id && TableKit.Sortable.types[cell.id]) {\r
-                               t = cell.id;\r
-                       }\r
-                       t = cell.classNames().detect(function(n){ // then look for a data type classname on the heading row cell\r
-                               return (TableKit.Sortable.types[n]) ? true : false;\r
-                       });\r
-                       if(!t) {\r
-                               var rows = TableKit.getBodyRows(table);\r
-                               cell = rows[0].cells[index]; // grab same index cell from body row to try and match data type\r
-                               t = TableKit.Sortable.detectors.detect(\r
-                                               function(d){\r
-                                                       return TableKit.Sortable.types[d].detect(TableKit.getCellText(cell));\r
-                                               });\r
-                       }\r
-                       cache[index] = t;\r
-               }\r
-               return cache[index];\r
-       },\r
-       _coltypecache : {}\r
-};\r
-\r
-TableKit.Sortable.detectors = $A($w('date-iso date date-eu date-au time currency datasize number casesensitivetext text')); // setting it here because Safari complained when I did it above...\r
-\r
-TableKit.Sortable.Type = Class.create();\r
-TableKit.Sortable.Type.prototype = {\r
-       initialize : function(name, options){\r
-               this.name = name;\r
-               options = Object.extend({\r
-                       normal : function(v){\r
-                               return v;\r
-                       },\r
-                       pattern : /.*/\r
-               }, options || {});\r
-               this.normal = options.normal;\r
-               this.pattern = options.pattern;\r
-               if(options.compare) {\r
-                       this.compare = options.compare;\r
-               }\r
-               if(options.detect) {\r
-                       this.detect = options.detect;\r
-               }\r
-       },\r
-       compare : function(a,b){\r
-               return TableKit.Sortable.Type.compare(this.normal(a), this.normal(b));\r
-       },\r
-       detect : function(v){\r
-               return this.pattern.test(v);\r
-       }\r
-};\r
-\r
-TableKit.Sortable.Type.compare = function(a,b) {\r
-       return a < b ? -1 : a === b ? 0 : 1;\r
-};\r
-\r
-TableKit.Sortable.addSortType(\r
-       new TableKit.Sortable.Type('number', {\r
-               pattern : /^[-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?/,\r
-               normal : function(v) {\r
-                       // This will grab the first thing that looks like a number from a string, so you can use it to order a column of various srings containing numbers.\r
-                       v = parseFloat(v.replace(/^.*?([-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?).*$/,"$1"));\r
-                       return isNaN(v) ? 0 : v;\r
-               }}),\r
-       new TableKit.Sortable.Type('text',{\r
-               normal : function(v) {\r
-                       return v ? v.toLowerCase() : '';\r
-               }}),\r
-       new TableKit.Sortable.Type('casesensitivetext',{pattern : /^[A-Z]+$/}),\r
-       new TableKit.Sortable.Type('datasize',{\r
-               pattern : /^[-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?\s?[k|m|g|t]b$/i,\r
-               normal : function(v) {\r
-                       var r = v.match(/^([-+]?[\d]*\.?[\d]+([eE][-+]?[\d]+)?)\s?([k|m|g|t]?b)?/i);\r
-                       var b = r[1] ? Number(r[1]).valueOf() : 0;\r
-                       var m = r[3] ? r[3].substr(0,1).toLowerCase() : '';\r
-                       var result = b;\r
-                       switch(m) {\r
-                               case  'k':\r
-                                       result = b * 1024;\r
-                                       break;\r
-                               case  'm':                              \r
-                                       result = b * 1024 * 1024;\r
-                                       break;\r
-                               case  'g':\r
-                                       result = b * 1024 * 1024 * 1024;\r
-                                       break;\r
-                               case  't':\r
-                                       result = b * 1024 * 1024 * 1024 * 1024;\r
-                                       break;\r
-                       }\r
-                       return result;\r
-               }}),\r
-       new TableKit.Sortable.Type('date-au',{\r
-               pattern : /^\d{2}\/\d{2}\/\d{4}\s?(?:\d{1,2}\:\d{2}(?:\:\d{2})?\s?[a|p]?m?)?/i,\r
-               normal : function(v) {\r
-                       if(!this.pattern.test(v)) {return 0;}\r
-                       var r = v.match(/^(\d{2})\/(\d{2})\/(\d{4})\s?(?:(\d{1,2})\:(\d{2})(?:\:(\d{2}))?\s?([a|p]?m?))?/i);\r
-                       var yr_num = r[3];\r
-                       var mo_num = parseInt(r[2],10)-1;\r
-                       var day_num = r[1];\r
-                       var hr_num = r[4] ? r[4] : 0;\r
-                       if(r[7] && r[7].toLowerCase().indexOf('p') !== -1) {\r
-                               hr_num = parseInt(r[4],10) + 12;\r
-                       }\r
-                       var min_num = r[5] ? r[5] : 0;\r
-                       var sec_num = r[6] ? r[6] : 0;\r
-                       return new Date(yr_num, mo_num, day_num, hr_num, min_num, sec_num, 0).valueOf();\r
-               }}),\r
-       new TableKit.Sortable.Type('date-us',{\r
-               pattern : /^\d{2}\/\d{2}\/\d{4}\s?(?:\d{1,2}\:\d{2}(?:\:\d{2})?\s?[a|p]?m?)?/i,\r
-               normal : function(v) {\r
-                       if(!this.pattern.test(v)) {return 0;}\r
-                       var r = v.match(/^(\d{2})\/(\d{2})\/(\d{4})\s?(?:(\d{1,2})\:(\d{2})(?:\:(\d{2}))?\s?([a|p]?m?))?/i);\r
-                       var yr_num = r[3];\r
-                       var mo_num = parseInt(r[1],10)-1;\r
-                       var day_num = r[2];\r
-                       var hr_num = r[4] ? r[4] : 0;\r
-                       if(r[7] && r[7].toLowerCase().indexOf('p') !== -1) {\r
-                               hr_num = parseInt(r[4],10) + 12;\r
-                       }\r
-                       var min_num = r[5] ? r[5] : 0;\r
-                       var sec_num = r[6] ? r[6] : 0;\r
-                       return new Date(yr_num, mo_num, day_num, hr_num, min_num, sec_num, 0).valueOf();\r
-               }}),\r
-       new TableKit.Sortable.Type('date-eu',{\r
-               pattern : /^\d{2}-\d{2}-\d{4}/i,\r
-               normal : function(v) {\r
-                       if(!this.pattern.test(v)) {return 0;}\r
-                       var r = v.match(/^(\d{2})-(\d{2})-(\d{4})/);\r
-                       var yr_num = r[3];\r
-                       var mo_num = parseInt(r[2],10)-1;\r
-                       var day_num = r[1];\r
-                       return new Date(yr_num, mo_num, day_num).valueOf();\r
-               }}),\r
-       new TableKit.Sortable.Type('date-iso',{\r
-               pattern : /[\d]{4}-[\d]{2}-[\d]{2}(?:T[\d]{2}\:[\d]{2}(?:\:[\d]{2}(?:\.[\d]+)?)?(Z|([-+][\d]{2}:[\d]{2})?)?)?/, // 2005-03-26T19:51:34Z\r
-               normal : function(v) {\r
-                       if(!this.pattern.test(v)) {return 0;}\r
-                   var d = v.match(/([\d]{4})(-([\d]{2})(-([\d]{2})(T([\d]{2}):([\d]{2})(:([\d]{2})(\.([\d]+))?)?(Z|(([-+])([\d]{2}):([\d]{2})))?)?)?)?/);             \r
-                   var offset = 0;\r
-                   var date = new Date(d[1], 0, 1);\r
-                   if (d[3]) { date.setMonth(d[3] - 1) ;}\r
-                   if (d[5]) { date.setDate(d[5]); }\r
-                   if (d[7]) { date.setHours(d[7]); }\r
-                   if (d[8]) { date.setMinutes(d[8]); }\r
-                   if (d[10]) { date.setSeconds(d[10]); }\r
-                   if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }\r
-                   if (d[14]) {\r
-                       offset = (Number(d[16]) * 60) + Number(d[17]);\r
-                       offset *= ((d[15] === '-') ? 1 : -1);\r
-                   }\r
-                   offset -= date.getTimezoneOffset();\r
-                   if(offset !== 0) {\r
-                       var time = (Number(date) + (offset * 60 * 1000));\r
-                       date.setTime(Number(time));\r
-                   }\r
-                       return date.valueOf();\r
-               }}),\r
-       new TableKit.Sortable.Type('date',{\r
-               pattern: /^(?:sun|mon|tue|wed|thu|fri|sat)\,\s\d{1,2}\s(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s\d{4}(?:\s\d{2}\:\d{2}(?:\:\d{2})?(?:\sGMT(?:[+-]\d{4})?)?)?/i, //Mon, 18 Dec 1995 17:28:35 GMT\r
-               compare : function(a,b) { // must be standard javascript date format\r
-                       if(a && b) {\r
-                               return TableKit.Sortable.Type.compare(new Date(a),new Date(b));\r
-                       } else {\r
-                               return TableKit.Sortable.Type.compare(a ? 1 : 0, b ? 1 : 0);\r
-                       }\r
-               }}),\r
-       new TableKit.Sortable.Type('time',{\r
-               pattern : /^\d{1,2}\:\d{2}(?:\:\d{2})?(?:\s[a|p]m)?$/i,\r
-               compare : function(a,b) {\r
-                       var d = new Date();\r
-                       var ds = d.getMonth() + "/" + d.getDate() + "/" + d.getFullYear() + " ";\r
-                       return TableKit.Sortable.Type.compare(new Date(ds + a),new Date(ds + b));\r
-               }}),\r
-       new TableKit.Sortable.Type('currency',{\r
-               pattern : /^[$£¥\80¤]/, // dollar,pound,yen,euro,generic currency symbol\r
-               normal : function(v) {\r
-                       return v ? parseFloat(v.replace(/[^-\d\.]/g,'')) : 0;\r
-               }})\r
-);\r
-\r
-TableKit.Resizable = {\r
-       init : function(elm, options){\r
-               var table = $(elm);\r
-               if(table.tagName !== "TABLE") {return;}\r
-               TableKit.register(table,Object.extend(options || {},{resizable:true}));          \r
-               var cells = TableKit.getHeaderCells(table);\r
-               cells.each(function(c){\r
-                       c = $(c);\r
-                       //log ("init on " + c.firstChild.nodeValue);\r
-                       Event.observe(c, 'mouseover', TableKit.Resizable.initDetect);\r
-                       Event.observe(c, 'mouseout', TableKit.Resizable.killDetect);\r
-               });\r
-       },\r
-       reload : function(table) {\r
-               table = $(table);\r
-               var cells = TableKit.getHeaderCells(table);\r
-               cells.each(function(c){\r
-                       c = $(c);\r
-                       Event.stopObserving(c, 'mouseover', TableKit.Resizable.initDetect);\r
-                       Event.stopObserving(c, 'mouseout', TableKit.Resizable.killDetect);\r
-               });\r
-               TableKit.Resizable.init(table);\r
-       },\r
-       resize : function(table, index, w) {\r
-               var cell;\r
-               if(typeof index === 'number') {\r
-                       if(!table || (table.tagName && table.tagName !== "TABLE")) {return;}\r
-                       table = $(table);\r
-                       index = Math.min(table.rows[0].cells.length, index);\r
-                       index = Math.max(1, index);\r
-                       index -= 1;\r
-                       cell = (table.tHead && table.tHead.rows.length > 0) ? $(table.tHead.rows[table.tHead.rows.length-1].cells[index]) : $(table.rows[0].cells[index]);\r
-               } else {\r
-                       cell = $(index);\r
-                       table = table ? $(table) : cell.up('table');\r
-                       index = TableKit.getCellIndex(cell);\r
-               }\r
-               var pad = parseInt(cell.getStyle('paddingLeft'),10) + parseInt(cell.getStyle('paddingRight'),10);\r
-               w = Math.max(w-pad, TableKit.option('minWidth', table.id)[0]);\r
-               cell.setStyle({'width' : w + 'px'});\r
-       },\r
-       initDetect : function(e) {\r
-               e = TableKit.e(e);\r
-               var cell = Event.element(e);\r
-               Event.observe(cell, 'mousemove', TableKit.Resizable.detectHandle);\r
-               Event.observe(cell, 'mousedown', TableKit.Resizable.startResize);\r
-       },\r
-       detectHandle : function(e) {\r
-               e = TableKit.e(e);\r
-               var cell = Event.element(e);\r
-               if(TableKit.Resizable.pointerPos(cell,Event.pointerX(e),Event.pointerY(e))){\r
-                       cell.addClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]);\r
-                       TableKit.Resizable._onHandle = true;\r
-               } else {\r
-                       cell.removeClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]);\r
-                       TableKit.Resizable._onHandle = false;\r
-               }\r
-       },\r
-       killDetect : function(e) {\r
-               e = TableKit.e(e);\r
-               TableKit.Resizable._onHandle = false;\r
-               var cell = Event.element(e);\r
-               if (!cell.tagName || cell.tagName != 'TD') return;\r
-               Event.stopObserving(cell, 'mousemove', TableKit.Resizable.detectHandle);\r
-               Event.stopObserving(cell, 'mousedown', TableKit.Resizable.startResize);\r
-               cell.removeClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]);\r
-       },\r
-       startResize : function(e) {\r
-               e = TableKit.e(e);\r
-               if(!TableKit.Resizable._onHandle) { return;}\r
-               var cell = Event.element(e);\r
-               Event.stopObserving(cell, 'mousemove', TableKit.Resizable.detectHandle);\r
-               Event.stopObserving(cell, 'mousedown', TableKit.Resizable.startResize);\r
-               Event.stopObserving(cell, 'mouseout', TableKit.Resizable.killDetect);\r
-               TableKit.Resizable._cell = cell;\r
-               var table = cell.up('table');\r
-               TableKit.Resizable._tbl = table;\r
-               if(TableKit.option('showHandle', table.id)[0]) {\r
-                       TableKit.Resizable._handle = $(document.createElement('div')).addClassName('resize-handle').setStyle({\r
-                               'top' : Position.cumulativeOffset(cell)[1] + 'px',\r
-                               'left' : Event.pointerX(e) + 'px',\r
-                               'height' : table.getDimensions().height + 'px'\r
-                       });\r
-                       document.body.appendChild(TableKit.Resizable._handle);\r
-               }\r
-               Event.observe(document, 'mousemove', TableKit.Resizable.drag);\r
-               Event.observe(document, 'mouseup', TableKit.Resizable.endResize);\r
-               Event.stop(e);\r
-       },\r
-       endResize : function(e) {\r
-               e = TableKit.e(e);\r
-               var cell = TableKit.Resizable._cell;\r
-               TableKit.Resizable.resize(null, cell, (Event.pointerX(e) - Position.cumulativeOffset(cell)[0]));\r
-               Event.stopObserving(document, 'mousemove', TableKit.Resizable.drag);\r
-               Event.stopObserving(document, 'mouseup', TableKit.Resizable.endResize);\r
-               if(TableKit.option('showHandle', TableKit.Resizable._tbl.id)[0]) {\r
-                       $$('div.resize-handle').each(function(elm){\r
-                               document.body.removeChild(elm);\r
-                       });\r
-               }\r
-               Event.observe(cell, 'mouseout', TableKit.Resizable.killDetect);\r
-               TableKit.Resizable._tbl = TableKit.Resizable._handle = TableKit.Resizable._cell = null;\r
-               Event.stop(e);\r
-       },\r
-       drag : function(e) {\r
-               e = TableKit.e(e);\r
-               if(TableKit.Resizable._handle === null) {\r
-                       try {\r
-                               TableKit.Resizable.resize(TableKit.Resizable._tbl, TableKit.Resizable._cell, (Event.pointerX(e) - Position.cumulativeOffset(TableKit.Resizable._cell)[0]));\r
-                       } catch(e) {}\r
-               } else {\r
-                       TableKit.Resizable._handle.setStyle({'left' : Event.pointerX(e) + 'px'});\r
-               }\r
-               return false;\r
-       },\r
-       pointerPos : function(element, x, y) {\r
-       var offset = Position.cumulativeOffset(element);\r
-           return (y >= offset[1] &&\r
-                   y <  offset[1] + element.offsetHeight &&\r
-                   x >= offset[0] + element.offsetWidth - 5 &&\r
-                   x <  offset[0] + element.offsetWidth);\r
-       },\r
-       _onHandle : false,\r
-       _cell : null,\r
-       _tbl : null,\r
-       _handle : null\r
-};\r
-\r
-\r
-TableKit.Editable = {\r
-       init : function(elm, options){\r
-               var table = $(elm);\r
-               if(table.tagName !== "TABLE") {return;}\r
-               TableKit.register(table,Object.extend(options || {},{editable:true}));\r
-               Event.observe(table.tBodies[0], 'click', TableKit.Editable._editCell);\r
-       },\r
-       _editCell : function(e) {\r
-               e = TableKit.e(e);\r
-               var cell = Event.findElement(e,'td');\r
-               TableKit.Editable.editCell(null, cell);\r
-       },\r
-       editCell : function(table, index, cindex) {\r
-               var cell, row;\r
-               if(typeof index === 'number') {\r
-                       if(!table || (table.tagName && table.tagName !== "TABLE")) {return;}\r
-                       table = $(table);\r
-                       index = Math.min(table.tBodies[0].rows.length, index);\r
-                       index = Math.max(1, index);\r
-                       index -= 1;\r
-                       cindex = Math.min(table.rows[0].cells.length, cindex);\r
-                       cindex = Math.max(1, cindex);\r
-                       cindex -= 1;\r
-                       row = $(table.tBodies[0].rows[index]);\r
-                       cell = $(row.cells[cindex]);\r
-               } else {\r
-                       cell = $(index);\r
-                       table = (table && table.tagName && table.tagName !== "TABLE") ? $(table) : cell.up('table');\r
-                       row = cell.up('tr');\r
-               }\r
-               var op = TableKit.option('noEditClass', table.id);\r
-               if(cell.hasClassName(op.noEditClass)) {return;}\r
-               \r
-               var head = $(TableKit.getHeaderCells(table, cell)[TableKit.getCellIndex(cell)]);\r
-               if(head.hasClassName(op.noEditClass)) {return;}\r
-               \r
-               TableKit.registerCell(cell);\r
-               var data = TableKit.cells[cell.id];\r
-               if(data.active) {return;}\r
-               data.htmlContent = cell.innerHTML;\r
-               var ftype = TableKit.Editable.types['text-input'];\r
-               if(head.id && TableKit.Editable.types[head.id]) {\r
-                       ftype = TableKit.Editable.types[head.id];\r
-               } else {\r
-                       var n = head.classNames().detect(function(n){\r
-                                       return (TableKit.Editable.types[n]) ? true : false;\r
-                       });\r
-                       ftype = n ? TableKit.Editable.types[n] : ftype;\r
-               }\r
-               ftype.edit(cell);\r
-               data.active = true;\r
-       },\r
-       types : {},\r
-       addCellEditor : function(o) {\r
-               if(o && o.name) { TableKit.Editable.types[o.name] = o; }\r
-       }\r
-};\r
-\r
-TableKit.Editable.CellEditor = Class.create();\r
-TableKit.Editable.CellEditor.prototype = {\r
-       initialize : function(name, options){\r
-               this.name = name;\r
-               this.options = Object.extend({\r
-                       element : 'input',\r
-                       attributes : {name : 'value', type : 'text'},\r
-                       selectOptions : [],\r
-                       showSubmit : true,\r
-                       submitText : 'OK',\r
-                       showCancel : true,\r
-                       cancelText : 'Cancel',\r
-                       ajaxURI : null,\r
-                       ajaxOptions : null\r
-               }, options || {});\r
-       },\r
-       edit : function(cell) {\r
-               cell = $(cell);\r
-               var op = this.options;\r
-               var table = cell.up('table');\r
-               \r
-               var form = $(document.createElement("form"));\r
-               form.id = cell.id + '-form';\r
-               form.addClassName(TableKit.option('formClassName', table.id)[0]);\r
-               form.onsubmit = this._submit.bindAsEventListener(this);\r
-               \r
-               var field = document.createElement(op.element);\r
-                       $H(op.attributes).each(function(v){\r
-                               field[v.key] = v.value;\r
-                       });\r
-                       switch(op.element) {\r
-                               case 'input':\r
-                               case 'textarea':\r
-                               field.value = TableKit.getCellText(cell);\r
-                               break;\r
-                               \r
-                               case 'select':\r
-                               var txt = TableKit.getCellText(cell);\r
-                               $A(op.selectOptions).each(function(v){\r
-                                       field.options[field.options.length] = new Option(v[0], v[1]);\r
-                                       if(txt === v[1]) {\r
-                                               field.options[field.options.length-1].selected = 'selected';\r
-                                       }\r
-                               });\r
-                               break;\r
-                       }\r
-                       form.appendChild(field);\r
-                       if(op.element === 'textarea') {\r
-                               form.appendChild(document.createElement("br"));\r
-                       }\r
-                       if(op.showSubmit) {\r
-                               var okButton = document.createElement("input");\r
-                               okButton.type = "submit";\r
-                               okButton.value = op.submitText;\r
-                               okButton.className = 'editor_ok_button';\r
-                               form.appendChild(okButton);\r
-                       }\r
-                       if(op.showCancel) {\r
-                               var cancelLink = document.createElement("a");\r
-                               cancelLink.href = "#";\r
-                               cancelLink.appendChild(document.createTextNode(op.cancelText));\r
-                               cancelLink.onclick = this._cancel.bindAsEventListener(this);\r
-                               cancelLink.className = 'editor_cancel';      \r
-                               form.appendChild(cancelLink);\r
-                       }\r
-                       cell.innerHTML = '';\r
-                       cell.appendChild(form);\r
-       },\r
-       _submit : function(e) {\r
-               var cell = Event.findElement(e,'td');\r
-               var form = Event.findElement(e,'form');\r
-               Event.stop(e);\r
-               this.submit(cell,form);\r
-       },\r
-       submit : function(cell, form) {\r
-               var op = this.options;\r
-               form = form ? form : cell.down('form');\r
-               var head = $(TableKit.getHeaderCells(null, cell)[TableKit.getCellIndex(cell)]);\r
-               var row = cell.up('tr');\r
-               var table = cell.up('table');\r
-               var s = '&row=' + (TableKit.getRowIndex(row)+1) + '&cell=' + (TableKit.getCellIndex(cell)+1) + '&id=' + row.id + '&field=' + head.id + '&' + Form.serialize(form);\r
-               this.ajax = new Ajax.Updater(cell, op.ajaxURI || TableKit.option('editAjaxURI', table.id)[0], Object.extend(op.ajaxOptions || TableKit.option('editAjaxOptions', table.id)[0], {\r
-                       postBody : s,\r
-                       onComplete : function() {\r
-                               var data = TableKit.cells[cell.id];\r
-                               data.active = false;\r
-                               data.refresh = true; // mark cell cache for refreshing, in case cell contents has changed and sorting is applied\r
-                       }\r
-               }));\r
-       },\r
-       _cancel : function(e) {\r
-               var cell = Event.findElement(e,'td');\r
-               Event.stop(e);\r
-               this.cancel(cell);\r
-       },\r
-       cancel : function(cell) {\r
-               this.ajax = null;\r
-               var data = TableKit.cells[cell.id];\r
-               cell.innerHTML = data.htmlContent;\r
-               data.htmlContent = '';\r
-               data.active = false;\r
-       },\r
-       ajax : null\r
-};\r
-\r
-TableKit.Editable.textInput = function(n,attributes) {\r
-       TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, {\r
-               element : 'input',\r
-               attributes : Object.extend({name : 'value', type : 'text'}, attributes||{})\r
-       }));\r
-};\r
-TableKit.Editable.textInput('text-input');\r
-\r
-TableKit.Editable.multiLineInput = function(n,attributes) {\r
-       TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, {\r
-               element : 'textarea',\r
-               attributes : Object.extend({name : 'value', rows : '5', cols : '20'}, attributes||{})\r
-       }));    \r
-};     \r
-TableKit.Editable.multiLineInput('multi-line-input');\r
-\r
-TableKit.Editable.selectInput = function(n,attributes,selectOptions) {\r
-       TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, {\r
-               element : 'select',\r
-               attributes : Object.extend({name : 'value'}, attributes||{}),\r
-               'selectOptions' : selectOptions\r
-       }));    \r
-};\r
-\r
-/*\r
-TableKit.Bench = {\r
-       bench : [],\r
-       start : function(){\r
-               TableKit.Bench.bench[0] = new Date().getTime();\r
-       },\r
-       end : function(s){\r
-               TableKit.Bench.bench[1] = new Date().getTime();\r
-               alert(s + ' ' + ((TableKit.Bench.bench[1]-TableKit.Bench.bench[0])/1000)+' seconds.') //console.log(s + ' ' + ((TableKit.Bench.bench[1]-TableKit.Bench.bench[0])/1000)+' seconds.')\r
-               TableKit.Bench.bench = [];\r
-       }\r
-} */\r
-\r
-if(window.FastInit) {\r
-       FastInit.addOnLoad(TableKit.load);\r
-} else {\r
-       Event.observe(window, 'load', TableKit.load);\r
-}\r
+/*
+*
+* Copyright (c) 2007 Andrew Tetlaw & Millstream Web Software
+* http://www.millstream.com.au/view/code/tablekit/
+* Version: 1.2.1 2007-03-11
+* 
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy,
+* modify, merge, publish, distribute, sublicense, and/or sell copies
+* of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+* 
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+* 
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+* * 
+*/
+
+// Use the TableKit class constructure if you'd prefer to init your tables as JS objects
+var TableKit = Class.create();
+
+TableKit.prototype = {
+       initialize : function(elm, options) {
+               var table = $(elm);
+               if(table.tagName !== "TABLE") {
+                       return;
+               }
+               TableKit.register(table,Object.extend(TableKit.options,options || {}));
+               this.id = table.id;
+               var op = TableKit.option('sortable resizable editable', this.id);
+               if(op.sortable) {
+                       TableKit.Sortable.init(table);
+               } 
+               if(op.resizable) {
+                       TableKit.Resizable.init(table);
+               }
+               if(op.editable) {
+                       TableKit.Editable.init(table);
+               }
+       },
+       sort : function(column, order) {
+               TableKit.Sortable.sort(this.id, column, order);
+       },
+       resizeColumn : function(column, w) {
+               TableKit.Resizable.resize(this.id, column, w);
+       },
+       editCell : function(row, column) {
+               TableKit.Editable.editCell(this.id, row, column);
+       }
+};
+
+Object.extend(TableKit, {
+       getBodyRows : function(table) {
+               table = $(table);
+               var id = table.id;
+               if(!TableKit.rows[id]) {
+                       TableKit.rows[id] = (table.tHead && table.tHead.rows.length > 0) ? $A(table.tBodies[0].rows) : $A(table.rows).without(table.rows[0]);
+               }
+               return TableKit.rows[id];
+       },
+       getHeaderCells : function(table, cell) {
+               if(!table) { table = $(cell).up('table'); }
+               var id = table.id;
+               if(!TableKit.heads[id]) {
+                       //TableKit.heads[id] = $A((table.tHead && table.tHead.rows.length > 0) ? table.tHead.rows[table.tHead.rows.length-1].cells : table.rows[0].cells);
+                       TableKit.heads[id] = $A((table.tHead && table.tHead.rows.length > 0) ? table.tHead.rows[0].cells : table.rows[0].cells);
+               }
+               return TableKit.heads[id];
+       },
+       getCellIndex : function(cell) {
+               return $A(cell.parentNode.cells).indexOf(cell);
+       },
+       getRowIndex : function(row) {
+               return $A(row.parentNode.rows).indexOf(row);
+       },
+       getCellText : function(cell, refresh) {
+               if(!cell) { return ""; }
+               TableKit.registerCell(cell);
+               var data = TableKit.cells[cell.id];
+               if(refresh || data.refresh || !data.textContent) {
+                       data.textContent = cell.textContent ? cell.textContent : cell.innerText;
+                       data.refresh = false;
+               }
+               return data.textContent;
+       },
+       register : function(table, options) {
+               if(!table.id) {
+                       TableKit._tblcount += 1;
+                       table.id = "tablekit-table-" + TableKit._tblcount;
+               }
+               var id = table.id;
+               TableKit.tables[id] = TableKit.tables[id] ? Object.extend(TableKit.tables[id], options || {}) : Object.extend({sortable:false,resizable:false,editable:false}, options || {});
+       },
+       registerCell : function(cell) {
+               if(!cell.id) {
+                       TableKit._cellcount += 1;
+                       cell.id = "tablekit-cell-" + TableKit._cellcount;
+               }
+               if(!TableKit.cells[cell.id]) {
+                       TableKit.cells[cell.id] = {textContent : '', htmlContent : '', active : false};
+               }
+       },
+       isSortable : function(table) {
+               return TableKit.tables[table.id] ? TableKit.tables[table.id].sortable : false;
+       },
+       isResizable : function(table) {
+               return TableKit.tables[table.id] ? TableKit.tables[table.id].resizable : false;
+       },
+       isEditable : function(table) {
+               return TableKit.tables[table.id] ? TableKit.tables[table.id].editable : false;
+       },
+       setup : function(o) {
+               Object.extend(TableKit.options, o || {} );
+       },
+       option : function(s, id, o1, o2) {
+               o1 = o1 || TableKit.options;
+               o2 = o2 || (id ? (TableKit.tables[id] ? TableKit.tables[id] : {}) : {});
+               var key = id + s;
+               if(!TableKit._opcache[key]){
+                       TableKit._opcache[key] = $A($w(s)).inject([],function(a,v){
+                               a.push(a[v] = o2[v] || o1[v]);
+                               return a;
+                       });
+               }
+               return TableKit._opcache[key];
+       },
+       e : function(event) {
+               return event || window.event;
+       },
+       tables : {},
+       _opcache : {},
+       cells : {},
+       rows : {},
+       heads : {},
+       options : {
+               autoLoad : true,
+               stripe : true,
+               sortable : true,
+               resizable : true,
+               editable : true,
+               rowEvenClass : 'roweven',
+               rowOddClass : 'rowodd',
+               sortableSelector : ['table.sortable'],
+               columnClass : 'sortcol',
+               descendingClass : 'sortdesc',
+               ascendingClass : 'sortasc',
+               noSortClass : 'nosort',
+               sortFirstAscendingClass : 'sortfirstasc',
+               sortFirstDecendingClass : 'sortfirstdesc',
+               resizableSelector : ['table.resizable'],
+               minWidth : 10,
+               showHandle : true,
+               resizeOnHandleClass : 'resize-handle-active',
+               editableSelector : ['table.editable'],
+               formClassName : 'editable-cell-form',
+               noEditClass : 'noedit',
+               editAjaxURI : '/',
+               editAjaxOptions : {}
+       },
+       _tblcount : 0,
+       _cellcount : 0,
+       load : function() {
+               if(TableKit.options.autoLoad) {
+                       if(TableKit.options.sortable) {
+                               $A(TableKit.options.sortableSelector).each(function(s){
+                                       $$(s).each(function(t) {
+                                               TableKit.Sortable.init(t);
+                                       });
+                               });
+                       }
+                       if(TableKit.options.resizable) {
+                               $A(TableKit.options.resizableSelector).each(function(s){
+                                       $$(s).each(function(t) {
+                                               TableKit.Resizable.init(t);
+                                       });
+                               });
+                       }
+                       if(TableKit.options.editable) {
+                               $A(TableKit.options.editableSelector).each(function(s){
+                                       $$(s).each(function(t) {
+                                               TableKit.Editable.init(t);
+                                       });
+                               });
+                       }
+               }
+       }
+});
+
+TableKit.Rows = {
+       stripe : function(table) {
+               var rows = TableKit.getBodyRows(table);
+               rows.each(function(r,i) {
+                       TableKit.Rows.addStripeClass(table,r,i);
+               });
+       },
+       addStripeClass : function(t,r,i) {
+               t = t || r.up('table');
+               var op = TableKit.option('rowEvenClass rowOddClass', t.id);
+               var css = ((i+1)%2 === 0 ? op[0] : op[1]);
+               // using prototype's assClassName/RemoveClassName was not efficient for large tables, hence:
+               var cn = r.className.split(/\s+/);
+               var newCn = [];
+               for(var x = 0, l = cn.length; x < l; x += 1) {
+                       if(cn[x] !== op[0] && cn[x] !== op[1]) { newCn.push(cn[x]); }
+               }
+               newCn.push(css);
+               r.className = newCn.join(" ");
+       }
+};
+
+TableKit.Sortable = {
+       init : function(elm, options){
+               var table = $(elm);
+               if(table.tagName !== "TABLE") {
+                       return;
+               }
+               TableKit.register(table,Object.extend(options || {},{sortable:true}));
+               var sortFirst;
+               var cells = TableKit.getHeaderCells(table);
+               var op = TableKit.option('noSortClass columnClass sortFirstAscendingClass sortFirstDecendingClass', table.id);
+               cells.each(function(c){
+                       c = $(c);
+                       if(!c.hasClassName(op.noSortClass)) {
+                               Event.observe(c, 'mousedown', TableKit.Sortable._sort);
+                               c.addClassName(op.columnClass);
+                               if(c.hasClassName(op.sortFirstAscendingClass) || c.hasClassName(op.sortFirstDecendingClass)) {
+                                       sortFirst = c;
+                               }
+                       }
+               });
+
+               if(sortFirst) {
+                       if(sortFirst.hasClassName(op.sortFirstAscendingClass)) {
+                               TableKit.Sortable.sort(table, sortFirst, 1);
+                       } else {
+                               TableKit.Sortable.sort(table, sortFirst, -1);
+                       }
+               } else { // just add row stripe classes
+                       TableKit.Rows.stripe(table);
+               }
+       },
+       reload : function(table) {
+               table = $(table);
+               var cells = TableKit.getHeaderCells(table);
+               var op = TableKit.option('noSortClass columnClass', table.id);
+               cells.each(function(c){
+                       c = $(c);
+                       if(!c.hasClassName(op.noSortClass)) {
+                               Event.stopObserving(c, 'mousedown', TableKit.Sortable._sort);
+                               c.removeClassName(op.columnClass);
+                       }
+               });
+               TableKit.Sortable.init(table);
+       },
+       _sort : function(e) {
+               if(TableKit.Resizable._onHandle) {return;}
+               e = TableKit.e(e);
+               Event.stop(e);
+               var cell = Event.element(e);
+               while(!(cell.tagName && cell.tagName.match(/td|th/gi))) {
+                       cell = cell.parentNode;
+               }
+               TableKit.Sortable.sort(null, cell);
+       },
+       sort : function(table, index, order) {
+               var cell;
+               if(typeof index === 'number') {
+                       if(!table || (table.tagName && table.tagName !== "TABLE")) {
+                               return;
+                       }
+                       table = $(table);
+                       index = Math.min(table.rows[0].cells.length, index);
+                       index = Math.max(1, index);
+                       index -= 1;
+                       cell = (table.tHead && table.tHead.rows.length > 0) ? $(table.tHead.rows[table.tHead.rows.length-1].cells[index]) : $(table.rows[0].cells[index]);
+               } else {
+                       cell = $(index);
+                       table = table ? $(table) : cell.up('table');
+                       index = TableKit.getCellIndex(cell);
+               }
+               var op = TableKit.option('noSortClass descendingClass ascendingClass', table.id);
+               
+               if(cell.hasClassName(op.noSortClass)) {return;} 
+               
+               order = order ? order : (cell.hasClassName(op.descendingClass) ? 1 : -1);
+               var rows = TableKit.getBodyRows(table);
+
+               if(cell.hasClassName(op.ascendingClass) || cell.hasClassName(op.descendingClass)) {
+                       rows.reverse(); // if it was already sorted we just need to reverse it.
+               } else {
+                       var datatype = TableKit.Sortable.getDataType(cell,index,table);
+                       var tkst = TableKit.Sortable.types;
+                       rows.sort(function(a,b) {
+                               return order * tkst[datatype].compare(TableKit.getCellText(a.cells[index]),TableKit.getCellText(b.cells[index]));
+                       });
+               }
+               var tb = table.tBodies[0];
+               var tkr = TableKit.Rows;
+               rows.each(function(r,i) {
+                       tb.appendChild(r);
+                       tkr.addStripeClass(table,r,i);
+               });
+               var hcells = TableKit.getHeaderCells(null, cell);
+               $A(hcells).each(function(c,i){
+                       c = $(c);
+                       c.removeClassName(op.ascendingClass);
+                       c.removeClassName(op.descendingClass);
+                       if(index === i) {
+                               if(order === 1) {
+                                       c.removeClassName(op.descendingClass);
+                                       c.addClassName(op.ascendingClass);
+                               } else {
+                                       c.removeClassName(op.ascendingClass);
+                                       c.addClassName(op.descendingClass);
+                               }
+                       }
+               });
+       },
+       types : {},
+       detectors : [],
+       addSortType : function() {
+               $A(arguments).each(function(o){
+                       TableKit.Sortable.types[o.name] = o;
+               });
+       },
+       getDataType : function(cell,index,table) {
+               cell = $(cell);
+               index = (index || index === 0) ? index : TableKit.getCellIndex(cell);
+               
+               var colcache = TableKit.Sortable._coltypecache;
+               var cache = colcache[table.id] ? colcache[table.id] : (colcache[table.id] = {});
+               
+               if(!cache[index]) {
+                       var t = '';
+                       // first look for a data type id on the heading row cell
+                       if(cell.id && TableKit.Sortable.types[cell.id]) {
+                               t = cell.id;
+                       }
+                       t = cell.classNames().detect(function(n){ // then look for a data type classname on the heading row cell
+                               return (TableKit.Sortable.types[n]) ? true : false;
+                       });
+                       if(!t) {
+                               var rows = TableKit.getBodyRows(table);
+                               cell = rows[0].cells[index]; // grab same index cell from body row to try and match data type
+                               t = TableKit.Sortable.detectors.detect(
+                                               function(d){
+                                                       return TableKit.Sortable.types[d].detect(TableKit.getCellText(cell));
+                                               });
+                       }
+                       cache[index] = t;
+               }
+               return cache[index];
+       },
+       _coltypecache : {}
+};
+
+TableKit.Sortable.detectors = $A($w('date-iso date date-eu date-au time currency datasize number casesensitivetext text')); // setting it here because Safari complained when I did it above...
+
+TableKit.Sortable.Type = Class.create();
+TableKit.Sortable.Type.prototype = {
+       initialize : function(name, options){
+               this.name = name;
+               options = Object.extend({
+                       normal : function(v){
+                               return v;
+                       },
+                       pattern : /.*/
+               }, options || {});
+               this.normal = options.normal;
+               this.pattern = options.pattern;
+               if(options.compare) {
+                       this.compare = options.compare;
+               }
+               if(options.detect) {
+                       this.detect = options.detect;
+               }
+       },
+       compare : function(a,b){
+               return TableKit.Sortable.Type.compare(this.normal(a), this.normal(b));
+       },
+       detect : function(v){
+               return this.pattern.test(v);
+       }
+};
+
+TableKit.Sortable.Type.compare = function(a,b) {
+       return a < b ? -1 : a === b ? 0 : 1;
+};
+
+TableKit.Sortable.addSortType(
+       new TableKit.Sortable.Type('number', {
+               pattern : /^[-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?/,
+               normal : function(v) {
+                       // This will grab the first thing that looks like a number from a string, so you can use it to order a column of various srings containing numbers.
+                       v = parseFloat(v.replace(/^.*?([-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?).*$/,"$1"));
+                       return isNaN(v) ? 0 : v;
+               }}),
+       new TableKit.Sortable.Type('text',{
+               normal : function(v) {
+                       return v ? v.toLowerCase() : '';
+               }}),
+       new TableKit.Sortable.Type('casesensitivetext',{pattern : /^[A-Z]+$/}),
+       new TableKit.Sortable.Type('datasize',{
+               pattern : /^[-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?\s?[k|m|g|t]b$/i,
+               normal : function(v) {
+                       var r = v.match(/^([-+]?[\d]*\.?[\d]+([eE][-+]?[\d]+)?)\s?([k|m|g|t]?b)?/i);
+                       var b = r[1] ? Number(r[1]).valueOf() : 0;
+                       var m = r[3] ? r[3].substr(0,1).toLowerCase() : '';
+                       var result = b;
+                       switch(m) {
+                               case  'k':
+                                       result = b * 1024;
+                                       break;
+                               case  'm':                              
+                                       result = b * 1024 * 1024;
+                                       break;
+                               case  'g':
+                                       result = b * 1024 * 1024 * 1024;
+                                       break;
+                               case  't':
+                                       result = b * 1024 * 1024 * 1024 * 1024;
+                                       break;
+                       }
+                       return result;
+               }}),
+       new TableKit.Sortable.Type('date-au',{
+               pattern : /^\d{2}\/\d{2}\/\d{4}\s?(?:\d{1,2}\:\d{2}(?:\:\d{2})?\s?[a|p]?m?)?/i,
+               normal : function(v) {
+                       if(!this.pattern.test(v)) {return 0;}
+                       var r = v.match(/^(\d{2})\/(\d{2})\/(\d{4})\s?(?:(\d{1,2})\:(\d{2})(?:\:(\d{2}))?\s?([a|p]?m?))?/i);
+                       var yr_num = r[3];
+                       var mo_num = parseInt(r[2],10)-1;
+                       var day_num = r[1];
+                       var hr_num = r[4] ? r[4] : 0;
+                       if(r[7] && r[7].toLowerCase().indexOf('p') !== -1) {
+                               hr_num = parseInt(r[4],10) + 12;
+                       }
+                       var min_num = r[5] ? r[5] : 0;
+                       var sec_num = r[6] ? r[6] : 0;
+                       return new Date(yr_num, mo_num, day_num, hr_num, min_num, sec_num, 0).valueOf();
+               }}),
+       new TableKit.Sortable.Type('date-us',{
+               pattern : /^\d{2}\/\d{2}\/\d{4}\s?(?:\d{1,2}\:\d{2}(?:\:\d{2})?\s?[a|p]?m?)?/i,
+               normal : function(v) {
+                       if(!this.pattern.test(v)) {return 0;}
+                       var r = v.match(/^(\d{2})\/(\d{2})\/(\d{4})\s?(?:(\d{1,2})\:(\d{2})(?:\:(\d{2}))?\s?([a|p]?m?))?/i);
+                       var yr_num = r[3];
+                       var mo_num = parseInt(r[1],10)-1;
+                       var day_num = r[2];
+                       var hr_num = r[4] ? r[4] : 0;
+                       if(r[7] && r[7].toLowerCase().indexOf('p') !== -1) {
+                               hr_num = parseInt(r[4],10) + 12;
+                       }
+                       var min_num = r[5] ? r[5] : 0;
+                       var sec_num = r[6] ? r[6] : 0;
+                       return new Date(yr_num, mo_num, day_num, hr_num, min_num, sec_num, 0).valueOf();
+               }}),
+       new TableKit.Sortable.Type('date-eu',{
+               pattern : /^\d{2}-\d{2}-\d{4}/i,
+               normal : function(v) {
+                       if(!this.pattern.test(v)) {return 0;}
+                       var r = v.match(/^(\d{2})-(\d{2})-(\d{4})/);
+                       var yr_num = r[3];
+                       var mo_num = parseInt(r[2],10)-1;
+                       var day_num = r[1];
+                       return new Date(yr_num, mo_num, day_num).valueOf();
+               }}),
+       new TableKit.Sortable.Type('date-iso',{
+               pattern : /[\d]{4}-[\d]{2}-[\d]{2}(?:T[\d]{2}\:[\d]{2}(?:\:[\d]{2}(?:\.[\d]+)?)?(Z|([-+][\d]{2}:[\d]{2})?)?)?/, // 2005-03-26T19:51:34Z
+               normal : function(v) {
+                       if(!this.pattern.test(v)) {return 0;}
+                   var d = v.match(/([\d]{4})(-([\d]{2})(-([\d]{2})(T([\d]{2}):([\d]{2})(:([\d]{2})(\.([\d]+))?)?(Z|(([-+])([\d]{2}):([\d]{2})))?)?)?)?/);             
+                   var offset = 0;
+                   var date = new Date(d[1], 0, 1);
+                   if (d[3]) { date.setMonth(d[3] - 1) ;}
+                   if (d[5]) { date.setDate(d[5]); }
+                   if (d[7]) { date.setHours(d[7]); }
+                   if (d[8]) { date.setMinutes(d[8]); }
+                   if (d[10]) { date.setSeconds(d[10]); }
+                   if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
+                   if (d[14]) {
+                       offset = (Number(d[16]) * 60) + Number(d[17]);
+                       offset *= ((d[15] === '-') ? 1 : -1);
+                   }
+                   offset -= date.getTimezoneOffset();
+                   if(offset !== 0) {
+                       var time = (Number(date) + (offset * 60 * 1000));
+                       date.setTime(Number(time));
+                   }
+                       return date.valueOf();
+               }}),
+       new TableKit.Sortable.Type('date',{
+               pattern: /^(?:sun|mon|tue|wed|thu|fri|sat)\,\s\d{1,2}\s(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s\d{4}(?:\s\d{2}\:\d{2}(?:\:\d{2})?(?:\sGMT(?:[+-]\d{4})?)?)?/i, //Mon, 18 Dec 1995 17:28:35 GMT
+               compare : function(a,b) { // must be standard javascript date format
+                       if(a && b) {
+                               return TableKit.Sortable.Type.compare(new Date(a),new Date(b));
+                       } else {
+                               return TableKit.Sortable.Type.compare(a ? 1 : 0, b ? 1 : 0);
+                       }
+               }}),
+       new TableKit.Sortable.Type('time',{
+               pattern : /^\d{1,2}\:\d{2}(?:\:\d{2})?(?:\s[a|p]m)?$/i,
+               compare : function(a,b) {
+                       var d = new Date();
+                       var ds = d.getMonth() + "/" + d.getDate() + "/" + d.getFullYear() + " ";
+                       return TableKit.Sortable.Type.compare(new Date(ds + a),new Date(ds + b));
+               }}),
+       new TableKit.Sortable.Type('currency',{
+               pattern : /^[$£¥\80¤]/, // dollar,pound,yen,euro,generic currency symbol
+               normal : function(v) {
+                       return v ? parseFloat(v.replace(/[^-\d\.]/g,'')) : 0;
+               }})
+);
+
+TableKit.Resizable = {
+       init : function(elm, options){
+               var table = $(elm);
+               if(table.tagName !== "TABLE") {return;}
+               TableKit.register(table,Object.extend(options || {},{resizable:true}));          
+               var cells = TableKit.getHeaderCells(table);
+               cells.each(function(c){
+                       c = $(c);
+                       //log ("init on " + c.firstChild.nodeValue);
+                       Event.observe(c, 'mouseover', TableKit.Resizable.initDetect);
+                       Event.observe(c, 'mouseout', TableKit.Resizable.killDetect);
+               });
+       },
+       reload : function(table) {
+               table = $(table);
+               var cells = TableKit.getHeaderCells(table);
+               cells.each(function(c){
+                       c = $(c);
+                       Event.stopObserving(c, 'mouseover', TableKit.Resizable.initDetect);
+                       Event.stopObserving(c, 'mouseout', TableKit.Resizable.killDetect);
+               });
+               TableKit.Resizable.init(table);
+       },
+       resize : function(table, index, w) {
+               var cell;
+               if(typeof index === 'number') {
+                       if(!table || (table.tagName && table.tagName !== "TABLE")) {return;}
+                       table = $(table);
+                       index = Math.min(table.rows[0].cells.length, index);
+                       index = Math.max(1, index);
+                       index -= 1;
+                       cell = (table.tHead && table.tHead.rows.length > 0) ? $(table.tHead.rows[table.tHead.rows.length-1].cells[index]) : $(table.rows[0].cells[index]);
+               } else {
+                       cell = $(index);
+                       table = table ? $(table) : cell.up('table');
+                       index = TableKit.getCellIndex(cell);
+               }
+               var pad = parseInt(cell.getStyle('paddingLeft'),10) + parseInt(cell.getStyle('paddingRight'),10);
+               w = Math.max(w-pad, TableKit.option('minWidth', table.id)[0]);
+               cell.setStyle({'width' : w + 'px'});
+       },
+       initDetect : function(e) {
+               e = TableKit.e(e);
+               var cell = Event.element(e);
+               Event.observe(cell, 'mousemove', TableKit.Resizable.detectHandle);
+               Event.observe(cell, 'mousedown', TableKit.Resizable.startResize);
+       },
+       detectHandle : function(e) {
+               e = TableKit.e(e);
+               var cell = Event.element(e);
+               if(TableKit.Resizable.pointerPos(cell,Event.pointerX(e),Event.pointerY(e))){
+                       cell.addClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]);
+                       TableKit.Resizable._onHandle = true;
+               } else {
+                       cell.removeClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]);
+                       TableKit.Resizable._onHandle = false;
+               }
+       },
+       killDetect : function(e) {
+               e = TableKit.e(e);
+               TableKit.Resizable._onHandle = false;
+               var cell = Event.element(e);
+               if (!cell.tagName || cell.tagName != 'TD') return;
+               Event.stopObserving(cell, 'mousemove', TableKit.Resizable.detectHandle);
+               Event.stopObserving(cell, 'mousedown', TableKit.Resizable.startResize);
+               cell.removeClassName(TableKit.option('resizeOnHandleClass', cell.up('table').id)[0]);
+       },
+       startResize : function(e) {
+               e = TableKit.e(e);
+               if(!TableKit.Resizable._onHandle) { return;}
+               var cell = Event.element(e);
+               Event.stopObserving(cell, 'mousemove', TableKit.Resizable.detectHandle);
+               Event.stopObserving(cell, 'mousedown', TableKit.Resizable.startResize);
+               Event.stopObserving(cell, 'mouseout', TableKit.Resizable.killDetect);
+               TableKit.Resizable._cell = cell;
+               var table = cell.up('table');
+               TableKit.Resizable._tbl = table;
+               if(TableKit.option('showHandle', table.id)[0]) {
+                       TableKit.Resizable._handle = $(document.createElement('div')).addClassName('resize-handle').setStyle({
+                               'top' : Position.cumulativeOffset(cell)[1] + 'px',
+                               'left' : Event.pointerX(e) + 'px',
+                               'height' : table.getDimensions().height + 'px'
+                       });
+                       document.body.appendChild(TableKit.Resizable._handle);
+               }
+               Event.observe(document, 'mousemove', TableKit.Resizable.drag);
+               Event.observe(document, 'mouseup', TableKit.Resizable.endResize);
+               Event.stop(e);
+       },
+       endResize : function(e) {
+               e = TableKit.e(e);
+               var cell = TableKit.Resizable._cell;
+               TableKit.Resizable.resize(null, cell, (Event.pointerX(e) - Position.cumulativeOffset(cell)[0]));
+               Event.stopObserving(document, 'mousemove', TableKit.Resizable.drag);
+               Event.stopObserving(document, 'mouseup', TableKit.Resizable.endResize);
+               if(TableKit.option('showHandle', TableKit.Resizable._tbl.id)[0]) {
+                       $$('div.resize-handle').each(function(elm){
+                               document.body.removeChild(elm);
+                       });
+               }
+               Event.observe(cell, 'mouseout', TableKit.Resizable.killDetect);
+               TableKit.Resizable._tbl = TableKit.Resizable._handle = TableKit.Resizable._cell = null;
+               Event.stop(e);
+       },
+       drag : function(e) {
+               e = TableKit.e(e);
+               if(TableKit.Resizable._handle === null) {
+                       try {
+                               TableKit.Resizable.resize(TableKit.Resizable._tbl, TableKit.Resizable._cell, (Event.pointerX(e) - Position.cumulativeOffset(TableKit.Resizable._cell)[0]));
+                       } catch(e) {}
+               } else {
+                       TableKit.Resizable._handle.setStyle({'left' : Event.pointerX(e) + 'px'});
+               }
+               return false;
+       },
+       pointerPos : function(element, x, y) {
+       var offset = Position.cumulativeOffset(element);
+           return (y >= offset[1] &&
+                   y <  offset[1] + element.offsetHeight &&
+                   x >= offset[0] + element.offsetWidth - 5 &&
+                   x <  offset[0] + element.offsetWidth);
+       },
+       _onHandle : false,
+       _cell : null,
+       _tbl : null,
+       _handle : null
+};
+
+
+TableKit.Editable = {
+       init : function(elm, options){
+               var table = $(elm);
+               if(table.tagName !== "TABLE") {return;}
+               TableKit.register(table,Object.extend(options || {},{editable:true}));
+               Event.observe(table.tBodies[0], 'click', TableKit.Editable._editCell);
+       },
+       _editCell : function(e) {
+               e = TableKit.e(e);
+               var cell = Event.findElement(e,'td');
+               TableKit.Editable.editCell(null, cell);
+       },
+       editCell : function(table, index, cindex) {
+               var cell, row;
+               if(typeof index === 'number') {
+                       if(!table || (table.tagName && table.tagName !== "TABLE")) {return;}
+                       table = $(table);
+                       index = Math.min(table.tBodies[0].rows.length, index);
+                       index = Math.max(1, index);
+                       index -= 1;
+                       cindex = Math.min(table.rows[0].cells.length, cindex);
+                       cindex = Math.max(1, cindex);
+                       cindex -= 1;
+                       row = $(table.tBodies[0].rows[index]);
+                       cell = $(row.cells[cindex]);
+               } else {
+                       cell = $(index);
+                       table = (table && table.tagName && table.tagName !== "TABLE") ? $(table) : cell.up('table');
+                       row = cell.up('tr');
+               }
+               var op = TableKit.option('noEditClass', table.id);
+               if(cell.hasClassName(op.noEditClass)) {return;}
+               
+               var head = $(TableKit.getHeaderCells(table, cell)[TableKit.getCellIndex(cell)]);
+               if(head.hasClassName(op.noEditClass)) {return;}
+               
+               TableKit.registerCell(cell);
+               var data = TableKit.cells[cell.id];
+               if(data.active) {return;}
+               data.htmlContent = cell.innerHTML;
+               var ftype = TableKit.Editable.types['text-input'];
+               if(head.id && TableKit.Editable.types[head.id]) {
+                       ftype = TableKit.Editable.types[head.id];
+               } else {
+                       var n = head.classNames().detect(function(n){
+                                       return (TableKit.Editable.types[n]) ? true : false;
+                       });
+                       ftype = n ? TableKit.Editable.types[n] : ftype;
+               }
+               ftype.edit(cell);
+               data.active = true;
+       },
+       types : {},
+       addCellEditor : function(o) {
+               if(o && o.name) { TableKit.Editable.types[o.name] = o; }
+       }
+};
+
+TableKit.Editable.CellEditor = Class.create();
+TableKit.Editable.CellEditor.prototype = {
+       initialize : function(name, options){
+               this.name = name;
+               this.options = Object.extend({
+                       element : 'input',
+                       attributes : {name : 'value', type : 'text'},
+                       selectOptions : [],
+                       showSubmit : true,
+                       submitText : 'OK',
+                       showCancel : true,
+                       cancelText : 'Cancel',
+                       ajaxURI : null,
+                       ajaxOptions : null
+               }, options || {});
+       },
+       edit : function(cell) {
+               cell = $(cell);
+               var op = this.options;
+               var table = cell.up('table');
+               
+               var form = $(document.createElement("form"));
+               form.id = cell.id + '-form';
+               form.addClassName(TableKit.option('formClassName', table.id)[0]);
+               form.onsubmit = this._submit.bindAsEventListener(this);
+               
+               var field = document.createElement(op.element);
+                       $H(op.attributes).each(function(v){
+                               field[v.key] = v.value;
+                       });
+                       switch(op.element) {
+                               case 'input':
+                               case 'textarea':
+                               field.value = TableKit.getCellText(cell);
+                               break;
+                               
+                               case 'select':
+                               var txt = TableKit.getCellText(cell);
+                               $A(op.selectOptions).each(function(v){
+                                       field.options[field.options.length] = new Option(v[0], v[1]);
+                                       if(txt === v[1]) {
+                                               field.options[field.options.length-1].selected = 'selected';
+                                       }
+                               });
+                               break;
+                       }
+                       form.appendChild(field);
+                       if(op.element === 'textarea') {
+                               form.appendChild(document.createElement("br"));
+                       }
+                       if(op.showSubmit) {
+                               var okButton = document.createElement("input");
+                               okButton.type = "submit";
+                               okButton.value = op.submitText;
+                               okButton.className = 'editor_ok_button';
+                               form.appendChild(okButton);
+                       }
+                       if(op.showCancel) {
+                               var cancelLink = document.createElement("a");
+                               cancelLink.href = "#";
+                               cancelLink.appendChild(document.createTextNode(op.cancelText));
+                               cancelLink.onclick = this._cancel.bindAsEventListener(this);
+                               cancelLink.className = 'editor_cancel';      
+                               form.appendChild(cancelLink);
+                       }
+                       cell.innerHTML = '';
+                       cell.appendChild(form);
+       },
+       _submit : function(e) {
+               var cell = Event.findElement(e,'td');
+               var form = Event.findElement(e,'form');
+               Event.stop(e);
+               this.submit(cell,form);
+       },
+       submit : function(cell, form) {
+               var op = this.options;
+               form = form ? form : cell.down('form');
+               var head = $(TableKit.getHeaderCells(null, cell)[TableKit.getCellIndex(cell)]);
+               var row = cell.up('tr');
+               var table = cell.up('table');
+               var s = '&row=' + (TableKit.getRowIndex(row)+1) + '&cell=' + (TableKit.getCellIndex(cell)+1) + '&id=' + row.id + '&field=' + head.id + '&' + Form.serialize(form);
+               this.ajax = new Ajax.Updater(cell, op.ajaxURI || TableKit.option('editAjaxURI', table.id)[0], Object.extend(op.ajaxOptions || TableKit.option('editAjaxOptions', table.id)[0], {
+                       postBody : s,
+                       onComplete : function() {
+                               var data = TableKit.cells[cell.id];
+                               data.active = false;
+                               data.refresh = true; // mark cell cache for refreshing, in case cell contents has changed and sorting is applied
+                       }
+               }));
+       },
+       _cancel : function(e) {
+               var cell = Event.findElement(e,'td');
+               Event.stop(e);
+               this.cancel(cell);
+       },
+       cancel : function(cell) {
+               this.ajax = null;
+               var data = TableKit.cells[cell.id];
+               cell.innerHTML = data.htmlContent;
+               data.htmlContent = '';
+               data.active = false;
+       },
+       ajax : null
+};
+
+TableKit.Editable.textInput = function(n,attributes) {
+       TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, {
+               element : 'input',
+               attributes : Object.extend({name : 'value', type : 'text'}, attributes||{})
+       }));
+};
+TableKit.Editable.textInput('text-input');
+
+TableKit.Editable.multiLineInput = function(n,attributes) {
+       TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, {
+               element : 'textarea',
+               attributes : Object.extend({name : 'value', rows : '5', cols : '20'}, attributes||{})
+       }));    
+};     
+TableKit.Editable.multiLineInput('multi-line-input');
+
+TableKit.Editable.selectInput = function(n,attributes,selectOptions) {
+       TableKit.Editable.addCellEditor(new TableKit.Editable.CellEditor(n, {
+               element : 'select',
+               attributes : Object.extend({name : 'value'}, attributes||{}),
+               'selectOptions' : selectOptions
+       }));    
+};
+
+/*
+TableKit.Bench = {
+       bench : [],
+       start : function(){
+               TableKit.Bench.bench[0] = new Date().getTime();
+       },
+       end : function(s){
+               TableKit.Bench.bench[1] = new Date().getTime();
+               alert(s + ' ' + ((TableKit.Bench.bench[1]-TableKit.Bench.bench[0])/1000)+' seconds.') //console.log(s + ' ' + ((TableKit.Bench.bench[1]-TableKit.Bench.bench[0])/1000)+' seconds.')
+               TableKit.Bench.bench = [];
+       }
+} */
+
+if(window.FastInit) {
+       FastInit.addOnLoad(TableKit.load);
+} else {
+       Event.observe(window, 'load', TableKit.load);
+}
index aabe79aa64315b1f574ed89becc1d431bfc1488e..e9be30c6a85318665bf5f01ae5854598cd2737fd 100644 (file)
Binary files a/UI/WebServerResources/tbtv_sent_17x17.gif and b/UI/WebServerResources/tbtv_sent_17x17.gif differ