]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1356 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Tue, 5 Feb 2008 23:17:19 +0000 (23:17 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Tue, 5 Feb 2008 23:17:19 +0000 (23:17 +0000)
15 files changed:
ChangeLog
SoObjects/Appointments/SOGoAptMailEnglishUpdate.wo/SOGoAptMailEnglishUpdate.html
SoObjects/Appointments/SOGoAptMailFrenchUpdate.wo/SOGoAptMailFrenchUpdate.html
UI/MailPartViewers/UIxMailPartICalActions.m
UI/WebServerResources/ContactsUI.js
UI/WebServerResources/HTMLElement.js
UI/WebServerResources/HTMLTableElement.js
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxAttendeesEditor.js
UI/WebServerResources/UIxContactsUserFolders.js
UI/WebServerResources/UIxMailEditor.js
UI/WebServerResources/dtree.js
UI/WebServerResources/generic.js
UI/WebServerResources/prototype.js

index f9f799d1e7a4b8057b6ffde02edd704bc0154d4b..4cb6edd321cd1db60c61446fdc9657ebc29c418c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2008-02-05  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * UI/MailPartViewers/UIxMailPartICalActions.m ([UIxMailPartICalActions -acceptAction])
+       ([UIxMailPartICalActions -declineAction]): define the organizer of
+       the event as one of the reply-to or from addresses whenever the
+       organizer is not specified in those Outlook invitations.
+
 2008-02-01  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
 
        * UI/MailerUI/UIxMailMainFrame.m ([UIxMailMainFrame
index e6763c8e8cd3cbb9ba127c5c9aa08dea5024c7f5..6a733247be0ecd5fb8b0b6654721d83faee7ab07 100644 (file)
@@ -1,4 +1,4 @@
-<#IsSubject>*E* Apt for the <#OldAptStartDate /> at <#OldAptStartTime /> changed</#IsSubject>
+<#IsSubject>The appointment for the <#OldAptStartDate /> at <#OldAptStartTime /> has changed</#IsSubject>
 <#IsBody>
 This appointment, previously set for <#OldAptStartDate /> 
 at <#OldAptStartTime /> is now scheduled for <#NewAptStartDate /> at <#NewAptStartTime />
index e23d83faf0ae59ddeb2eea8894d5aff8bbb8e6a8..48a3c22650796f1ff7f6f8f48cedaa2420154ca8 100644 (file)
@@ -1,4 +1,4 @@
-<#IsSubject>Rendez-vous du <#OldAptStartDate /> à <#OldAptStartTime /> modifié</#IsSubject>
+<#IsSubject>Le rendez-vous du <#OldAptStartDate /> à <#OldAptStartTime /> est modifié</#IsSubject>
 <#IsBody>
 La réunion qui devait se dérouler le <#OldAptStartDate /> à <#OldAptStartTime /> est maintenant prévue le <#NewAptStartDate /> à <#NewAptStartTime />.
 Vous êtes invité à accepter ou refuser de participer à la réunion pour cette nouvelle date à l'adresse <#HomePageURL />.
index e695de0df90137b4b351a5f61777e9b30c24f7d5..6d91a5fcd1dceb03d9758f69464574a3bdb2aa06 100644 (file)
@@ -25,6 +25,9 @@
 #import <NGObjWeb/WOContext+SoObjects.h>
 #import <NGObjWeb/WOResponse.h>
 
+#import <NGExtensions/NSNull+misc.h>
+#import <NGExtensions/NSObject+Logs.h>
+
 #import <NGCards/iCalCalendar.h>
 #import <NGCards/iCalEvent.h>
 #import <NGCards/iCalPerson.h>
   return [self _eventObjectWithUID: uid forUser: [context activeUser]];
 }
 
+- (void) _fixOrganizerInEvent: (iCalEvent *) brokenEvent
+{
+  iCalPerson *organizer;
+  SOGoMailObject *mail;
+  NSArray *addresses;
+  NGImap4EnvelopeAddress *from;
+  id value;
+
+  organizer = nil;
+
+  mail = [[self clientObject] mailObject];
+
+  addresses = [mail replyToEnvelopeAddresses];
+  if (![addresses count])
+    addresses = [mail fromEnvelopeAddresses];
+  if ([addresses count] > 0)
+    {
+      from = [addresses objectAtIndex: 0];
+      value = [from baseEMail];
+      if ([value isNotNull])
+       {
+         organizer = [iCalPerson elementWithTag: @"organizer"];
+         [organizer setEmail: value];
+         value = [from personalName];
+         if ([value isNotNull])
+           [organizer setCn: value];
+         [brokenEvent setOrganizer: organizer];
+       }
+    }
+
+  if (!organizer)
+    [self errorWithFormat: @"no organizer could be found, SOGo will crash"
+         @" if the user replies"];
+}
+
 - (iCalEvent *)
   _setupChosenEventAndEventObject: (SOGoAppointmentObject **) eventObject
 {
   iCalEvent *emailEvent, *calendarEvent, *chosenEvent;
+  iCalPerson *organizer;
 
   emailEvent = [self _emailEvent];
   if (emailEvent)
          else
            chosenEvent = calendarEvent;
        }
+
+      organizer = [chosenEvent organizer];
+      if (![[organizer rfc822Email] length])
+       [self _fixOrganizerInEvent: chosenEvent];
     }
   else
     chosenEvent = nil;
index 87c12eae9a1b4a7e86da2c2fa93a480b6d5c6b3b..4ec024786e351653c1fc5a06f9aa6eb343a74fa9 100644 (file)
@@ -132,7 +132,7 @@ function contactsListCallback(http) {
        for (var i = 0; i < selected.length; i++) {
          var row = $(selected[i]);
          if (row)
-           row.select();
+           row.selectElement();
        }
       }
     }
@@ -166,7 +166,7 @@ function onContactFoldersContextMenu(event) {
   for (var i = 0; i < selectedNodes.length; i++)
     $(selectedNodes[i]).deselect();
   topNode.menuSelectedEntry = this;
-  $(this).select();
+  $(this).selectElement();
 }
 
 function onContactContextMenu(event, element) { log ("onContactContextMenu");
@@ -181,7 +181,7 @@ function onContactContextMenu(event, element) { log ("onContactContextMenu");
   for (var i = 0; i < selectedNodes.length; i++)
     $(selectedNodes[i]).deselect();
   topNode.menuSelectedEntry = element;
-  $(element).select();
+  $(element).selectElement();
 }
 
 function onContactContextMenuHide(event) {
@@ -194,7 +194,7 @@ function onContactContextMenuHide(event) {
   if (topNode.menuSelectedRows) {
     var nodes = topNode.menuSelectedRows;
     for (var i = 0; i < nodes.length; i++)
-      $(nodes[i]).select();
+      $(nodes[i]).selectElement();
     topNode.menuSelectedRows = null;
   }
 }
@@ -209,7 +209,7 @@ function onContactFoldersContextMenuHide(event) {
   if (topNode.menuSelectedRows) {
     var nodes = topNode.menuSelectedRows;
     for (var i = 0; i < nodes.length; i++)
-      nodes[i].select();
+      nodes[i].selectElement();
     topNode.menuSelectedRows = null;
   }
 }
@@ -222,7 +222,7 @@ function onFolderMenuHide(event) {
     topNode.menuSelectedEntry = null;
   }
   if (topNode.selectedEntry)
-    topNode.selectedEntry.select();
+    topNode.selectedEntry.selectElement();
 }
 
 function loadContact(idx) {
@@ -602,7 +602,7 @@ function onFolderUnsubscribeCB(folderId) {
    var node = $(folderId);
    node.parentNode.removeChild(node);
    var personal = $("/personal");
-   personal.select();
+   personal.selectElement();
    onFolderSelectionChange();
 }
 
@@ -619,7 +619,7 @@ function onAddressBookRemove(event) {
       var abId = folderIdElements[0].substr(1);
       deletePersonalAddressBook(abId);
       var personal = $("/personal");
-      personal.select();
+      personal.selectElement();
       onFolderSelectionChange();
     }
   }
@@ -723,7 +723,7 @@ function configureContactFolders() {
     lookupDeniedFolders();
 
     var personalFolder = $("/personal");
-    personalFolder.select();
+    personalFolder.selectElement();
     openContactsFolder("/personal");
   }
 }
index 8fdcb00a18e2ed21ffe07e2bbf115b70a259e17a..33af5266ba01dc8dc6733b3fc171b0ae49dc57a3 100644 (file)
@@ -155,7 +155,7 @@ Element.addMethods({
                  element.onContextMenu.bindAsEventListener(element));
   },
 
-  select: function(element) {
+  selectElement: function(element) {
     element = $(element);
     element.addClassName('_selected');
   },
@@ -180,7 +180,7 @@ Element.addMethods({
       rows = element.getElementsByTagName('TR');
     while (s <= e) {
       if (rows[s].nodeType == 1)
-       $(rows[s]).select();
+       $(rows[s]).selectElement();
       s++;
     }
   },
index 44917545707b366979aa1ba6a98128dd60ec5dd4..b7275a7837654f3353fdc627684a384c95e250fd 100644 (file)
@@ -31,7 +31,7 @@ Element.addMethods({
       for (var i = 0; i < nodes.length; i++) {
        var node = nodes.item(i);
        if (node.tagName && node.hasClassName(className))
-         node.select();
+         node.selectElement();
       }
     }
   }
index fb055ebc587f8089afeee2c8a1065a8fd3e19c4b..c4b8b4530ff28022bbc2a8c7268ac53720936d3c 100644 (file)
@@ -262,7 +262,7 @@ function deleteSelectedMessagesCallback(http) {
       if (deleteMessageRequestCount == 0) {
        if (nextRow) {
          Mailer.currentMessages[Mailer.currentMailbox] = nextRow.getAttribute("id").substr(4);
-         nextRow.select();
+         nextRow.selectElement();
          loadMessage(Mailer.currentMessages[Mailer.currentMailbox]);
        }
       }
@@ -351,7 +351,7 @@ function onMailboxTreeItemClick(event) {
 
   if (topNode.selectedEntry)
     topNode.selectedEntry.deselect();
-  this.select();
+  this.selectElement();
   topNode.selectedEntry = this;
 
   search = {};
@@ -537,7 +537,7 @@ function messageListCallback(http) {
     if (selected) {
       var row = $("row_" + selected);
       if (row) {
-       row.select();
+       row.selectElement();
        lastClickedRow = row.rowIndex - $(row).up('table').down('thead').getElementsByTagName('tr').length;  
        var rowPosition = row.rowIndex * row.getHeight();
        if ($(row).up('div').getHeight() > rowPosition)
@@ -626,7 +626,7 @@ function onMessageContextMenuHide(event) {
   if (topNode.menuSelectedRows) {
     var nodes = topNode.menuSelectedRows;
     for (var i = 0; i < nodes.length; i++)
-      nodes[i].select();
+      nodes[i].selectElement();
     topNode.menuSelectedRows = null;
   }
 }
@@ -659,7 +659,7 @@ function onFolderMenuClick(event) {
   if (topNode.menuSelectedEntry)
     topNode.menuSelectedEntry.deselect();
   topNode.menuSelectedEntry = this;
-  this.select();
+  this.selectElement();
 
   preventDefault(event);
 }
@@ -672,7 +672,7 @@ function onFolderMenuHide(event) {
     topNode.menuSelectedEntry = null;
   }
   if (topNode.selectedEntry)
-    topNode.selectedEntry.select();
+    topNode.selectedEntry.selectElement();
 }
 
 function deleteCachedMessage(messageId) {
@@ -1244,7 +1244,7 @@ function openInbox(node) {
   openMailbox(node.parentNode.getAttribute("dataname"));
   var tree = $("mailboxTree");
   tree.selectedEntry = node;
-  node.select();
+  node.selectElement();
   mailboxTree.o(1);
 }
 
@@ -1450,7 +1450,7 @@ function onLoadMailboxesCallback(http) {
   //    var links = document.getElementsByClassName("node", treeNodes[i]);
   //    if (tree.selectedEntry)
   //       tree.selectedEntry.deselect();
-  //    links[0].select();
+  //    links[0].selectElement();
   //    tree.selectedEntry = links[0];
   //    expandUpperTree(links[0]);
   //       }
index d9ab34890fadaf8e1984d8ce1a845c85eece65dd..c459dc4005c4181d49e4e3a10781ea6c3ae675fd 100644 (file)
@@ -269,7 +269,7 @@ function onDaySelect(node) {
   if (document.selectedDate)
     document.selectedDate.deselect();
 
-  td.select();
+  td.selectElement();
   document.selectedDate = td;
 
   changeCalendarDisplay( { "day": day } );
@@ -445,7 +445,7 @@ function tasksListCallback(http) {
        var selectedNodesId = http.callbackData;
        for (var i = 0; i < selectedNodesId.length; i++) {
          //    log(selectedNodesId[i] + " (" + i + ") is selected");
-         $(selectedNodesId[i]).select();
+         $(selectedNodesId[i]).selectElement();
        }
       }
       else
@@ -474,7 +474,7 @@ function restoreCurrentDaySelection(div) {
        var td = $(elements[i]).getParentWithTagName("td");
        if (document.selectedDate)
          document.selectedDate.deselect();
-       $(td).select();
+       $(td).selectElement();
        document.selectedDate = td;
       }
     }
@@ -547,7 +547,7 @@ function changeCalendarDisplay(data, newView) {
        var selectedLink = $$('table#dateSelectorTable a[day='+day+']');
        if (selectedLink.length > 0) {
          selectedCell = selectedLink[0].up(1);
-         selectedCell.select();
+         selectedCell.selectElement();
          document.selectedDate = selectedCell;
        }
        
@@ -1148,7 +1148,7 @@ function selectCalendarEvent(div) {
       selectedCalendarCell[i].deselect();
 
   for (var i = 0; i < div.siblings.length; i++)
-    div.siblings[i].select();
+    div.siblings[i].selectElement();
 
   selectedCalendarCell = div.siblings;
 }
@@ -1164,7 +1164,7 @@ function onCalendarSelectEvent() {
   if (row) {
     var div = row.parentNode.parentNode.parentNode;
     div.scrollTop = row.offsetTop - (div.offsetHeight / 2);
-    row.select();
+    row.selectElement();
   }
 }
 
index 9484f18b504350263b08b5c38268c2230482e137..655ecfff40448e073694306716de2a709ad5a6e4 100644 (file)
@@ -49,6 +49,10 @@ function onContactKeydown(event) {
        delayedSearch = true;
        setTimeout("performSearch()", attendeesEditor.delay);
       }
+      else if (this.value.length == 0) {
+       if (document.currentPopupMenu)
+         hideMenu(document.currentPopupMenu);
+      }
   }
   else if (this.confirmedValue)
     if (event.keyCode == 13) // Enter
index 57fc1a342f53b8b07dd26c0fb851f0e83aba7e95..ee22fd26d37997f92f87c7329f99f79a04b99989 100644 (file)
@@ -86,7 +86,7 @@ function onFolderTreeItemClick(event) {
    var topNode = $("d");
    if (topNode.selectedEntry)
       topNode.selectedEntry.deselect();
-   this.select();
+   this.selectElement();
    topNode.selectedEntry = this;
 }
 
index 53d1fd813f204e90d8870c321aff2bfa22d3dd97..e961740bfe12955b3f2412d2ca4ab18e5032df14 100644 (file)
@@ -364,7 +364,7 @@ function onSelectAllAttachments() {
   var list = $("attachments");
   var nodes = list.childNodesWithTag("li");
   for (var i = 0; i < nodes.length; i++)
-    nodes[i].select();
+    nodes[i].selectElement();
 }
 
 function onWindowResize(event) {
index 4ddee693c0f3ea96ecf6f7507914bf6c32c14b3b..8167ce9ac50305c00ba8335289c711ce76c17e12 100644 (file)
@@ -221,7 +221,7 @@ dTree.prototype.s = function(id) {
       eOld.deselect();
     }
     eNew = document.getElementById("s" + this.obj + id);
-    eNew.select();
+    eNew.selectElement();
     this.selectedNode = id;
     if (this.config.useCookies) this.setCookie('cs' + this.obj, cn.id);
   }
index d25ea6670808917d74f8c5ef9a9c9af29a0dfa5c..c5ef30a006c8b78e96a642978ff4f9d6550efd21 100644 (file)
@@ -555,13 +555,13 @@ function onRowClick(event) {
     else if (isNodeSelected(node) == true) {
       $(node).deselect();
     } else {
-      $(node).select();
+      $(node).selectElement();
     }
     // At this point, should empty content of 3-pane view
   } else {
     // Single line selection
     $(node.parentNode).deselectAll();
-    $(node).select();
+    $(node).selectElement();
   
     if (initialSelection != $(node.parentNode).getSelectedNodes()) {
       // Selection has changed; fire mousedown event
@@ -920,7 +920,7 @@ function onSearchFocus() {
     this.value = "";
     this.setAttribute("modified", "");
   } else {
-    this.select();
+    this.selectElement();
   }
 
   this.setStyle({ color: "#000" });
@@ -975,9 +975,9 @@ function initCriteria() {
   var searchCriteria = $("searchCriteria");
   var searchValue = $("searchValue");
   var searchOptions = $("searchOptions");
-  
+    
   if (searchValue) {
-    var firstOption = searchOptions.down('li');
+    var firstOption = searchOptions.down("li");
     if (firstOption) {
       searchCriteria.value = firstOption.getAttribute('id');
       searchValue.ghostPhrase = firstOption.innerHTML;
@@ -1181,7 +1181,7 @@ function initMenus() {
 }
 
 function initMenu(menuDIV, callbacks) {
-  var lis = $(menuDIV.down("ul")).childNodesWithTag("li");
+  var lis = menuDIV.down("ul").childNodesWithTag("li");
   for (var j = 0; j < lis.length; j++) {
     var node = $(lis[j]);
     node.observe("mousedown", listRowMouseDownHandler, false);
@@ -1375,7 +1375,7 @@ function loadPreferences() {
 }
 
 function onLoadHandler(event) {
-  if (typeof UserLogin != "undefined")
+  if (typeof UserLogin != "undefined" && UserLogin != "wrongusernamepassword")
     loadPreferences();
   queryParameters = parseQueryParameters('' + window.location);
   if (!$(document.body).hasClassName("popup")) {
index 5c73462946b96cd5f44fa74aae64895b99e14eb6..35a691379ca66b0e38ed10b8ce9ef257fff8cfd5 100644 (file)
@@ -1,5 +1,5 @@
-/*  Prototype JavaScript framework, version 1.6.0
- *  (c) 2005-2007 Sam Stephenson
+/*  Prototype JavaScript framework, version 1.6.0.2
+ *  (c) 2005-2008 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://www.prototypejs.org/
@@ -7,7 +7,7 @@
  *--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.6.0',
+  Version: '1.6.0.2',
 
   Browser: {
     IE:     !!(window.attachEvent && !window.opera),
@@ -36,8 +36,6 @@ var Prototype = {
 if (Prototype.Browser.MobileSafari)
   Prototype.BrowserFeatures.SpecificElementExtensions = false;
 
-if (Prototype.Browser.WebKit)
-  Prototype.BrowserFeatures.XPath = false;
 
 /* Based on Alex Arnell's inheritance implementation. */
 var Class = {
@@ -110,9 +108,9 @@ Object.extend = function(destination, source) {
 Object.extend(Object, {
   inspect: function(object) {
     try {
-      if (object === undefined) return 'undefined';
+      if (Object.isUndefined(object)) return 'undefined';
       if (object === null) return 'null';
-      return object.inspect ? object.inspect() : object.toString();
+      return object.inspect ? object.inspect() : String(object);
     } catch (e) {
       if (e instanceof RangeError) return '...';
       throw e;
@@ -135,7 +133,7 @@ Object.extend(Object, {
     var results = [];
     for (var property in object) {
       var value = Object.toJSON(object[property]);
-      if (value !== undefined)
+      if (!Object.isUndefined(value))
         results.push(property.toJSON() + ': ' + value);
     }
 
@@ -173,7 +171,8 @@ Object.extend(Object, {
   },
 
   isArray: function(object) {
-    return object && object.constructor === Array;
+    return object != null && typeof object == "object" &&
+      'splice' in object && 'join' in object;
   },
 
   isHash: function(object) {
@@ -204,7 +203,7 @@ Object.extend(Function.prototype, {
   },
 
   bind: function() {
-    if (arguments.length < 2 && arguments[0] === undefined) return this;
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
     var __method = this, args = $A(arguments), object = args.shift();
     return function() {
       return __method.apply(object, args.concat($A(arguments)));
@@ -351,7 +350,7 @@ Object.extend(String.prototype, {
 
   sub: function(pattern, replacement, count) {
     replacement = this.gsub.prepareReplacement(replacement);
-    count = count === undefined ? 1 : count;
+    count = Object.isUndefined(count) ? 1 : count;
 
     return this.gsub(pattern, function(match) {
       if (--count < 0) return match[0];
@@ -366,7 +365,7 @@ Object.extend(String.prototype, {
 
   truncate: function(length, truncation) {
     length = length || 30;
-    truncation = truncation === undefined ? '...' : truncation;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
     return this.length > length ?
       this.slice(0, length - truncation.length) + truncation : String(this);
   },
@@ -486,7 +485,9 @@ Object.extend(String.prototype, {
   },
 
   isJSON: function() {
-    var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    var str = this;
+    if (str.blank()) return false;
+    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
     return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
   },
 
@@ -565,7 +566,8 @@ var Template = Class.create({
       if (before == '\\') return match[2];
 
       var ctx = object, expr = match[3];
-      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      match = pattern.exec(expr);
       if (match == null) return before;
 
       while (match != null) {
@@ -577,7 +579,7 @@ var Template = Class.create({
       }
 
       return before + String.interpret(ctx);
-    }.bind(this));
+    });
   }
 });
 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
@@ -686,7 +688,7 @@ var Enumerable = {
   },
 
   inGroupsOf: function(number, fillWith) {
-    fillWith = fillWith === undefined ? null : fillWith;
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
     return this.eachSlice(number, function(slice) {
       while(slice.length < number) slice.push(fillWith);
       return slice;
@@ -713,7 +715,7 @@ var Enumerable = {
     var result;
     this.each(function(value, index) {
       value = iterator(value, index);
-      if (result == undefined || value >= result)
+      if (result == null || value >= result)
         result = value;
     });
     return result;
@@ -724,7 +726,7 @@ var Enumerable = {
     var result;
     this.each(function(value, index) {
       value = iterator(value, index);
-      if (result == undefined || value < result)
+      if (result == null || value < result)
         result = value;
     });
     return result;
@@ -805,20 +807,20 @@ Object.extend(Enumerable, {
 function $A(iterable) {
   if (!iterable) return [];
   if (iterable.toArray) return iterable.toArray();
-  var length = iterable.length, results = new Array(length);
+  var length = iterable.length || 0, results = new Array(length);
   while (length--) results[length] = iterable[length];
   return results;
 }
 
 if (Prototype.Browser.WebKit) {
-  function $A(iterable) {
+  $A = function(iterable) {
     if (!iterable) return [];
     if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
         iterable.toArray) return iterable.toArray();
-    var length = iterable.length, results = new Array(length);
+    var length = iterable.length || 0, results = new Array(length);
     while (length--) results[length] = iterable[length];
     return results;
-  }
+  };
 }
 
 Array.from = $A;
@@ -904,7 +906,7 @@ Object.extend(Array.prototype, {
     var results = [];
     this.each(function(object) {
       var value = Object.toJSON(object);
-      if (value !== undefined) results.push(value);
+      if (!Object.isUndefined(value)) results.push(value);
     });
     return '[' + results.join(', ') + ']';
   }
@@ -984,34 +986,6 @@ function $H(object) {
 };
 
 var Hash = Class.create(Enumerable, (function() {
-  if (function() {
-    var i = 0, Test = function(value) { this.key = value };
-    Test.prototype.key = 'foo';
-    for (var property in new Test('bar')) i++;
-    return i > 1;
-  }()) {
-    function each(iterator) {
-      var cache = [];
-      for (var key in this._object) {
-        var value = this._object[key];
-        if (cache.include(key)) continue;
-        cache.push(key);
-        var pair = [key, value];
-        pair.key = key;
-        pair.value = value;
-        iterator(pair);
-      }
-    }
-  } else {
-    function each(iterator) {
-      for (var key in this._object) {
-        var value = this._object[key], pair = [key, value];
-        pair.key = key;
-        pair.value = value;
-        iterator(pair);
-      }
-    }
-  }
 
   function toQueryPair(key, value) {
     if (Object.isUndefined(value)) return key;
@@ -1023,7 +997,14 @@ var Hash = Class.create(Enumerable, (function() {
       this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
     },
 
-    _each: each,
+    _each: function(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    },
 
     set: function(key, value) {
       return this._object[key] = value;
@@ -1187,8 +1168,11 @@ Ajax.Base = Class.create({
     Object.extend(this.options, options || { });
 
     this.options.method = this.options.method.toLowerCase();
+
     if (Object.isString(this.options.parameters))
       this.options.parameters = this.options.parameters.toQueryParams();
+    else if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
   }
 });
 
@@ -1315,7 +1299,7 @@ Ajax.Request = Class.create(Ajax.Base, {
 
       var contentType = response.getHeader('Content-type');
       if (this.options.evalJS == 'force'
-          || (this.options.evalJS && contentType
+          || (this.options.evalJS && this.isSameOrigin() && contentType
           && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
         this.evalResponse();
     }
@@ -1333,9 +1317,18 @@ Ajax.Request = Class.create(Ajax.Base, {
     }
   },
 
+  isSameOrigin: function() {
+    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+      protocol: location.protocol,
+      domain: document.domain,
+      port: location.port ? ':' + location.port : ''
+    }));
+  },
+
   getHeader: function(name) {
     try {
-      return this.transport.getResponseHeader(name);
+      return this.transport.getResponseHeader(name) || null;
     } catch (e) { return null }
   },
 
@@ -1371,7 +1364,7 @@ Ajax.Response = Class.create({
 
     if(readyState == 4) {
       var xml = transport.responseXML;
-      this.responseXML  = xml === undefined ? null : xml;
+      this.responseXML  = Object.isUndefined(xml) ? null : xml;
       this.responseJSON = this._getResponseJSON();
     }
   },
@@ -1408,7 +1401,8 @@ Ajax.Response = Class.create({
     if (!json) return null;
     json = decodeURIComponent(escape(json));
     try {
-      return json.evalJSON(this.request.options.sanitizeJSON);
+      return json.evalJSON(this.request.options.sanitizeJSON ||
+        !this.request.isSameOrigin());
     } catch (e) {
       this.request.dispatchException(e);
     }
@@ -1417,10 +1411,12 @@ Ajax.Response = Class.create({
   _getResponseJSON: function() {
     var options = this.request.options;
     if (!options.evalJSON || (options.evalJSON != 'force' &&
-      !(this.getHeader('Content-type') || '').include('application/json')))
-        return null;
+      !(this.getHeader('Content-type') || '').include('application/json')) ||
+        this.responseText.blank())
+          return null;
     try {
-      return this.transport.responseText.evalJSON(options.sanitizeJSON);
+      return this.responseText.evalJSON(options.sanitizeJSON ||
+        !this.request.isSameOrigin());
     } catch (e) {
       this.request.dispatchException(e);
     }
@@ -1434,11 +1430,11 @@ Ajax.Updater = Class.create(Ajax.Request, {
       failure: (container.failure || (container.success ? null : container))
     };
 
-    options = options || { };
+    options = Object.clone(options);
     var onComplete = options.onComplete;
-    options.onComplete = (function(response, param) {
+    options.onComplete = (function(response, json) {
       this.updateContent(response.responseText);
-      if (Object.isFunction(onComplete)) onComplete(response, param);
+      if (Object.isFunction(onComplete)) onComplete(response, json);
     }).bind(this);
 
     $super(url, options);
@@ -1460,10 +1456,6 @@ Ajax.Updater = Class.create(Ajax.Request, {
       }
       else receiver.update(responseText);
     }
-
-    if (this.success()) {
-      if (this.onComplete) this.onComplete.bind(this).defer();
-    }
   }
 });
 
@@ -1628,24 +1620,28 @@ Element.Methods = {
         Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
           insertions = {bottom:insertions};
 
-    var content, t, range;
+    var content, insert, tagName, childNodes;
 
-    for (position in insertions) {
+    for (var position in insertions) {
       content  = insertions[position];
       position = position.toLowerCase();
-      t = Element._insertionTranslations[position];
+      insert = Element._insertionTranslations[position];
 
       if (content && content.toElement) content = content.toElement();
       if (Object.isElement(content)) {
-        t.insert(element, content);
+        insert(element, content);
         continue;
       }
 
       content = Object.toHTML(content);
 
-      range = element.ownerDocument.createRange();
-      t.initializeRange(element, range);
-      t.insert(element, range.createContextualFragment(content.stripScripts()));
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+      if (position == 'top' || position == 'after') childNodes.reverse();
+      childNodes.each(insert.curry(element));
 
       content.evalScripts.bind(content).defer();
     }
@@ -1690,7 +1686,7 @@ Element.Methods = {
   },
 
   descendants: function(element) {
-    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+    return $(element).select("*");
   },
 
   firstDescendant: function(element) {
@@ -1729,32 +1725,31 @@ Element.Methods = {
     element = $(element);
     if (arguments.length == 1) return $(element.parentNode);
     var ancestors = element.ancestors();
-    return expression ? Selector.findElement(ancestors, expression, index) :
-      ancestors[index || 0];
+    return Object.isNumber(expression) ? ancestors[expression] :
+      Selector.findElement(ancestors, expression, index);
   },
 
   down: function(element, expression, index) {
     element = $(element);
     if (arguments.length == 1) return element.firstDescendant();
-    var descendants = element.descendants();
-    return expression ? Selector.findElement(descendants, expression, index) :
-      descendants[index || 0];
+    return Object.isNumber(expression) ? element.descendants()[expression] :
+      element.select(expression)[index || 0];
   },
 
   previous: function(element, expression, index) {
     element = $(element);
     if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
     var previousSiblings = element.previousSiblings();
-    return expression ? Selector.findElement(previousSiblings, expression, index) :
-      previousSiblings[index || 0];
+    return Object.isNumber(expression) ? previousSiblings[expression] :
+      Selector.findElement(previousSiblings, expression, index);
   },
 
   next: function(element, expression, index) {
     element = $(element);
     if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
     var nextSiblings = element.nextSiblings();
-    return expression ? Selector.findElement(nextSiblings, expression, index) :
-      nextSiblings[index || 0];
+    return Object.isNumber(expression) ? nextSiblings[expression] :
+      Selector.findElement(nextSiblings, expression, index);
   },
 
   select: function() {
@@ -1795,10 +1790,11 @@ Element.Methods = {
     var attributes = { }, t = Element._attributeTranslations.write;
 
     if (typeof name == 'object') attributes = name;
-    else attributes[name] = value === undefined ? true : value;
+    else attributes[name] = Object.isUndefined(value) ? true : value;
 
     for (var attr in attributes) {
-      var name = t.names[attr] || attr, value = attributes[attr];
+      name = t.names[attr] || attr;
+      value = attributes[attr];
       if (t.values[attr]) name = t.values[attr](element, value);
       if (value === false || value === null)
         element.removeAttribute(name);
@@ -1867,6 +1863,7 @@ Element.Methods = {
 
   descendantOf: function(element, ancestor) {
     element = $(element), ancestor = $(ancestor);
+    var originalAncestor = ancestor;
 
     if (element.compareDocumentPosition)
       return (element.compareDocumentPosition(ancestor) & 8) === 8;
@@ -1878,11 +1875,12 @@ Element.Methods = {
         do { ancestor = ancestor.parentNode; }
         while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
       }
-      if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
+      if (nextAncestor && nextAncestor.sourceIndex)
+       return (e > a && e < nextAncestor.sourceIndex);
     }
 
     while (element = element.parentNode)
-      if (element == ancestor) return true;
+      if (element == originalAncestor) return true;
     return false;
   },
 
@@ -1921,7 +1919,7 @@ Element.Methods = {
       if (property == 'opacity') element.setOpacity(styles[property]);
       else
         elementStyle[(property == 'float' || property == 'cssFloat') ?
-          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
             property] = styles[property];
 
     return element;
@@ -2022,7 +2020,7 @@ Element.Methods = {
       if (element) {
         if (element.tagName == 'BODY') break;
         var p = Element.getStyle(element, 'position');
-        if (p == 'relative' || p == 'absolute') break;
+        if (p !== 'static') break;
       }
     } while (element);
     return Element._returnOffset(valueL, valueT);
@@ -2171,72 +2169,75 @@ Element._attributeTranslations = {
   }
 };
 
-
-if (!document.createRange || Prototype.Browser.Opera) {
-  Element.Methods.insert = function(element, insertions) {
-    element = $(element);
-
-    if (Object.isString(insertions) || Object.isNumber(insertions) ||
-        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
-          insertions = { bottom: insertions };
-
-    var t = Element._insertionTranslations, content, position, pos, tagName;
-
-    for (position in insertions) {
-      content  = insertions[position];
-      position = position.toLowerCase();
-      pos      = t[position];
-
-      if (content && content.toElement) content = content.toElement();
-      if (Object.isElement(content)) {
-        pos.insert(element, content);
-        continue;
-      }
-
-      content = Object.toHTML(content);
-      tagName = ((position == 'before' || position == 'after')
-        ? element.parentNode : element).tagName.toUpperCase();
-
-      if (t.tags[tagName]) {
-        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
-        if (position == 'top' || position == 'after') fragments.reverse();
-        fragments.each(pos.insert.curry(element));
+if (Prototype.Browser.Opera) {
+  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+    function(proceed, element, style) {
+      switch (style) {
+        case 'left': case 'top': case 'right': case 'bottom':
+          if (proceed(element, 'position') === 'static') return null;
+        case 'height': case 'width':
+          // returns '0px' for hidden elements; we want it to return null
+          if (!Element.visible(element)) return null;
+
+          // returns the border-box dimensions rather than the content-box
+          // dimensions, so we subtract padding and borders from the value
+          var dim = parseInt(proceed(element, style), 10);
+
+          if (dim !== element['offset' + style.capitalize()])
+            return dim + 'px';
+
+          var properties;
+          if (style === 'height') {
+            properties = ['border-top-width', 'padding-top',
+             'padding-bottom', 'border-bottom-width'];
+          }
+          else {
+            properties = ['border-left-width', 'padding-left',
+             'padding-right', 'border-right-width'];
+          }
+          return properties.inject(dim, function(memo, property) {
+            var val = proceed(element, property);
+            return val === null ? memo : memo - parseInt(val, 10);
+          }) + 'px';
+        default: return proceed(element, style);
       }
-      else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
-
-      content.evalScripts.bind(content).defer();
     }
+  );
 
-    return element;
-  };
-}
-
-if (Prototype.Browser.Opera) {
-  Element.Methods._getStyle = Element.Methods.getStyle;
-  Element.Methods.getStyle = function(element, style) {
-    switch(style) {
-      case 'left':
-      case 'top':
-      case 'right':
-      case 'bottom':
-        if (Element._getStyle(element, 'position') == 'static') return null;
-      default: return Element._getStyle(element, style);
+  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+    function(proceed, element, attribute) {
+      if (attribute === 'title') return element.title;
+      return proceed(element, attribute);
     }
-  };
-  Element.Methods._readAttribute = Element.Methods.readAttribute;
-  Element.Methods.readAttribute = function(element, attribute) {
-    if (attribute == 'title') return element.title;
-    return Element._readAttribute(element, attribute);
-  };
+  );
 }
 
 else if (Prototype.Browser.IE) {
-  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
+  // IE doesn't report offsets correctly for static elements, so we change them
+  // to "relative" to get the values, then change them back.
+  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+    function(proceed, element) {
+      element = $(element);
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    }
+  );
+
+  $w('positionedOffset viewportOffset').each(function(method) {
     Element.Methods[method] = Element.Methods[method].wrap(
       function(proceed, element) {
         element = $(element);
         var position = element.getStyle('position');
-        if (position != 'static') return proceed(element);
+        if (position !== 'static') return proceed(element);
+        // Trigger hasLayout on the offset parent so that IE6 reports
+        // accurate offsetTop and offsetLeft values for position: fixed.
+        var offsetParent = element.getOffsetParent();
+        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+          offsetParent.setStyle({ zoom: 1 });
         element.setStyle({ position: 'relative' });
         var value = proceed(element);
         element.setStyle({ position: position });
@@ -2301,7 +2302,7 @@ else if (Prototype.Browser.IE) {
           return node ? node.value : "";
         },
         _getEv: function(element, attribute) {
-          var attribute = element.getAttribute(attribute);
+          attribute = element.getAttribute(attribute);
           return attribute ? attribute.toString().slice(23, -2) : null;
         },
         _flag: function(element, attribute) {
@@ -2318,7 +2319,10 @@ else if (Prototype.Browser.IE) {
   };
 
   Element._attributeTranslations.write = {
-    names: Object.clone(Element._attributeTranslations.read.names),
+    names: Object.extend({
+      cellpadding: 'cellPadding',
+      cellspacing: 'cellSpacing'
+    }, Element._attributeTranslations.read.names),
     values: {
       checked: function(element, value) {
         element.checked = !!value;
@@ -2398,7 +2402,7 @@ else if (Prototype.Browser.WebKit) {
   };
 
   // Safari returns margins on body which is incorrect if the child is absolutely
-  // positioned.  For performance reasons, redefine Position.cumulativeOffset for
+  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
   // KHTML/WebKit only.
   Element.Methods.cumulativeOffset = function(element) {
     var valueT = 0, valueL = 0;
@@ -2438,7 +2442,7 @@ if (Prototype.Browser.IE || Prototype.Browser.Opera) {
   };
 }
 
-if (document.createElement('div').outerHTML) {
+if ('outerHTML' in document.createElement('div')) {
   Element.Methods.replace = function(element, content) {
     element = $(element);
 
@@ -2476,45 +2480,25 @@ Element._returnOffset = function(l, t) {
 
 Element._getContentFromAnonymousElement = function(tagName, html) {
   var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
-  div.innerHTML = t[0] + html + t[1];
-  t[2].times(function() { div = div.firstChild });
+  if (t) {
+    div.innerHTML = t[0] + html + t[1];
+    t[2].times(function() { div = div.firstChild });
+  } else div.innerHTML = html;
   return $A(div.childNodes);
 };
 
 Element._insertionTranslations = {
-  before: {
-    adjacency: 'beforeBegin',
-    insert: function(element, node) {
-      element.parentNode.insertBefore(node, element);
-    },
-    initializeRange: function(element, range) {
-      range.setStartBefore(element);
-    }
+  before: function(element, node) {
+    element.parentNode.insertBefore(node, element);
   },
-  top: {
-    adjacency: 'afterBegin',
-    insert: function(element, node) {
-      element.insertBefore(node, element.firstChild);
-    },
-    initializeRange: function(element, range) {
-      range.selectNodeContents(element);
-      range.collapse(true);
-    }
+  top: function(element, node) {
+    element.insertBefore(node, element.firstChild);
   },
-  bottom: {
-    adjacency: 'beforeEnd',
-    insert: function(element, node) {
-      element.appendChild(node);
-    }
+  bottom: function(element, node) {
+    element.appendChild(node);
   },
-  after: {
-    adjacency: 'afterEnd',
-    insert: function(element, node) {
-      element.parentNode.insertBefore(node, element.nextSibling);
-    },
-    initializeRange: function(element, range) {
-      range.setStartAfter(element);
-    }
+  after: function(element, node) {
+    element.parentNode.insertBefore(node, element.nextSibling);
   },
   tags: {
     TABLE:  ['<table>',                '</table>',                   1],
@@ -2526,7 +2510,6 @@ Element._insertionTranslations = {
 };
 
 (function() {
-  this.bottom.initializeRange = this.top.initializeRange;
   Object.extend(this.tags, {
     THEAD: this.tags.TBODY,
     TFOOT: this.tags.TBODY,
@@ -2687,10 +2670,11 @@ Element.addMethods = function(methods) {
 document.viewport = {
   getDimensions: function() {
     var dimensions = { };
+    var B = Prototype.Browser;
     $w('width height').each(function(d) {
       var D = d.capitalize();
-      dimensions[d] = self['inner' + D] ||
-       (document.documentElement['client' + D] || document.body['client' + D]);
+      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
     });
     return dimensions;
   },
@@ -2719,9 +2703,26 @@ var Selector = Class.create({
     this.compileMatcher();
   },
 
+  shouldUseXPath: function() {
+    if (!Prototype.BrowserFeatures.XPath) return false;
+
+    var e = this.expression;
+
+    // Safari 3 chokes on :*-of-type and :empty
+    if (Prototype.Browser.WebKit &&
+     (e.include("-of-type") || e.include(":empty")))
+      return false;
+
+    // XPath can't do namespaced attributes, nor can it read
+    // the "checked" property from DOM nodes
+    if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+      return false;
+
+    return true;
+  },
+
   compileMatcher: function() {
-    // Selectors with namespaced attributes can't use the XPath version
-    if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
+    if (this.shouldUseXPath())
       return this.compileXPathMatcher();
 
     var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
@@ -2844,8 +2845,12 @@ Object.extend(Selector, {
     },
     className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
     id:           "[@id='#{1}']",
-    attrPresence: "[@#{1}]",
+    attrPresence: function(m) {
+      m[1] = m[1].toLowerCase();
+      return new Template("[@#{1}]").evaluate(m);
+    },
     attr: function(m) {
+      m[1] = m[1].toLowerCase();
       m[3] = m[5] || m[6];
       return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
     },
@@ -2874,7 +2879,7 @@ Object.extend(Selector, {
       'enabled':     "[not(@disabled)]",
       'not': function(m) {
         var e = m[6], p = Selector.patterns,
-            x = Selector.xpath, le, m, v;
+            x = Selector.xpath, le, v;
 
         var exclusion = [];
         while (e && le != e && (/\S/).test(e)) {
@@ -2931,13 +2936,13 @@ Object.extend(Selector, {
   },
 
   criteria: {
-    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
-    className:    'n = h.className(n, r, "#{1}", c); c = false;',
-    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
-    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
+    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
     attr: function(m) {
       m[3] = (m[5] || m[6]);
-      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
     },
     pseudo: function(m) {
       if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
@@ -2961,7 +2966,8 @@ Object.extend(Selector, {
     tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
     id:           /^#([\w\-\*]+)(\b|$)/,
     className:    /^\.([\w\-\*]+)(\b|$)/,
-    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
+    pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
     attrPresence: /^\[([\w]+)\]/,
     attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
   },
@@ -2986,7 +2992,7 @@ Object.extend(Selector, {
 
     attr: function(element, matches) {
       var nodeValue = Element.readAttribute(element, matches[1]);
-      return Selector.operators[matches[2]](nodeValue, matches[3]);
+      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
     }
   },
 
@@ -3001,14 +3007,15 @@ Object.extend(Selector, {
 
     // marks an array of nodes for counting
     mark: function(nodes) {
+      var _true = Prototype.emptyFunction;
       for (var i = 0, node; node = nodes[i]; i++)
-        node._counted = true;
+        node._countedByPrototype = _true;
       return nodes;
     },
 
     unmark: function(nodes) {
       for (var i = 0, node; node = nodes[i]; i++)
-        node._counted = undefined;
+        node._countedByPrototype = undefined;
       return nodes;
     },
 
@@ -3016,15 +3023,15 @@ Object.extend(Selector, {
     // "ofType" flag indicates whether we're indexing for nth-of-type
     // rather than nth-child
     index: function(parentNode, reverse, ofType) {
-      parentNode._counted = true;
+      parentNode._countedByPrototype = Prototype.emptyFunction;
       if (reverse) {
         for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
           var node = nodes[i];
-          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
         }
       } else {
         for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
-          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
       }
     },
 
@@ -3033,8 +3040,8 @@ Object.extend(Selector, {
       if (nodes.length == 0) return nodes;
       var results = [], n;
       for (var i = 0, l = nodes.length; i < l; i++)
-        if (!(n = nodes[i])._counted) {
-          n._counted = true;
+        if (!(n = nodes[i])._countedByPrototype) {
+          n._countedByPrototype = Prototype.emptyFunction;
           results.push(Element.extend(n));
         }
       return Selector.handlers.unmark(results);
@@ -3051,7 +3058,7 @@ Object.extend(Selector, {
     child: function(nodes) {
       var h = Selector.handlers;
       for (var i = 0, results = [], node; node = nodes[i]; i++) {
-        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+        for (var j = 0, child; child = node.childNodes[j]; j++)
           if (child.nodeType == 1 && child.tagName != '!') results.push(child);
       }
       return results;
@@ -3086,7 +3093,7 @@ Object.extend(Selector, {
 
     // TOKEN FUNCTIONS
     tagName: function(nodes, root, tagName, combinator) {
-      tagName = tagName.toUpperCase();
+      var uTagName = tagName.toUpperCase();
       var results = [], h = Selector.handlers;
       if (nodes) {
         if (combinator) {
@@ -3099,7 +3106,7 @@ Object.extend(Selector, {
           if (tagName == "*") return nodes;
         }
         for (var i = 0, node; node = nodes[i]; i++)
-          if (node.tagName.toUpperCase() == tagName) results.push(node);
+          if (node.tagName.toUpperCase() === uTagName) results.push(node);
         return results;
       } else return root.getElementsByTagName(tagName);
     },
@@ -3146,16 +3153,18 @@ Object.extend(Selector, {
       return results;
     },
 
-    attrPresence: function(nodes, root, attr) {
+    attrPresence: function(nodes, root, attr, combinator) {
       if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
       var results = [];
       for (var i = 0, node; node = nodes[i]; i++)
         if (Element.hasAttribute(node, attr)) results.push(node);
       return results;
     },
 
-    attr: function(nodes, root, attr, value, operator) {
+    attr: function(nodes, root, attr, value, operator, combinator) {
       if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
       var handler = Selector.operators[operator], results = [];
       for (var i = 0, node; node = nodes[i]; i++) {
         var nodeValue = Element.readAttribute(node, attr);
@@ -3234,7 +3243,7 @@ Object.extend(Selector, {
       var h = Selector.handlers, results = [], indexed = [], m;
       h.mark(nodes);
       for (var i = 0, node; node = nodes[i]; i++) {
-        if (!node.parentNode._counted) {
+        if (!node.parentNode._countedByPrototype) {
           h.index(node.parentNode, reverse, ofType);
           indexed.push(node.parentNode);
         }
@@ -3272,7 +3281,7 @@ Object.extend(Selector, {
       var exclusions = new Selector(selector).findElements(root);
       h.mark(exclusions);
       for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (!node._counted) results.push(node);
+        if (!node._countedByPrototype) results.push(node);
       h.unmark(exclusions);
       return results;
     },
@@ -3306,11 +3315,19 @@ Object.extend(Selector, {
     '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
   },
 
+  split: function(expression) {
+    var expressions = [];
+    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    return expressions;
+  },
+
   matchElements: function(elements, expression) {
-    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    var matches = $$(expression), h = Selector.handlers;
     h.mark(matches);
     for (var i = 0, results = [], element; element = elements[i]; i++)
-      if (element._counted) results.push(element);
+      if (element._countedByPrototype) results.push(element);
     h.unmark(matches);
     return results;
   },
@@ -3323,10 +3340,7 @@ Object.extend(Selector, {
   },
 
   findChildElements: function(element, expressions) {
-    var exprs = expressions.join(','), expressions = [];
-    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
-      expressions.push(m[1].strip());
-    });
+    expressions = Selector.split(expressions.join(','));
     var results = [], h = Selector.handlers;
     for (var i = 0, l = expressions.length, selector; i < l; i++) {
       selector = new Selector(expressions[i].strip());
@@ -3336,6 +3350,25 @@ Object.extend(Selector, {
   }
 });
 
+if (Prototype.Browser.IE) {
+  Object.extend(Selector.handlers, {
+    // IE returns comment nodes on getElementsByTagName("*").
+    // Filter them out.
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        if (node.tagName !== "!") a.push(node);
+      return a;
+    },
+
+    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node.removeAttribute('_countedByPrototype');
+      return nodes;
+    }
+  });
+}
+
 function $$() {
   return Selector.findChildElements(document, $A(arguments));
 }
@@ -3347,7 +3380,7 @@ var Form = {
 
   serializeElements: function(elements, options) {
     if (typeof options != 'object') options = { hash: !!options };
-    else if (options.hash === undefined) options.hash = true;
+    else if (Object.isUndefined(options.hash)) options.hash = true;
     var key, value, submitted = false, submit = options.submit;
 
     var data = elements.inject({ }, function(result, element) {
@@ -3545,17 +3578,17 @@ Form.Element.Serializers = {
   },
 
   inputSelector: function(element, value) {
-    if (value === undefined) return element.checked ? element.value : null;
+    if (Object.isUndefined(value)) return element.checked ? element.value : null;
     else element.checked = !!value;
   },
 
   textarea: function(element, value) {
-    if (value === undefined) return element.value;
+    if (Object.isUndefined(value)) return element.value;
     else element.value = value;
   },
 
   select: function(element, index) {
-    if (index === undefined)
+    if (Object.isUndefined(index))
       return this[element.type == 'select-one' ?
         'selectOne' : 'selectMany'](element);
     else {
@@ -3746,7 +3779,9 @@ Event.Methods = (function() {
 
     findElement: function(event, expression) {
       var element = Event.element(event);
-      return element.match(expression) ? element : element.up(expression);
+      if (!expression) return element;
+      var elements = [element].concat(element.ancestors());
+      return Selector.findElement(elements, expression, 0);
     },
 
     pointer: function(event) {
@@ -3809,9 +3844,9 @@ Object.extend(Event, (function() {
   var cache = Event.cache;
 
   function getEventID(element) {
-    if (element._eventID) return element._eventID;
+    if (element._prototypeEventID) return element._prototypeEventID[0];
     arguments.callee.id = arguments.callee.id || 1;
-    return element._eventID = ++arguments.callee.id;
+    return element._prototypeEventID = [++arguments.callee.id];
   }
 
   function getDOMEventName(eventName) {
@@ -3839,7 +3874,7 @@ Object.extend(Event, (function() {
           return false;
 
       Event.extend(event);
-      handler.call(element, event)
+      handler.call(element, event);
     };
 
     wrapper.handler = handler;
@@ -3921,11 +3956,12 @@ Object.extend(Event, (function() {
       if (element == document && document.createEvent && !element.dispatchEvent)
         element = document.documentElement;
 
+      var event;
       if (document.createEvent) {
-        var event = document.createEvent("HTMLEvents");
+        event = document.createEvent("HTMLEvents");
         event.initEvent("dataavailable", true, true);
       } else {
-        var event = document.createEventObject();
+        event = document.createEventObject();
         event.eventType = "ondataavailable";
       }
 
@@ -3938,7 +3974,7 @@ Object.extend(Event, (function() {
         element.fireEvent(event.eventType, event);
       }
 
-      return event;
+      return Event.extend(event);
     }
   };
 })());
@@ -3954,20 +3990,21 @@ Element.addMethods({
 Object.extend(document, {
   fire:          Element.Methods.fire.methodize(),
   observe:       Element.Methods.observe.methodize(),
-  stopObserving: Element.Methods.stopObserving.methodize()
+  stopObserving: Element.Methods.stopObserving.methodize(),
+  loaded:        false
 });
 
 (function() {
   /* Support for the DOMContentLoaded event is based on work by Dan Webb,
      Matthias Miller, Dean Edwards and John Resig. */
 
-  var timer, fired = false;
+  var timer;
 
   function fireContentLoadedEvent() {
-    if (fired) return;
+    if (document.loaded) return;
     if (timer) window.clearInterval(timer);
     document.fire("dom:loaded");
-    fired = true;
+    document.loaded = true;
   }
 
   if (document.addEventListener) {
@@ -4181,4 +4218,4 @@ Object.extend(Element.ClassNames.prototype, Enumerable);
 
 /*--------------------------------------------------------------------------*/
 
-Element.addMethods();
\ No newline at end of file
+Element.addMethods();