2009-05-27 Maciej Stachowiak <mjs@apple.com>
authormjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 May 2009 20:04:40 +0000 (20:04 +0000)
committermjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 May 2009 20:04:40 +0000 (20:04 +0000)
        Not reviewed, demo content.

        - add Calendar demo.

        * demos/calendar: Added.
        * demos/calendar/Boom.aiff: Added.
        * demos/calendar/Calendar.css: Added.
        * demos/calendar/Calendar.html: Added.
        * demos/calendar/Calendar.js: Added.
        * demos/calendar/Calendar.manifest: Added.
        * demos/calendar/CalendarApp.icns: Added.
        * demos/calendar/Images: Added.
        * demos/calendar/Images/AirPort4.png: Added.
        * demos/calendar/Images/AirPortError.png: Added.
        * demos/calendar/Images/disclosureTriangleSmallDown.png: Added.
        * demos/calendar/Images/disclosureTriangleSmallRight.png: Added.
        * demos/calendar/Images/statusbarBackground.png: Added.
        * demos/calendar/Images/statusbarResizerVertical.png: Added.
        * demos/calendar/LocationImage.js: Added.
        * demos/calendar/Utilities.js: Added.
        * demos/calendar/favicon.ico: Added.
        * demos/calendar/mime.types: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@44198 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
WebKitSite/ChangeLog
WebKitSite/demos/calendar/Boom.aiff [new file with mode: 0644]
WebKitSite/demos/calendar/Calendar.css [new file with mode: 0644]
WebKitSite/demos/calendar/Calendar.html [new file with mode: 0644]
WebKitSite/demos/calendar/Calendar.js [new file with mode: 0644]
WebKitSite/demos/calendar/Calendar.manifest [new file with mode: 0644]
WebKitSite/demos/calendar/CalendarApp.icns [new file with mode: 0644]
WebKitSite/demos/calendar/Images/AirPort4.png [new file with mode: 0644]
WebKitSite/demos/calendar/Images/AirPortError.png [new file with mode: 0644]
WebKitSite/demos/calendar/Images/disclosureTriangleSmallDown.png [new file with mode: 0644]
WebKitSite/demos/calendar/Images/disclosureTriangleSmallRight.png [new file with mode: 0644]
WebKitSite/demos/calendar/Images/statusbarBackground.png [new file with mode: 0644]
WebKitSite/demos/calendar/Images/statusbarResizerVertical.png [new file with mode: 0644]
WebKitSite/demos/calendar/LocationImage.js [new file with mode: 0644]
WebKitSite/demos/calendar/Utilities.js [new file with mode: 0644]
WebKitSite/demos/calendar/favicon.ico [new file with mode: 0644]
WebKitSite/demos/calendar/mime.types [new file with mode: 0644]

index 8a7d086..d1c2620 100644 (file)
@@ -1,3 +1,28 @@
+2009-05-27  Maciej Stachowiak  <mjs@apple.com>
+
+        Not reviewed, demo content.
+        
+        - add Calendar demo.
+
+        * demos/calendar: Added.
+        * demos/calendar/Boom.aiff: Added.
+        * demos/calendar/Calendar.css: Added.
+        * demos/calendar/Calendar.html: Added.
+        * demos/calendar/Calendar.js: Added.
+        * demos/calendar/Calendar.manifest: Added.
+        * demos/calendar/CalendarApp.icns: Added.
+        * demos/calendar/Images: Added.
+        * demos/calendar/Images/AirPort4.png: Added.
+        * demos/calendar/Images/AirPortError.png: Added.
+        * demos/calendar/Images/disclosureTriangleSmallDown.png: Added.
+        * demos/calendar/Images/disclosureTriangleSmallRight.png: Added.
+        * demos/calendar/Images/statusbarBackground.png: Added.
+        * demos/calendar/Images/statusbarResizerVertical.png: Added.
+        * demos/calendar/LocationImage.js: Added.
+        * demos/calendar/Utilities.js: Added.
+        * demos/calendar/favicon.ico: Added.
+        * demos/calendar/mime.types: Added.
+
 2009-05-20  Mark Rowe  <mrowe@apple.com>
 
         Rubber-stamped by Eric Seidel.
diff --git a/WebKitSite/demos/calendar/Boom.aiff b/WebKitSite/demos/calendar/Boom.aiff
new file mode 100644 (file)
index 0000000..a73f51a
Binary files /dev/null and b/WebKitSite/demos/calendar/Boom.aiff differ
diff --git a/WebKitSite/demos/calendar/Calendar.css b/WebKitSite/demos/calendar/Calendar.css
new file mode 100644 (file)
index 0000000..1cefe40
--- /dev/null
@@ -0,0 +1,384 @@
+body {
+    font-family: "Helvetica";
+    font-size: 11px;
+    cursor: default;
+}
+
+*[contentEditable="true"] {
+    cursor: text;
+}
+
+#sidePanel {
+    position: absolute;
+    left: 0;
+    top: 0;
+    bottom: 0;
+    width: 170px;
+    background: #d4dde6;
+    border-right: 1px solid #a5a5a5;
+    margin-left: 0;
+    margin-right: 0;
+    overflow: hidden;
+}
+
+#dividerBar {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 24px;
+    background-image: url(Images/statusbarResizerVertical.png), url(Images/statusbarBackground.png);
+    background-repeat: no-repeat, repeat-x;
+    background-position: right center, center;
+    cursor: row-resize;
+}
+
+#searchArea {
+    position: absolute;
+    background-color: #f1f1f1;
+    top: 100%;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+input[type="search"] {
+    -webkit-appearance: searchfield;
+}
+
+#searchField {
+    position: absolute;
+    left: 10px;
+    right: 10px;
+    top: 10px;
+}
+
+#searchResults {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 40px;
+    bottom: 0;
+    margin: 0;
+    padding: 0;
+    overflow: auto;
+    list-style-position: inside;
+}
+
+#searchResults li {
+    padding: 10px;
+}
+
+.colorIndicator {
+    display: inline-block;
+    width: 20px;
+    height: 1em;
+    vertical-align: -2px;
+    border: 1px black solid;
+    margin-right: 5px;
+}
+
+.colorIndicator.home {
+    background-color: #00a700
+}
+
+.colorIndicator.work {
+    background-color: #004ee2
+}
+
+#gridView {
+    position: absolute;
+    left: 171px;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    background: #ffffff;
+}
+
+#gridView.inactive {
+    opacity: 0.5;
+}
+
+#eventOverlay {
+    position: absolute;
+    left: 171px;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    background: rgba(0, 0, 0, 0.2);
+    z-index: auto;
+    opacity: 0;
+    -webkit-transition: opacity 0.2s ease-in;
+}
+
+#eventOverlay.show {
+    opacity: 1;
+    -webkit-transition-property: opacity, z-index;
+    -webkit-transition-duration: 1s, 0;
+    -webkit-transition-delay: 0, 0.5s;
+    -webkit-transition-timing-function: ease-out, linear;
+    z-index: 1000;
+}
+
+#details {
+    position: absolute;
+    background: white;
+    left: 30%;
+    right: 30%;
+    top: 20%;
+    bottom: 20%;
+    overflow: hidden;
+    -webkit-box-shadow: 0 8px 12px rgba(0, 0, 0, 0.5);
+    -webkit-border-radius: 8px;
+    padding: 30px;
+    font-size: 12px;
+    -webkit-transition-property: top, bottom;
+    -webkit-transition-duration: 0.5s, 0.5s;
+}
+
+#eventTitle {
+    font-size: 20px;
+}
+
+#details *:focus {
+    outline: none;
+    -webkit-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
+}
+
+#details dt {
+    width: 20%;
+    float: left;
+    clear: both;
+    color: #999;
+    text-align: right;
+    margin-top: 10px;
+}
+
+#details dd {
+    display: inline-block;
+    width: 70%;
+    float: left;
+    margin-left: 5%;
+    margin-top: 10px;
+    display: inline-block;
+    text-align: left;
+}
+
+#details input {
+    position: absolute;
+    bottom: 15px;
+    right: 15px;
+}
+
+#eventDisclosureArrow {
+    display: none;
+    float: left;
+    width: 12px;
+    height: 12px;
+    background-position: 1px left;
+    background-repeat: no-repeat;
+    background-image: url("Images/disclosureTriangleSmallRight.png");
+}
+
+#eventDisclosureArrow.show {
+    display: inline-block;
+}
+
+#eventDisclosureArrow.expanded {
+    background-image: url("Images/disclosureTriangleSmallDown.png");
+}
+
+#map {
+    display: none;
+    width: 100%;
+    min-width: 200px;
+    height: 180px;
+    overflow: hidden;
+    opacity: 0;
+    -webkit-transition: opacity 1s linear;
+}
+
+#map.show {
+    opacity: 1;
+    display: block;
+}
+
+#mapImage {
+    width: 100%;
+}
+
+#details.showingMap {
+    top: 10%;
+    bottom: 10%;
+    -webkit-transition-property: top, bottom;
+    -webkit-transition-duration: 0.5s, 0.5s;
+}
+
+#eventFrom .time, #eventTo .time {
+    display: inline-block;
+    min-width: 5px;
+    max-width: 20px;
+}
+
+#sidePanel h3 {
+    color: #5c6e91;
+    font-size: 11px;
+    text-shadow: 0.1em 0.1em #e5ebee;
+    margin-left: 20px;
+    margin-bottom: 4px;
+}
+
+#calendarList {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+#calendarList li {
+    padding-left: 23px;
+}
+
+#calendarList li.selected {
+    border-top: 1px solid #4580c8;
+    background: -webkit-gradient(linear, left top, left bottom, from(#5b93d5), to(#1553aa));
+    color: white;
+    font-weight: bold;
+    text-shadow: 0.1em 0.1em #6c6c6c;
+}
+
+#calendarList input {
+    margin-right: 5px;
+}
+
+#gridView h2 {
+    margin-top: 8px;
+    margin-bottom: 5px;
+    text-align: center;
+    color: #3f3f3f;
+    font-size: 19px;
+    font-weight: bolder;
+}
+
+#gridView .navButtonGroup {
+    text-align: center;
+}
+
+.navButton {
+    font-size: 14px;
+    font-weight: bold;
+    vertical-align: top;
+}
+
+#daysHeader {
+    margin-top: 5px;
+}
+
+.day {
+    display: inline-block;
+    width: 14%;
+    float: left;
+    -webkit-box-sizing: border-box;
+}
+
+/* REVIEW - trying to get these days to have their widths nicely divided */
+.day:nth-child(7n+1), .day:nth-child(7n+7) {
+    width: 15%;
+}
+
+.day.title {
+    color: #626262;
+    text-align: center;
+}
+
+#daysGrid {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 80px;
+    bottom: 0;
+}
+
+.day.box {
+    height: 20%;
+    border: 1px #ccc;
+    border-top-style: solid;
+    border-right-style: solid;
+    position: relative;
+}
+
+.day.box.today {
+    background-color: #e5ecf7;
+}
+
+.day.box:nth-child(7n+7) {
+    border-right-style: none;
+}
+
+.day .date {
+    color: #212121;
+    text-align: right;
+    font-size: 12px;
+    padding-right: 5px;
+    padding-top: 5px;
+}
+
+.day.box.notThisMonth .date {
+    color: #a6a6a6;
+}
+
+.day .contents {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 20px;
+    bottom: 0;
+    overflow: auto;
+    margin-top: 0;
+    margin-bottom: 0;
+    margin-left: 0;
+    padding: 2px 0;
+    list-style-position: inside;
+    color: orange;
+}
+
+.day .contents li {
+    background-color: rgba(255, 255, 255, 0);
+    padding-left: 8px;
+    padding-right: 8px;
+}
+
+li.home {
+    color: #00a700;
+}
+
+li.home.selected {
+    color: #fff;
+    background: #00a700;
+}
+
+li.work {
+    color: #004ee2;
+}
+
+li.work.selected {
+    color: #fff;
+    background: #004ee2;
+}
+
+li.highlighted {
+    background-color: #ffa !important;
+}
+
+/* Online Status */
+#onlineStatusIcon {
+    width: 44px;
+    height: 32px;
+    margin-right: 8px;
+    display: inline-block;
+    float: right;
+    background-image: url("Images/AirPort4.png");
+}
+
+#onlineStatusIcon.offline {
+    background-image: url("Images/AirPortError.png");
+}
diff --git a/WebKitSite/demos/calendar/Calendar.html b/WebKitSite/demos/calendar/Calendar.html
new file mode 100644 (file)
index 0000000..2cda08b
--- /dev/null
@@ -0,0 +1,106 @@
+<!-- Application Cache Manifest -->
+<html manifest="Calendar.manifest">
+
+<head>
+
+<!-- Application Name -->
+<meta name="application-name" content="Offline Calendar">
+
+<!-- Application Icon -->
+<link rel="icon" href="surfin-safari.icns" sizes="32x32 128x128 512x512">
+
+<!-- Normal favicon -->
+<link rel="icon" href="favicon.ico" sizes="16x16">
+
+<title>Calendar</title>
+<link rel="stylesheet" type="text/css" href="Calendar.css">
+<script type="text/javascript" src="LocationImage.js"></script>
+<script type="text/javascript" src="Utilities.js"></script>
+<script type="text/javascript" src="Calendar.js"></script>
+</head>
+
+<body onload="pageLoaded()">
+
+<div id="sidePanel">
+    <h3>CALENDARS</h3>
+    <ul id="calendarList">
+        <li class="selected" onclick="calendarSelected(event)">
+            <span class="colorIndicator home"></span><input type="checkbox" id="home" onchange="calendarClicked(event)">Home
+        </li>
+        <li onclick="calendarSelected(event)">
+            <span class="colorIndicator work"></span><input type="checkbox" id="work" onchange="calendarClicked(event)">Work
+        </li>
+    </ul>
+    
+    <div id="dividerBar"></div>
+    
+    <div id="searchArea">
+        <input type="search" id="searchField" results="0" incremental="1" placeholder="" value="" onsearch="searchForEvent(this.value)">
+        <ul id="searchResults">
+        </ul>
+    </div>
+</div>
+
+<div id="eventOverlay">
+    <div id="details">
+        <div id="eventTitle" contenteditable="true"></div>
+        <dl>
+            <dt>location</dt>
+            <dd>
+                <span id="eventDisclosureArrow" onclick="toggleArrow()"></span>
+                <div id="eventLocation" contentEditable="true" onfocus="hideMapDisclosureArrow()" onblur="requestMapImage()"></div>
+                <div id="map"></div>
+            </dd>
+            <dt>from</dt>
+            <dd id="eventFrom">
+                <span id="eventFromDate"></span>
+                <span id="eventFromHours" class="time" contentEditable="true">00</span>:<span id="eventFromMinutes" class="time" contentEditable="true">00</span>
+            </dd>
+            <dt>to</dt>
+            <dd id="eventTo">
+                <span id="eventToDate"></span>
+                <span id="eventToHours" class="time" contentEditable="true">00</span>:<span id="eventToMinutes" class="time" contentEditable="true">00</span>
+            </dd>
+            <dt style="line-height: 23px;">calendar</dt>
+            <dd>
+                <select id="eventCalendarType">
+                    <option value="home">Home</option>
+                    <option value="work">Work</option>
+                </select>
+            </dd>
+            <dt>details</dt>
+            <dd id="eventDetails" contentEditable="true"></dd>
+        </dl>
+        <input type="button" value="Done" onclick="eventDetailsDismissed()">
+    </div>
+</div>
+
+<div id="gridView">
+
+    <h2>
+        <span id="monthTitle"></span>
+        <span id="onlineStatusIcon"></span>
+    </h2>
+    
+    <div class="navButtonGroup">
+        <input class="navButton" type="button" onclick="previousMonth()" value="&lt;">
+        <input class="navButton" type="button" onclick="nextMonth()" value="&gt;">    
+    </div>
+    
+    <div id="daysHeader">
+        <span class="day title">Sunday</span>
+        <span class="day title">Monday</span>
+        <span class="day title">Tuesday</span>
+        <span class="day title">Wednesday</span>
+        <span class="day title">Thursday</span>
+        <span class="day title">Friday</span>
+        <span class="day title">Saturday</span>
+    </div>
+    
+    <div id="daysGrid">
+    </div>
+
+</div>
+
+</body>
+</html>
diff --git a/WebKitSite/demos/calendar/Calendar.js b/WebKitSite/demos/calendar/Calendar.js
new file mode 100644 (file)
index 0000000..db70c36
--- /dev/null
@@ -0,0 +1,910 @@
+var monthStrings = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
+// Date object that represents the currently displayed month
+var monthOnDisplay;
+// Table that maps a date (in milliseconds) to the corresponding Day object
+var dateToDayObjectMap;
+// Calendar type currently selected
+var selectedCalendarType = "home";
+// Calendar event currently selected
+var selectedCalendarEvent = null;
+// The highest ID number that we have assigned so far
+var highestID = 0;
+// Date (in milliseconds) of the first day displayed in the calendar
+var calendarStartTime = 0;
+// Date (in milliseconds) of the last day displayed in the calendar
+var calendarEndTime = 0;
+// Audio sound for network connection lost event
+var networkDropSound = new Audio("Boom.aiff");
+
+// Override day box height if the number of rows in calendar is not 5 
+var insertedStyleRuleIndexForDayBox = -1;
+function updateCalendarRowCount(count) 
+{
+    var stylesheet = document.styleSheets[0];
+    if (count == 5) {   // The case our stylesheet already handles, and probably the most common case
+        if (insertedStyleRuleIndexForDayBox >= 0) {
+            // Remove the style rule we previously added
+            stylesheet.deleteRule(insertedStyleRuleIndexForDayBox);
+        }
+        insertedStyleRuleIndexForDayBox = -1;
+        return;
+    }
+    if (insertedStyleRuleIndexForDayBox < 0)
+        insertedStyleRuleIndexForDayBox = stylesheet.insertRule(".day.box { }", stylesheet.cssRules.length);
+    var styleRule = stylesheet.cssRules[insertedStyleRuleIndexForDayBox];
+    var h = 100 / count;
+    styleRule.style.height = h + "%";
+}
+
+function initMonthOnDisplay() 
+{
+    monthOnDisplay = CalendarState.currentMonth();
+    monthOnDisplayUpdated();
+}
+
+function monthOnDisplayUpdated() 
+{
+    var monthTitle = monthStrings[monthOnDisplay.getMonth()] + " " + monthOnDisplay.getFullYear();
+    document.getElementById("monthTitle").innerText = monthTitle;
+    CalendarState.setCurrentMonth(monthOnDisplay.toString());
+    initDaysGrid();
+    CalendarDatabase.openWebKitCalendarEvents(); 
+}
+
+function initDaysGrid() 
+{
+    // Figure out the first day that should be displayed in this grid.
+    var displayedDate = new Date(monthOnDisplay);
+    displayedDate.setDate(displayedDate.getDate() - displayedDate.getDay());
+    
+    // Fill out the entire grid
+    var daysGrid = document.getElementById("daysGrid");
+    daysGrid.removeChildren();
+    dateToDayObjectMap = new Object();
+    
+    calendarStartTime = displayedDate.getTime();
+    var doneWithThisMonth = false;
+    var rows = 0;
+    while (!doneWithThisMonth) {
+        rows++;
+        for (var i = 0; i < 7; ++i) {
+            var dateTime = displayedDate.getTime();
+            var dayObj = new Day(displayedDate);
+            dateToDayObjectMap[dateTime] = dayObj;
+            dayObj.attach(daysGrid);
+            // Increment by a day
+            displayedDate.setDate(displayedDate.getDate() + 1);
+        }
+        doneWithThisMonth = (displayedDate.getMonth() != monthOnDisplay.getMonth());
+    }
+    updateCalendarRowCount(rows);
+    displayedDate.setDate(displayedDate.getDate() - 1);     // Roll back to the last displayed date
+    displayedDate.setHours(23, 59, 59, 999);
+    calendarEndTime = displayedDate.getTime();
+}
+
+function isCalendarTypeVisible(type) 
+{
+    var input = document.getElementById(type);
+    if (!input || input.tagName != "INPUT")
+        return;
+    return input.checked;
+}
+
+function addEventsOfCalendarType(calendarType)
+{
+    CalendarDatabase.loadEventsFromDBForCalendarType(calendarType);
+}
+
+function removeEventsOfCalendarType(calendarType)
+{
+    var daysGridElement = document.getElementById("daysGrid");
+    var dayElements = daysGridElement.childNodes;
+    for (var i = 0; i < dayElements.length; i++) {
+        var dayObj = dayObjectFromElement(dayElements[i]);
+        if (!dayObj)
+            continue;
+        dayObj.hideEventsOfCalendarType(calendarType);
+    }
+}
+
+// Event handlers --------------------------------------------------
+
+function pageLoaded() 
+{
+    document.getElementById("gridView").addEventListener("selectstart", stopEvent, true);
+    document.getElementById("searchResults").addEventListener("selectstart", stopEvent, true);
+    document.body.addEventListener("keyup", keyUpHandler, false);
+    document.body.addEventListener("online", displayOnlineStatus, true);
+    document.body.addEventListener("offline", displayOnlineStatus, true);
+    displayOnlineStatus();
+
+    initSearchArea();
+    // Initialize the checked states of the calendars
+    var calendarCheckboxes = document.getElementById("calendarList").getElementsByTagName("INPUT");
+    for (var i = 0; i < calendarCheckboxes.length; i++)
+        calendarCheckboxes[i].checked = CalendarState.calendarChecked(calendarCheckboxes[i].id);
+    // Initialize the calendar grid.
+    initMonthOnDisplay();
+}
+
+function previousMonth() 
+{
+    monthOnDisplay.setMonth(monthOnDisplay.getMonth() - 1);
+    monthOnDisplayUpdated();
+}
+
+function nextMonth() 
+{
+    monthOnDisplay.setMonth(monthOnDisplay.getMonth() + 1);
+    monthOnDisplayUpdated();
+}
+
+function calendarSelected(event) 
+{
+    if (event.target.tagName == "INPUT")
+        return;
+    var oldSelectedInput = document.getElementById(selectedCalendarType);
+    var oldListItemElement = oldSelectedInput.findParentOfTagName("LI");
+    oldListItemElement.removeStyleClass("selected");
+    event.target.addStyleClass("selected");
+    selectedCalendarType = event.target.getElementsByTagName("INPUT")[0].id;
+}
+
+function calendarClicked(event) 
+{
+    if (event.target.tagName != "INPUT")
+        return;
+    var calendarType = event.target.id;
+    var checked = event.target.checked;
+    CalendarState.setCalendarChecked(calendarType, checked);
+    if (checked)
+        addEventsOfCalendarType(calendarType);
+    else
+        removeEventsOfCalendarType(calendarType);
+}
+
+function keyUpHandler(event) 
+{
+    switch (event.keyIdentifier) {
+        case "U+007F":   // Delete key
+            if (selectedCalendarEvent && selectedCalendarEvent.day)
+                selectedCalendarEvent.day.deleteEvent(selectedCalendarEvent);
+            break;
+        case "Enter":
+            if (selectedCalendarEvent && !document.getElementById("eventOverlay").hasStyleClass("show"))
+                selectedCalendarEvent.selected();
+            break;
+        default:
+            break;
+    }
+}
+
+function eventDetailsDismissed(event) 
+{
+    if (selectedCalendarEvent) {
+        selectedCalendarEvent.listItemNode.removeStyleClass("selected");
+        // Update selected event
+        selectedCalendarEvent.detailsUpdated();
+    }
+    
+    // Hide event details
+    var eventOverlayElement = document.getElementById("eventOverlay");
+    // FIXME: would have added transitionend listener here but not supported yet.  Use setTimeout for now.
+    // Set timer to hide map and disclosure arrow - this is delayed so things wouldn't change while event details is fading out.
+    setTimeout("hideMapDisclosureArrow()", 1000);
+    eventOverlayElement.removeStyleClass("show");
+
+    selectedCalendarEvent = null;
+
+    // Show the gridView
+    document.getElementById("gridView").removeStyleClass("inactive");
+}
+
+function searchForEvent(query) 
+{
+    if (query.length == 0) {
+        var searchResultsList = document.getElementById("searchResults");
+        searchResultsList.removeChildren();
+        unhighlightAllEvents();
+        return;
+    }
+    query = "%" + query + "%";
+        
+    CalendarDatabase.queryEventsInDB(query);
+}
+
+// Online status ----------------------------------------------------
+
+function isOnline()
+{
+    return navigator.onLine;
+}
+
+function displayOnlineStatus()
+{
+    var statusIcon = document.getElementById("onlineStatusIcon");
+    if (isOnline())
+        statusIcon.removeStyleClass("offline");
+    else {
+        statusIcon.addStyleClass("offline");
+        networkDropSound.play();
+    }
+}
+
+// Map revelation ---------------------------------------------------
+
+function hideMap() 
+{
+    document.getElementById("eventDisclosureArrow").removeStyleClass("expanded");
+    document.getElementById("map").removeStyleClass("show");
+    document.getElementById("details").removeStyleClass("showingMap");
+}
+
+function showMap() 
+{
+    document.getElementById("eventDisclosureArrow").addStyleClass("expanded");
+    document.getElementById("map").addStyleClass("show");
+    document.getElementById("details").addStyleClass("showingMap");
+}
+
+function toggleArrow() 
+{
+    var newMapShowState = !document.getElementById("map").hasStyleClass("show");
+    if (newMapShowState)
+        showMap();
+    else
+        hideMap();
+}
+
+function hideMapDisclosureArrow() 
+{
+    document.getElementById("eventDisclosureArrow").removeStyleClass("show");
+    document.getElementById("map").removeChildren();
+    hideMap();
+}
+
+function showMapDisclosureArrow() 
+{
+    document.getElementById("eventDisclosureArrow").addStyleClass("show");
+}
+
+var locationImage;
+
+function mapImageReceived(image) 
+{
+    if (!image)
+        return;
+    image.addStyleClass("mapImage");
+    document.getElementById("map").appendChild(image);
+    showMapDisclosureArrow();
+}
+
+function requestMapImage() 
+{
+    if (!isOnline())
+        return;
+    if (!locationImage)
+        locationImage = new LocationImage();
+    var address = document.getElementById("eventLocation").innerText;
+    locationImage.requestLocationImage(address, mapImageReceived);
+}
+
+// Search area ------------------------------------------------------
+
+var dividerBarDragOffset = 0;
+var dividerBarHeight = 24;
+
+function initSearchArea() 
+{
+    document.getElementById("dividerBar").addEventListener("mousedown", startDividerBarDragging, true);
+}
+
+function startDividerBarDragging(event) 
+{
+    var dividerBarElement = document.getElementById("dividerBar");
+    if (event.target !== dividerBarElement)
+        return;
+
+    document.addEventListener("mousemove", dividerBarDragging, true);
+    document.addEventListener("mouseup", endDividerBarDragging, true);
+    
+    document.body.style.cursor = "row-resize";
+    
+    dividerBarDragOffset = event.pageY - document.getElementById("searchArea").totalOffsetTop;
+    stopEvent(event);
+}
+
+function dividerBarDragging(event) 
+{
+    var dividerBarElement = document.getElementById("dividerBar");
+    var searchAreaElement = document.getElementById("searchArea");
+    var sidePanelHeight = document.getElementById("sidePanel").offsetHeight;
+    var calendarListElement = document.getElementById("calendarList");
+    var calendarListBottom = calendarListElement.totalOffsetTop + calendarListElement.offsetHeight;
+    
+    var dividerTop = event.pageY + dividerBarDragOffset;
+    dividerTop = Number.constrain(dividerTop, calendarListBottom, sidePanelHeight - dividerBarHeight);
+    var searchAreaTop = dividerTop + dividerBarHeight;
+    
+    dividerBarElement.style.top = dividerTop + "px";
+    searchAreaElement.style.top = searchAreaTop + "px";
+
+    stopEvent(event);
+}
+
+function endDividerBarDragging(event) 
+{
+    document.removeEventListener("mousemove", dividerBarDragging, true);
+    document.removeEventListener("mouseup", endDividerBarDragging, true);
+
+    document.body.style.removeProperty("cursor");
+    dividerBarDragOffset = 0;
+    stopEvent(event);
+}
+
+// LocalStorage access ---------------------------------------------
+
+var CalendarState = {
+    currentMonth: function() 
+    {
+        var month = new Date();
+        // Retrieve the month on display saved from last time this app is launched.
+        if (localStorage.monthOnDisplay)
+            month.setTime(Date.parse(localStorage.monthOnDisplay));
+        // First time we load this page - just use the current month.
+        month.setDate(1);
+        month.setHours(0, 0, 0, 0);
+        return month;
+    },
+
+    setCurrentMonth: function(monthString) 
+    {
+        localStorage.monthOnDisplay = monthString;
+    },
+
+    toCalendarKey: function(calendarType) 
+    {
+        return calendarType + "CalendarChecked";
+    },
+
+    calendarChecked: function(calendarType) 
+    {
+        var value = localStorage.getItem(CalendarState.toCalendarKey(calendarType));
+        if (!value)
+            return true;
+        return (value == "yes") ? true : false;
+    },
+
+    setCalendarChecked: function(calendarType, checked) 
+    {
+        localStorage.setItem(CalendarState.toCalendarKey(calendarType), checked ? "yes" : "no");
+    },
+}
+
+// Database access -------------------------------------------------
+
+var CalendarDatabase = {
+    // The Events database object
+    db: null,
+    
+    // Flag that tracks if we have opened the database for the very first time
+    dbOpened: false,
+
+    // REVIEW: can probably have a method that takes in a SQL statement, the arguments to the statement, optional callback and error
+    // callback methods and execute the transaction on the database, and then have other methods just call that one method.
+    // But we'll spell out the steps to make the database transaction in each method here for demonstration purposes.
+
+    openWebKitCalendarEvents: function() 
+    {
+        // We have already made sure the WebKitCalendarEvents table has been created. Just load events directly.
+        if (CalendarDatabase.dbOpened) {
+            CalendarDatabase.loadEventsFromDB();
+            return;
+        }
+        CalendarDatabase.dbOpened = true;
+        
+        // Query for opening WebKitCalendarEvents table
+        var openTableStatement = "CREATE TABLE IF NOT EXISTS WebKitCalendarEvents (id REAL UNIQUE, eventTitle TEXT, eventLocation TEXT, startTime REAL, endTime REAL, eventCalendar TEXT, eventDetails TEXT)";
+        
+        // SQLStatementCallback - gets called after the table is created
+        function sqlStatementCallback(result) { CalendarDatabase.loadEventsFromDB(); };
+
+        // SQLStatementErrorCallback - gets called if there's an error opening the table
+        function sqlStatementErrorCallback(tx, err) { alert("Error opening WebKitCalendarEvents: " + err.message); };
+
+        // SQLTransactionCallback
+        function sqlTransactionCallback(tx) { tx.executeSql(openTableStatement, [], sqlStatementCallback, sqlStatementErrorCallback); };
+
+        CalendarDatabase.db.transaction(sqlTransactionCallback);
+    },
+
+    open: function() 
+    {
+        try {
+            if (!window.openDatabase) {
+                alert("Couldn't open the database.  Please try with a WebKit nightly with the database feature enabled.");
+                return;
+            }
+            CalendarDatabase.db = openDatabase("Events", "1.0", "Events Database", 1000000);
+            if (!CalendarDatabase.db)
+                alert("Failed to open the database on disk.");
+        } catch(err) { }
+    },
+
+    loadEventsFromDB: function() 
+    {
+        var self = this;
+
+        // SQL query to retrieve all the events for this month
+        var eventsQuery = "SELECT id, eventTitle, eventLocation, startTime, endTime, eventCalendar, eventDetails FROM WebKitCalendarEvents WHERE (startTime BETWEEN ? and ?)";
+        
+        // Arguments to the SQL query above
+        var sqlArguments = [calendarStartTime, calendarEndTime];
+        
+        // SQLStatementCallback to process the query result
+        function sqlStatementCallback(tx, result) { self.processLoadedEvents(result.rows); };
+        
+        // SQLStatementErrorCallback that handles any error from the query
+        function sqlStatementErrorCallback(tx, error) { alert("Failed to retrieve events from database - " + error.message); };
+        
+        // SQLTransactionCallback
+        function sqlTransactionCallback(tx) { tx.executeSql(eventsQuery, sqlArguments, sqlStatementCallback, sqlStatementErrorCallback); };
+        
+        CalendarDatabase.db.transaction(sqlTransactionCallback);
+    },
+
+    saveAsNewEventToDB: function(calendarEvent) 
+    {
+        // SQL statement to insert new event into the database table
+        var insertEventStatement = "INSERT INTO WebKitCalendarEvents(id, eventTitle, eventLocation, startTime, endTime, eventCalendar, eventDetails) VALUES (?, ?, ?, ?, ?, ?, ?)";
+        
+        // Arguments to the SQL statement above
+        var sqlArguments = [calendarEvent.id, calendarEvent.title, calendarEvent.location, calendarEvent.from.getTime(), calendarEvent.to.getTime(), calendarEvent.calendar, calendarEvent.details];
+        
+        // SQLTransactionCallback
+        function sqlTransactionCallback(tx) { tx.executeSql(insertEventStatement, sqlArguments); };
+
+        CalendarDatabase.db.transaction(sqlTransactionCallback);
+    },
+
+    saveEventToDB: function(calendarEvent) {
+        // SQL statement to update an event in the database table
+        var updateEventStatement = "UPDATE WebKitCalendarEvents SET eventTitle = ?, eventLocation = ?, startTime = ?, endTime = ?, eventCalendar = ?, eventDetails = ? WHERE id = ?";
+        
+        // Arguments to the SQL statement above
+        var sqlArguments = [calendarEvent.title, calendarEvent.location, calendarEvent.from.getTime(), calendarEvent.to.getTime(), calendarEvent.calendar, calendarEvent.details, calendarEvent.id];
+        
+        // SQLTransactionCallback
+        function sqlTransactionCallback(tx) { tx.executeSql(updateEventStatement, sqlArguments); };
+
+        CalendarDatabase.db.transaction(sqlTransactionCallback);
+    },
+
+    deleteEventFromDB: function(calendarEvent) 
+    {
+        // SQL statement to delete an event from the database table
+        var deleteEventStatement = "DELETE FROM WebKitCalendarEvents WHERE id = ?";
+        
+        // Arguments to the SQL statement above
+        var sqlArguments = [calendarEvent.id];
+
+        // SQLTransactionCallback
+        function sqlTransactionCallback(tx) { tx.executeSql(deleteEventStatement, sqlArguments); };
+        CalendarDatabase.db.transaction(sqlTransactionCallback);
+    },
+
+    queryEventsInDB: function(query) 
+    {
+        var self = this;
+
+        // SQL query to search for events with keyword
+        var searchEventQuery = "SELECT id, eventTitle, eventLocation, startTime, endTime, eventCalendar, eventDetails FROM WebKitCalendarEvents WHERE eventTitle LIKE ? OR eventDetails LIKE ? OR eventLocation LIKE ?";
+        
+        // Arguments to the SQL query
+        var sqlArguments = [query, query, query];
+        
+        // SQLStatementCallback that processes the query result
+        function sqlStatementCallback(tx, result) { self.processQueryResults(result.rows); };
+        
+        // SQLStatementErrorCallback that reports any error from the query
+        function sqlStatementErrorCallback(tx, error) { alert("Failed to retrieve events from database - " + error.message); };
+        
+        // SQLTransactionCallback
+        function sqlTransactionCallback(tx) { tx.executeSql(searchEventQuery, sqlArguments, sqlStatementCallback, sqlTransactionCallback); };
+        
+        CalendarDatabase.db.transaction(sqlTransactionCallback);    
+    },
+
+    loadEventsFromDBForCalendarType: function(calendarType) 
+    {
+        var self = this;
+
+        // SQL query to retrieve all the events for this month
+        var eventsQuery = "SELECT id, eventTitle, eventLocation, startTime, endTime, eventCalendar, eventDetails FROM WebKitCalendarEvents WHERE (startTime BETWEEN ? and ?) AND eventCalendar = ?";
+        
+        // Arguments to the SQL query above
+        var sqlArguments = [calendarStartTime, calendarEndTime, calendarType];
+        
+        // SQLStatementCallback to process the query result
+        function sqlStatementCallback(tx, result) { self.processLoadedEvents(result.rows); };
+        
+        // SQLStatementErrorCallback that handles any error from the query
+        function sqlStatementErrorCallback(tx, error) { alert("Failed to retrieve events from database - " + error.message); };
+        
+        // SQLTransactionCallback
+        function sqlTransactionCallback(tx) { tx.executeSql(eventsQuery, sqlArguments, sqlStatementCallback, sqlStatementErrorCallback); };
+        
+        CalendarDatabase.db.transaction(sqlTransactionCallback);
+    },
+
+    processLoadedEvents: function(rows)
+    {
+        for (var i = 0; i < rows.length; i++) {
+            var row = rows.item(i);
+            var dayDate = Date.dayDateFromTime(row["startTime"]);
+            var dayObj = dateToDayObjectMap[dayDate.getTime()];
+            if (!dayObj)
+                continue;
+
+            dayObj.insertEvent(CalendarEvent.calendarEventFromResultRow(row, false));
+
+            // Keep track of the highest id seen.
+            if (row["id"] > highestID)
+                highestID = row["id"];
+        }
+    },
+
+    processQueryResults: function(rows)
+    {
+        var searchResultsList = document.getElementById("searchResults");
+        searchResultsList.removeChildren();
+        unhighlightAllEvents();
+        for (var i = 0; i < rows.length; i++) {
+            var row = rows.item(i);
+            var calendarEvent = CalendarEvent.calendarEventFromResultRow(row, true);
+            highlightEventInCalendar(calendarEvent);
+            searchResultsList.appendChild(calendarEvent.searchResultAsListItem());
+        }
+    }
+}
+
+// Open Database!
+CalendarDatabase.open();
+
+function highlightEventInCalendar(calendarEvent)
+{
+    var itemsToHighlight = document.querySelectorAll("ul.contents li." + calendarEvent.calendar);
+    for (var i = 0; i < itemsToHighlight.length; ++i) {
+        var listItem = itemsToHighlight[i];
+        if (listItem.innerText !== calendarEvent.title)
+            continue;
+        if (listItem.hasStyleClass("highlighted"))
+            continue;
+        listItem.addStyleClass("highlighted");
+        break;
+    }
+}
+
+function unhighlightAllEvents()
+{
+    var highlightedItems = document.querySelectorAll("ul.contents li.highlighted");
+    for (var i = 0; i < highlightedItems.length; ++i)
+        highlightedItems[i].removeStyleClass("highlighted");
+}
+
+// Day object -------------------------------------------------------
+
+function dayObjectFromElement(element) 
+{
+    var parent = element;
+    while (parent && !parent.dayObject)
+        parent = parent.parentNode;
+    return parent ? parent.dayObject : null;
+}
+
+function Day(date) 
+{
+    this.date = new Date(date);
+    this.divNode = null;
+    this.contentsListNode = null;
+    this.eventsArray = null;
+}
+
+Day.prototype.attach = function(parent) 
+{
+    /* The HTML of each day looks like this:
+        <div class="day box">
+            <div class="date">1</div>
+            <ul class="contents">
+                <li>Event 1</li>
+                <li>Event 2</li>
+            </ul>
+        </div>
+    */
+
+    if (this.divNode)
+        throw("We have already created html elements for this day!");
+    this.divNode = document.createElement("div");
+    this.divNode.dayObject = this;
+    this.divNode.addStyleClass("day");
+    this.divNode.addStyleClass("box");
+    if (this.date.getMonth() != monthOnDisplay.getMonth())
+        this.divNode.addStyleClass("notThisMonth");
+    if (this.date.isToday())
+        this.divNode.addStyleClass("today");
+    
+    var dateDiv = document.createElement("div");
+    dateDiv.addStyleClass("date");
+    dateDiv.innerText = this.date.getDate();
+    this.divNode.appendChild(dateDiv);
+    
+    this.contentsListNode = document.createElement("ul");
+    this.contentsListNode.addStyleClass("contents");
+    this.contentsListNode.addEventListener("dblclick", Day.newEvent, false);
+    this.divNode.appendChild(this.contentsListNode);
+    
+    parent.appendChild(this.divNode);
+}
+
+Day.newEvent = function(event) 
+{
+    var element = event.target;
+    var dayObj = dayObjectFromElement(element);
+    if (!dayObj || dayObj.contentsListNode != element)
+        return;
+        
+    var calendarEvent = new CalendarEvent(dayObj.date, dayObj, selectedCalendarType, false);
+    calendarEvent.title = "New Event";
+    calendarEvent.from = dayObj.defaultEventStartTime();
+    var endTime = new Date(calendarEvent.from);
+    endTime.setHours(endTime.getHours() + 1);
+    calendarEvent.to = endTime;
+    dayObj.insertEvent(calendarEvent);
+    CalendarDatabase.saveAsNewEventToDB(calendarEvent);
+    selectedCalendarEvent = calendarEvent;
+    calendarEvent.show();
+    
+    stopEvent(event);
+}
+
+Day.prototype.insertEvent = function(calendarEvent) 
+{
+    if (!this.eventsArray)
+        this.eventsArray = new Array();
+    // Remove the event from array if it's already there.
+    var index = this.eventsArray.indexOf(calendarEvent);
+    if (index >= 0)
+        this.eventsArray.splice(index, 1);
+    calendarEvent.detach();
+    // Don't display this in the calendar if the calendar is unchecked
+    if (!isCalendarTypeVisible(calendarEvent.calendar))
+        return;
+    // We want to insert the calendarEvent in order of start time
+    for (index = 0; index < this.eventsArray.length; index++) {
+        if (this.eventsArray[index].from > calendarEvent.from)
+            break;
+    }
+    this.eventsArray.splice(index, 0, calendarEvent);
+    calendarEvent.attach();
+}
+
+Day.prototype.deleteEvent = function(calendarEvent) 
+{
+    CalendarDatabase.deleteEventFromDB(calendarEvent);
+    this.hideEvent(calendarEvent);
+}
+
+Day.prototype.hideEvent = function(calendarEvent)
+{
+    calendarEvent.detach();
+    selectedCalendarEvent = null;
+    if (!this.eventsArray)
+        return;
+    var index = this.eventsArray.indexOf(calendarEvent);
+    if (index >= 0)
+        this.eventsArray.splice(index, 1);
+}
+
+Day.prototype.hideEventsOfCalendarType = function(calendarType) 
+{
+    if (!this.eventsArray)
+        return;
+    var i = 0;
+    while (i < this.eventsArray.length) {
+        if (this.eventsArray[i].calendar == calendarType)
+            this.hideEvent(this.eventsArray[i]);
+        else
+            i++;
+    }
+}
+
+Day.prototype.defaultEventStartTime = function() 
+{
+    var startTime;
+    if (!this.eventsArray || !this.eventsArray.length) {
+        startTime = new Date(this.date);
+        startTime.setHours(9, 0, 0, 0);     // Default: events start at 9am!
+        return startTime;
+    }
+    var lastEvent = this.eventsArray[this.eventsArray.length-1];
+    startTime = new Date(lastEvent.to);
+    startTime.roundToHour();
+    return startTime;
+}
+
+// CalendarEvent object -------------------------------------------------------
+
+function calendarEventFromElement(element) 
+{
+    var parent = element;
+    while (parent) {
+        if (parent.calendarEvent)
+            return parent.calendarEvent;
+        parent = parent.parentNode;
+    }
+    return null;
+}
+
+function CalendarEvent(date, day, calendar, fromSearch) 
+{
+    this.date = date;
+    this.day = day;
+    this.fromSearch = fromSearch;
+    this.id = ++highestID;
+    this.title = "";
+    this.location = "";
+    this.from = null;
+    this.to = null;
+    this.calendar = calendar;
+    this.details = "";
+    this.listItemNode = null;
+}
+
+CalendarEvent.prototype.attach = function() 
+{
+    var parentNode = this.day.contentsListNode;
+    if (!parentNode)
+        throw("Must have created day box's html hierarchy before adding calendar events.");
+    if (this.listItemNode)
+        parentNode.removeChild(this.listItemNode);
+    this.listItemNode = document.createElement("li");
+    this.listItemNode.calendarEvent = this;
+    this.listItemNode.addStyleClass(this.calendar);
+    this.listItemNode.innerText = this.title;
+    this.listItemNode.addEventListener("click", CalendarEvent.eventSelected, false);
+    var index = this.day.eventsArray.indexOf(this);
+    if (index < 0)
+        throw("Cannot attach if CalendarEvent does not belong to a Day object.");
+    var adjacentNode = null;
+    if (index < this.day.contentsListNode.childNodes.length)
+        adjacentNode = this.day.contentsListNode.childNodes[index];
+    this.day.contentsListNode.insertBefore(this.listItemNode, adjacentNode);
+}
+
+CalendarEvent.prototype.detach = function() 
+{
+    var parentNode = this.day.contentsListNode;
+    if (parentNode && this.listItemNode)
+        parentNode.removeChild(this.listItemNode);
+    this.listItemNode = null;
+}
+
+CalendarEvent.eventSelected = function(event) 
+{
+    var calendarEvent = calendarEventFromElement(event.target);
+    if (!calendarEvent)
+        return;
+    calendarEvent.selected();
+    stopEvent(event);
+}
+
+CalendarEvent.prototype.selected = function() 
+{
+    if (selectedCalendarEvent == this) {
+        selectedCalendarEvent.show();
+        return;
+    }
+    if (selectedCalendarEvent)
+        selectedCalendarEvent.listItemNode.removeStyleClass("selected");
+    selectedCalendarEvent = this;
+    if (selectedCalendarEvent)
+        selectedCalendarEvent.listItemNode.addStyleClass("selected");
+}
+
+function minutesString(minutes) 
+{
+    if (minutes < 10)
+        return "0" + minutes;
+    return minutes.toString();
+}
+
+CalendarEvent.prototype.show = function() 
+{
+    // Update the event details
+    document.getElementById("eventTitle").innerText = this.title;
+    document.getElementById("eventLocation").innerText = this.location;
+    document.getElementById("eventFromDate").innerText = this.date.toLocaleDateString();
+    document.getElementById("eventFromHours").innerText = this.from.getHours();
+    document.getElementById("eventFromMinutes").innerText = minutesString(this.from.getMinutes());
+    document.getElementById("eventToDate").innerText = this.date.toLocaleDateString();
+    document.getElementById("eventToHours").innerText = this.to.getHours();
+    document.getElementById("eventToMinutes").innerText = minutesString(this.to.getMinutes());
+    document.getElementById("eventCalendarType").value = this.calendar;
+    document.getElementById("eventDetails").innerText = this.details;
+    
+    // Reset the map
+    requestMapImage();
+
+    // Fade out the gridView
+    document.getElementById("gridView").addStyleClass("inactive");
+        
+    // Show event details
+    document.getElementById("eventOverlay").addStyleClass("show");
+}
+
+CalendarEvent.prototype.detailsUpdated = function() 
+{
+    // FIXME: error checking!!
+    this.title = document.getElementById("eventTitle").innerText;
+    this.location = document.getElementById("eventLocation").innerText;
+    this.from.setHours(document.getElementById("eventFromHours").innerText);
+    this.from.setMinutes(document.getElementById("eventFromMinutes").innerText);
+    this.to.setHours(document.getElementById("eventToHours").innerText);
+    this.to.setMinutes(document.getElementById("eventToMinutes").innerText);
+    this.calendar = document.getElementById("eventCalendarType").value;
+    this.details = document.getElementById("eventDetails").innerText;
+
+    CalendarDatabase.saveEventToDB(this);
+    if (!this.fromSearch && this.day)
+        this.day.insertEvent(this);
+}
+
+CalendarEvent.prototype.toString = function() 
+{
+    return "CalendarEvent: " + this.title + " starting on " + this.from.toString();
+}
+
+CalendarEvent.prototype.searchResultAsListItem = function() 
+{
+    var listItem = document.createElement("li");
+    var titleText = document.createTextNode(this.title);
+    var locationText = this.location.length > 0 ? document.createTextNode(this.location) : null;
+    var startTimeText = document.createTextNode(this.from.toLocaleString());
+    listItem.appendChild(titleText);
+    listItem.appendChild(document.createElement("br"));
+    if (locationText) {
+        listItem.appendChild(locationText);
+        listItem.appendChild(document.createElement("br"));
+    }
+    listItem.appendChild(startTimeText);
+    listItem.calendarEvent = this;
+    listItem.addStyleClass(this.calendar);
+    listItem.addEventListener("click", CalendarEvent.eventSelected, false);
+    this.listItemNode = listItem;
+    return listItem;
+}
+
+CalendarEvent.calendarEventFromResultRow = function(row, fromSearch) 
+{
+    var dayDate = Date.dayDateFromTime(row["startTime"]);
+    var dayObj = dateToDayObjectMap[dayDate.getTime()];
+    var calendarEvent = new CalendarEvent(dayDate, dayObj, row["eventCalendar"], fromSearch);
+    calendarEvent.id = row["id"];
+    calendarEvent.title = row["eventTitle"];
+    calendarEvent.location = row["eventLocation"];
+    calendarEvent.from = new Date();
+    calendarEvent.from.setTime(row["startTime"]);
+    calendarEvent.to = new Date();
+    calendarEvent.to.setTime(row["endTime"]);
+    calendarEvent.details = row["eventDetails"];
+    return calendarEvent;
+}
+
+// Miscellaneous methods ------------------------------------------------
+
+function stopEvent(event) 
+{
+    event.preventDefault();
+    event.stopPropagation();
+}
diff --git a/WebKitSite/demos/calendar/Calendar.manifest b/WebKitSite/demos/calendar/Calendar.manifest
new file mode 100644 (file)
index 0000000..56a904b
--- /dev/null
@@ -0,0 +1,18 @@
+CACHE MANIFEST
+# All resources required to make Calendar work offline
+
+Boom.aiff
+Calendar.html
+Calendar.css
+Calendar.js
+CalendarApp.icns
+LocationImage.js
+Utilities.js
+favicon.ico
+Images/AirPort4.png
+Images/AirPortError.png
+Images/disclosureTriangleSmallDown.png
+Images/disclosureTriangleSmallRight.png
+Images/statusbarBackground.png
+Images/statusbarResizerVertical.png
+
diff --git a/WebKitSite/demos/calendar/CalendarApp.icns b/WebKitSite/demos/calendar/CalendarApp.icns
new file mode 100644 (file)
index 0000000..0d2f339
Binary files /dev/null and b/WebKitSite/demos/calendar/CalendarApp.icns differ
diff --git a/WebKitSite/demos/calendar/Images/AirPort4.png b/WebKitSite/demos/calendar/Images/AirPort4.png
new file mode 100644 (file)
index 0000000..ed330c4
Binary files /dev/null and b/WebKitSite/demos/calendar/Images/AirPort4.png differ
diff --git a/WebKitSite/demos/calendar/Images/AirPortError.png b/WebKitSite/demos/calendar/Images/AirPortError.png
new file mode 100644 (file)
index 0000000..e72f25a
Binary files /dev/null and b/WebKitSite/demos/calendar/Images/AirPortError.png differ
diff --git a/WebKitSite/demos/calendar/Images/disclosureTriangleSmallDown.png b/WebKitSite/demos/calendar/Images/disclosureTriangleSmallDown.png
new file mode 100644 (file)
index 0000000..cffc835
Binary files /dev/null and b/WebKitSite/demos/calendar/Images/disclosureTriangleSmallDown.png differ
diff --git a/WebKitSite/demos/calendar/Images/disclosureTriangleSmallRight.png b/WebKitSite/demos/calendar/Images/disclosureTriangleSmallRight.png
new file mode 100644 (file)
index 0000000..a3102ea
Binary files /dev/null and b/WebKitSite/demos/calendar/Images/disclosureTriangleSmallRight.png differ
diff --git a/WebKitSite/demos/calendar/Images/statusbarBackground.png b/WebKitSite/demos/calendar/Images/statusbarBackground.png
new file mode 100644 (file)
index 0000000..b466a49
Binary files /dev/null and b/WebKitSite/demos/calendar/Images/statusbarBackground.png differ
diff --git a/WebKitSite/demos/calendar/Images/statusbarResizerVertical.png b/WebKitSite/demos/calendar/Images/statusbarResizerVertical.png
new file mode 100644 (file)
index 0000000..7fc1452
Binary files /dev/null and b/WebKitSite/demos/calendar/Images/statusbarResizerVertical.png differ
diff --git a/WebKitSite/demos/calendar/LocationImage.js b/WebKitSite/demos/calendar/LocationImage.js
new file mode 100644 (file)
index 0000000..382aa6e
--- /dev/null
@@ -0,0 +1,15 @@
+function LocationImage() 
+{
+
+}
+
+LocationImage.prototype.requestLocationImage = function(address, callback)
+{
+
+}
+
+
+LocationImage.prototype.geocodeResultReceived = function(point, callback)
+{
+
+}
diff --git a/WebKitSite/demos/calendar/Utilities.js b/WebKitSite/demos/calendar/Utilities.js
new file mode 100644 (file)
index 0000000..30b4e10
--- /dev/null
@@ -0,0 +1,134 @@
+// Element extension --------------------------------------------------------------
+
+Element.prototype.removeStyleClass = function(className) 
+{
+    // Test for the simple case before using a RegExp.
+    if (this.className === className) {
+        this.className = "";
+        return;
+    }
+
+    var regex = new RegExp("(^|\\s+)" + className.escapeForRegExp() + "($|\\s+)");
+    if (regex.test(this.className))
+        this.className = this.className.replace(regex, " ");
+}
+
+Element.prototype.addStyleClass = function(className) 
+{
+    if (className && !this.hasStyleClass(className))
+        this.className += (this.className.length ? " " + className : className);
+}
+
+Element.prototype.hasStyleClass = function(className) 
+{
+    if (!className)
+        return false;
+    // Test for the simple case before using a RegExp.
+    if (this.className === className)
+        return true;
+    var regex = new RegExp("(^|\\s)" + className.escapeForRegExp() + "($|\\s)");
+    return regex.test(this.className);
+}
+
+Element.prototype.removeChildren = function()
+{
+    while (this.firstChild) 
+        this.removeChild(this.firstChild);        
+}
+
+Element.prototype.findParentOfTagName = function(tagName)
+{
+    var parent = this;
+    while (parent) {
+        if (parent.tagName == tagName)
+            return parent;
+        parent = parent.parentNode;
+    }
+    return null;
+}
+
+Element.prototype.__defineGetter__("totalOffsetLeft", function()
+{
+    var total = 0;
+    for (var element = this; element; element = element.offsetParent)
+        total += element.offsetLeft;
+    return total;
+});
+
+Element.prototype.__defineGetter__("totalOffsetTop", function()
+{
+    var total = 0;
+    for (var element = this; element; element = element.offsetParent)
+        total += element.offsetTop;
+    return total;
+});
+
+// String extension ------------------------------------------------------
+
+String.prototype.hasSubstring = function(string, caseInsensitive)
+{
+    if (!caseInsensitive)
+        return this.indexOf(string) !== -1;
+    return this.match(new RegExp(string.escapeForRegExp(), "i"));
+}
+
+String.prototype.escapeCharacters = function(chars)
+{
+    var foundChar = false;
+    for (var i = 0; i < chars.length; ++i) {
+        if (this.indexOf(chars.charAt(i)) !== -1) {
+            foundChar = true;
+            break;
+        }
+    }
+
+    if (!foundChar)
+        return this;
+
+    var result = "";
+    for (var i = 0; i < this.length; ++i) {
+        if (chars.indexOf(this.charAt(i)) !== -1)
+            result += "\\";
+        result += this.charAt(i);
+    }
+
+    return result;
+}
+
+String.prototype.escapeForRegExp = function()
+{
+    return this.escapeCharacters("^[]{}()\\.$*+?|");
+}
+
+// Number extension ------------------------------------------------------
+
+Number.constrain = function(num, min, max)
+{
+    if (num < min)
+        num = min;
+    else if (num > max)
+        num = max;
+    return num;
+}
+
+// Date extension --------------------------------------------------------
+
+Date.prototype.isToday = function()
+{
+    var today = new Date();
+    return (today.getFullYear() == this.getFullYear() && today.getMonth() == this.getMonth() && today.getDate() == this.getDate());    
+}
+
+Date.prototype.roundToHour = function() {
+    if (this.getMinutes() == 0)
+        return; // Already rounded to the hour
+    if (this.getHours() < 23)
+        this.setHours(this.getHours() + 1);
+    this.setMinutes(0);
+}
+
+Date.dayDateFromTime = function(time) {
+    var date = new Date(time);
+    date.setHours(0, 0, 0, 0);
+    return date;
+}
diff --git a/WebKitSite/demos/calendar/favicon.ico b/WebKitSite/demos/calendar/favicon.ico
new file mode 100644 (file)
index 0000000..bf276bf
Binary files /dev/null and b/WebKitSite/demos/calendar/favicon.ico differ
diff --git a/WebKitSite/demos/calendar/mime.types b/WebKitSite/demos/calendar/mime.types
new file mode 100644 (file)
index 0000000..3f1443d
--- /dev/null
@@ -0,0 +1,989 @@
+# This is a comment. I love comments.
+
+# This file controls what Internet media types are sent to the client for
+# given file extension(s).  Sending the correct media type to the client
+# is important so they know how to handle the content of the file.
+# Extra types can either be added here or by using an AddType directive
+# in your config files. For more information about Internet media types,
+# please read RFC 2045, 2046, 2047, 2048, and 2077.  The Internet media type
+# registry is at <http://www.iana.org/assignments/media-types/>.
+
+# MIME type                                    Extensions
+application/activemessage
+application/andrew-inset                       ez
+application/applefile
+application/atom+xml                           atom
+application/atomcat+xml                                atomcat
+application/atomicmail
+application/atomsvc+xml                                atomsvc
+application/auth-policy+xml
+application/batch-smtp
+application/beep+xml
+application/cals-1840
+application/ccxml+xml                          ccxml
+application/cellml+xml
+application/cnrp+xml
+application/commonground
+application/conference-info+xml
+application/cpl+xml
+application/csta+xml
+application/cstadata+xml
+application/cybercash
+application/davmount+xml                       davmount
+application/dca-rft
+application/dec-dx
+application/dialog-info+xml
+application/dicom
+application/dns
+application/dvcs
+application/ecmascript                         ecma
+application/edi-consent
+application/edi-x12
+application/edifact
+application/epp+xml
+application/eshop
+application/fastinfoset
+application/fastsoap
+application/fits
+application/font-tdpfr                         pfr
+application/h224
+application/http
+application/hyperstudio                                stk
+application/iges
+application/im-iscomposing+xml
+application/index
+application/index.cmd
+application/index.obj
+application/index.response
+application/index.vnd
+application/iotp
+application/ipp
+application/isup
+application/javascript                         js
+application/json                               json
+application/kpml-request+xml
+application/kpml-response+xml
+application/mac-binhex40                       hqx
+application/mac-compactpro                     cpt
+application/macwriteii
+application/marc                               mrc
+application/mathematica                                ma nb mb
+application/mathml+xml                         mathml
+application/mbms-associated-procedure-description+xml
+application/mbms-deregister+xml
+application/mbms-envelope+xml
+application/mbms-msk+xml
+application/mbms-msk-response+xml
+application/mbms-protection-description+xml
+application/mbms-reception-report+xml
+application/mbms-register+xml
+application/mbms-register-response+xml
+application/mbms-user-service-description+xml
+application/mbox                               mbox
+application/mediaservercontrol+xml             mscml
+application/mikey
+application/mp4                                        mp4s
+application/mpeg4-generic
+application/mpeg4-iod
+application/mpeg4-iod-xmt
+application/msword                             doc dot
+application/mxf                                        mxf
+application/nasdata
+application/news-message-id
+application/news-transmission
+application/nss
+application/ocsp-request
+application/ocsp-response
+application/octet-stream bin dms lha lzh class so iso dmg dist distz pkg bpk dump elc scpt
+application/oda                                        oda
+application/oebps-package+xml
+application/ogg                                        ogg
+application/parityfec
+application/pdf                                        pdf
+application/pgp-encrypted                      pgp
+application/pgp-keys
+application/pgp-signature                      asc sig
+application/pics-rules                         prf
+application/pidf+xml
+application/pkcs10                             p10
+application/pkcs7-mime                         p7m p7c
+application/pkcs7-signature                    p7s
+application/pkix-cert                          cer
+application/pkix-crl                           crl
+application/pkix-pkipath                       pkipath
+application/pkixcmp                            pki
+application/pls+xml                            pls
+application/poc-settings+xml
+application/postscript                         ai eps ps
+application/prs.alvestrand.titrax-sheet
+application/prs.cww                            cww
+application/prs.nprend
+application/prs.plucker
+application/qsig
+application/rdf+xml                            rdf
+application/reginfo+xml                                rif
+application/relax-ng-compact-syntax            rnc
+application/remote-printing
+application/resource-lists+xml                 rl
+application/riscos
+application/rlmi+xml
+application/rls-services+xml                   rs
+application/rsd+xml                            rsd
+application/rss+xml                            rss
+application/rtf                                        rtf
+application/rtx
+application/samlassertion+xml
+application/samlmetadata+xml
+application/sbml+xml                           sbml
+application/sdp                                        sdp
+application/set-payment
+application/set-payment-initiation             setpay
+application/set-registration
+application/set-registration-initiation                setreg
+application/sgml
+application/sgml-open-catalog
+application/shf+xml                            shf
+application/sieve
+application/simple-filter+xml
+application/simple-message-summary
+application/simplesymbolcontainer
+application/slate
+application/smil
+application/smil+xml                           smi smil
+application/soap+fastinfoset
+application/soap+xml
+application/spirits-event+xml
+application/srgs                               gram
+application/srgs+xml                           grxml
+application/ssml+xml                           ssml
+application/timestamp-query
+application/timestamp-reply
+application/tve-trigger
+application/vemmi
+application/vividence.scriptfile
+application/vnd.3gpp.bsf+xml
+application/vnd.3gpp.pic-bw-large              plb
+application/vnd.3gpp.pic-bw-small              psb
+application/vnd.3gpp.pic-bw-var                        pvb
+application/vnd.3gpp.sms
+application/vnd.3gpp2.bcmcsinfo+xml
+application/vnd.3gpp2.sms
+application/vnd.3m.post-it-notes               pwn
+application/vnd.accpac.simply.aso              aso
+application/vnd.accpac.simply.imp              imp
+application/vnd.acucobol                       acu
+application/vnd.acucorp                                atc acutc
+application/vnd.adobe.xdp+xml                  xdp
+application/vnd.adobe.xfdf                     xfdf
+application/vnd.aether.imp
+application/vnd.amiga.ami                      ami
+application/vnd.anser-web-certificate-issue-initiation cii
+application/vnd.anser-web-funds-transfer-initiation    fti
+application/vnd.antix.game-component           atx
+application/vnd.apple.installer+xml            mpkg
+application/vnd.audiograph                     aep
+application/vnd.autopackage
+application/vnd.avistar+xml
+application/vnd.blueice.multipass              mpm
+application/vnd.bmi                            bmi
+application/vnd.businessobjects                        rep
+application/vnd.cab-jscript
+application/vnd.canon-cpdl
+application/vnd.canon-lips
+application/vnd.cendio.thinlinc.clientconf
+application/vnd.chemdraw+xml                   cdxml
+application/vnd.chipnuts.karaoke-mmd           mmd
+application/vnd.cinderella                     cdy
+application/vnd.cirpack.isdn-ext
+application/vnd.claymore                       cla
+application/vnd.clonk.c4group                  c4g c4d c4f c4p c4u
+application/vnd.commerce-battelle
+application/vnd.commonspace                    csp cst
+application/vnd.contact.cmsg                   cdbcmsg
+application/vnd.cosmocaller                    cmc
+application/vnd.crick.clicker                  clkx
+application/vnd.crick.clicker.keyboard         clkk
+application/vnd.crick.clicker.palette          clkp
+application/vnd.crick.clicker.template         clkt
+application/vnd.crick.clicker.wordbank         clkw
+application/vnd.criticaltools.wbs+xml          wbs
+application/vnd.ctc-posml                      pml
+application/vnd.cups-pdf
+application/vnd.cups-postscript
+application/vnd.cups-ppd                       ppd
+application/vnd.cups-raster
+application/vnd.cups-raw
+application/vnd.curl                           curl
+application/vnd.cybank
+application/vnd.data-vision.rdz                        rdz
+application/vnd.denovo.fcselayout-link         fe_launch
+application/vnd.dna                            dna
+application/vnd.dolby.mlp                      mlp
+application/vnd.dpgraph                                dpg
+application/vnd.dreamfactory                   dfac
+application/vnd.dvb.esgcontainer
+application/vnd.dvb.ipdcesgaccess
+application/vnd.dxr
+application/vnd.ecdis-update
+application/vnd.ecowin.chart                   mag
+application/vnd.ecowin.filerequest
+application/vnd.ecowin.fileupdate
+application/vnd.ecowin.series
+application/vnd.ecowin.seriesrequest
+application/vnd.ecowin.seriesupdate
+application/vnd.enliven                                nml
+application/vnd.epson.esf                      esf
+application/vnd.epson.msf                      msf
+application/vnd.epson.quickanime               qam
+application/vnd.epson.salt                     slt
+application/vnd.epson.ssf                      ssf
+application/vnd.ericsson.quickcall
+application/vnd.eszigno3+xml                   es3 et3
+application/vnd.eudora.data
+application/vnd.ezpix-album                    ez2
+application/vnd.ezpix-package                  ez3
+application/vnd.fdf                            fdf
+application/vnd.ffsns
+application/vnd.fints
+application/vnd.flographit                     gph
+application/vnd.fluxtime.clip                  ftc
+application/vnd.framemaker                     fm frame maker
+application/vnd.frogans.fnc                    fnc
+application/vnd.frogans.ltf                    ltf
+application/vnd.fsc.weblaunch                  fsc
+application/vnd.fujitsu.oasys                  oas
+application/vnd.fujitsu.oasys2                 oa2
+application/vnd.fujitsu.oasys3                 oa3
+application/vnd.fujitsu.oasysgp                        fg5
+application/vnd.fujitsu.oasysprs               bh2
+application/vnd.fujixerox.art-ex
+application/vnd.fujixerox.art4
+application/vnd.fujixerox.hbpl
+application/vnd.fujixerox.ddd                  ddd
+application/vnd.fujixerox.docuworks            xdw
+application/vnd.fujixerox.docuworks.binder     xbd
+application/vnd.fut-misnet
+application/vnd.fuzzysheet                     fzs
+application/vnd.genomatix.tuxedo               txd
+application/vnd.google-earth.kml+xml           kml
+application/vnd.google-earth.kmz               kmz
+application/vnd.grafeq                         gqf gqs
+application/vnd.gridmp
+application/vnd.groove-account                 gac
+application/vnd.groove-help                    ghf
+application/vnd.groove-identity-message                gim
+application/vnd.groove-injector                        grv
+application/vnd.groove-tool-message            gtm
+application/vnd.groove-tool-template           tpl
+application/vnd.groove-vcard                   vcg
+application/vnd.handheld-entertainment+xml     zmm
+application/vnd.hbci                           hbci
+application/vnd.hcl-bireports
+application/vnd.hhe.lesson-player              les
+application/vnd.hp-hpgl                                hpgl
+application/vnd.hp-hpid                                hpid
+application/vnd.hp-hps                         hps
+application/vnd.hp-jlyt                                jlt
+application/vnd.hp-pcl                         pcl
+application/vnd.hp-pclxl                       pclxl
+application/vnd.httphone
+application/vnd.hzn-3d-crossword               x3d
+application/vnd.ibm.afplinedata
+application/vnd.ibm.electronic-media
+application/vnd.ibm.minipay                    mpy
+application/vnd.ibm.modcap                     afp listafp list3820
+application/vnd.ibm.rights-management          irm
+application/vnd.ibm.secure-container           sc
+application/vnd.igloader                       igl
+application/vnd.immervision-ivp                        ivp
+application/vnd.immervision-ivu                        ivu
+application/vnd.informedcontrol.rms+xml
+application/vnd.intercon.formnet               xpw xpx
+application/vnd.intertrust.digibox
+application/vnd.intertrust.nncp
+application/vnd.intu.qbo                       qbo
+application/vnd.intu.qfx                       qfx
+application/vnd.ipunplugged.rcprofile          rcprofile
+application/vnd.irepository.package+xml                irp
+application/vnd.is-xpr                         xpr
+application/vnd.jam                            jam
+application/vnd.japannet-directory-service
+application/vnd.japannet-jpnstore-wakeup
+application/vnd.japannet-payment-wakeup
+application/vnd.japannet-registration
+application/vnd.japannet-registration-wakeup
+application/vnd.japannet-setstore-wakeup
+application/vnd.japannet-verification
+application/vnd.japannet-verification-wakeup
+application/vnd.jcp.javame.midlet-rms          rms
+application/vnd.jisp                           jisp
+application/vnd.kahootz                                ktz ktr
+application/vnd.kde.karbon                     karbon
+application/vnd.kde.kchart                     chrt
+application/vnd.kde.kformula                   kfo
+application/vnd.kde.kivio                      flw
+application/vnd.kde.kontour                    kon
+application/vnd.kde.kpresenter                 kpr kpt
+application/vnd.kde.kspread                    ksp
+application/vnd.kde.kword                      kwd kwt
+application/vnd.kenameaapp                     htke
+application/vnd.kidspiration                   kia
+application/vnd.kinar                          kne knp
+application/vnd.koan                           skp skd skt skm
+application/vnd.liberty-request+xml
+application/vnd.llamagraphics.life-balance.desktop     lbd
+application/vnd.llamagraphics.life-balance.exchange+xml        lbe
+application/vnd.lotus-1-2-3                    123
+application/vnd.lotus-approach                 apr
+application/vnd.lotus-freelance                        pre
+application/vnd.lotus-notes                    nsf
+application/vnd.lotus-organizer                        org
+application/vnd.lotus-screencam                        scm
+application/vnd.lotus-wordpro                  lwp
+application/vnd.macports.portpkg               portpkg
+application/vnd.marlin.drm.actiontoken+xml
+application/vnd.marlin.drm.conftoken+xml
+application/vnd.marlin.drm.mdcf
+application/vnd.mcd                            mcd
+application/vnd.medcalcdata                    mc1
+application/vnd.mediastation.cdkey             cdkey
+application/vnd.meridian-slingshot
+application/vnd.mfer                           mwf
+application/vnd.mfmp                           mfm
+application/vnd.micrografx.flo                 flo
+application/vnd.micrografx.igx                 igx
+application/vnd.mif                            mif
+application/vnd.minisoft-hp3000-save
+application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf                     daf
+application/vnd.mobius.dis                     dis
+application/vnd.mobius.mbk                     mbk
+application/vnd.mobius.mqy                     mqy
+application/vnd.mobius.msl                     msl
+application/vnd.mobius.plc                     plc
+application/vnd.mobius.txf                     txf
+application/vnd.mophun.application             mpn
+application/vnd.mophun.certificate             mpc
+application/vnd.motorola.flexsuite
+application/vnd.motorola.flexsuite.adsi
+application/vnd.motorola.flexsuite.fis
+application/vnd.motorola.flexsuite.gotap
+application/vnd.motorola.flexsuite.kmr
+application/vnd.motorola.flexsuite.ttc
+application/vnd.motorola.flexsuite.wem
+application/vnd.mozilla.xul+xml        xul
+application/vnd.ms-artgalry                    cil
+application/vnd.ms-asf                         asf
+application/vnd.ms-cab-compressed              cab
+application/vnd.ms-excel                       xls xlm xla xlc xlt xlw
+application/vnd.ms-fontobject                  eot
+application/vnd.ms-htmlhelp                    chm
+application/vnd.ms-ims                         ims
+application/vnd.ms-lrm                         lrm
+application/vnd.ms-playready.initiator+xml
+application/vnd.ms-powerpoint                  ppt pps pot
+application/vnd.ms-project                     mpp mpt
+application/vnd.ms-tnef
+application/vnd.ms-wmdrm.lic-chlg-req
+application/vnd.ms-wmdrm.lic-resp
+application/vnd.ms-wmdrm.meter-chlg-req
+application/vnd.ms-wmdrm.meter-resp
+application/vnd.ms-works                       wps wks wcm wdb
+application/vnd.ms-wpl                         wpl
+application/vnd.ms-xpsdocument                 xps
+application/vnd.mseq                           mseq
+application/vnd.msign
+application/vnd.music-niff
+application/vnd.musician                       mus
+application/vnd.ncd.control
+application/vnd.nervana
+application/vnd.netfpx
+application/vnd.neurolanguage.nlu              nlu
+application/vnd.noblenet-directory             nnd
+application/vnd.noblenet-sealer                        nns
+application/vnd.noblenet-web                   nnw
+application/vnd.nokia.catalogs
+application/vnd.nokia.conml+wbxml
+application/vnd.nokia.conml+xml
+application/vnd.nokia.isds-radio-presets
+application/vnd.nokia.iptv.config+xml
+application/vnd.nokia.landmark+wbxml
+application/vnd.nokia.landmark+xml
+application/vnd.nokia.landmarkcollection+xml
+application/vnd.nokia.n-gage.ac+xml
+application/vnd.nokia.n-gage.data              ngdat
+application/vnd.nokia.n-gage.symbian.install   n-gage
+application/vnd.nokia.ncd
+application/vnd.nokia.pcd+wbxml
+application/vnd.nokia.pcd+xml
+application/vnd.nokia.radio-preset             rpst
+application/vnd.nokia.radio-presets            rpss
+application/vnd.novadigm.edm                   edm
+application/vnd.novadigm.edx                   edx
+application/vnd.novadigm.ext                   ext
+application/vnd.oasis.opendocument.chart               odc
+application/vnd.oasis.opendocument.chart-template      otc
+application/vnd.oasis.opendocument.formula             odf
+application/vnd.oasis.opendocument.formula-template    otf
+application/vnd.oasis.opendocument.graphics            odg
+application/vnd.oasis.opendocument.graphics-template   otg
+application/vnd.oasis.opendocument.image               odi
+application/vnd.oasis.opendocument.image-template      oti
+application/vnd.oasis.opendocument.presentation                odp
+application/vnd.oasis.opendocument.presentation-template otp
+application/vnd.oasis.opendocument.spreadsheet         ods
+application/vnd.oasis.opendocument.spreadsheet-template        ots
+application/vnd.oasis.opendocument.text                        odt
+application/vnd.oasis.opendocument.text-master         otm
+application/vnd.oasis.opendocument.text-template       ott
+application/vnd.oasis.opendocument.text-web            oth
+application/vnd.obn
+application/vnd.olpc-sugar                     xo
+application/vnd.oma-scws-config
+application/vnd.oma-scws-http-request
+application/vnd.oma-scws-http-response
+application/vnd.oma.bcast.associated-procedure-parameter+xml
+application/vnd.oma.bcast.drm-trigger+xml
+application/vnd.oma.bcast.imd+xml
+application/vnd.oma.bcast.notification+xml
+application/vnd.oma.bcast.sgboot
+application/vnd.oma.bcast.sgdd+xml
+application/vnd.oma.bcast.sgdu
+application/vnd.oma.bcast.simple-symbol-container
+application/vnd.oma.bcast.smartcard-trigger+xml
+application/vnd.oma.bcast.sprov+xml
+application/vnd.oma.dd2+xml                    dd2
+application/vnd.oma.drm.risd+xml
+application/vnd.oma.group-usage-list+xml
+application/vnd.oma.poc.groups+xml
+application/vnd.oma.xcap-directory+xml
+application/vnd.omads-email+xml
+application/vnd.omads-file+xml
+application/vnd.omads-folder+xml
+application/vnd.omaloc-supl-init
+application/vnd.openofficeorg.extension                oxt
+application/vnd.osa.netdeploy
+application/vnd.osgi.dp                                dp
+application/vnd.otps.ct-kip+xml
+application/vnd.palm                           prc pdb pqa oprc
+application/vnd.paos.xml
+application/vnd.pg.format                      str
+application/vnd.pg.osasli                      ei6
+application/vnd.piaccess.application-licence
+application/vnd.picsel                         efif
+application/vnd.poc.group-advertisement+xml
+application/vnd.pocketlearn                    plf
+application/vnd.powerbuilder6                  pbd
+application/vnd.powerbuilder6-s
+application/vnd.powerbuilder7
+application/vnd.powerbuilder7-s
+application/vnd.powerbuilder75
+application/vnd.powerbuilder75-s
+application/vnd.preminet
+application/vnd.previewsystems.box             box
+application/vnd.proteus.magazine               mgz
+application/vnd.publishare-delta-tree          qps
+application/vnd.pvi.ptid1                      ptid
+application/vnd.pwg-multiplexed
+application/vnd.pwg-xhtml-print+xml
+application/vnd.qualcomm.brew-app-res
+application/vnd.quark.quarkxpress              qxd qxt qwd qwt qxl qxb
+application/vnd.rapid
+application/vnd.recordare.musicxml             mxl
+application/vnd.recordare.musicxml+xml
+application/vnd.renlearn.rlprint
+application/vnd.rn-realmedia                   rm
+application/vnd.ruckus.download
+application/vnd.s3sms
+application/vnd.scribus
+application/vnd.sealed.3df
+application/vnd.sealed.csf
+application/vnd.sealed.doc
+application/vnd.sealed.eml
+application/vnd.sealed.mht
+application/vnd.sealed.net
+application/vnd.sealed.ppt
+application/vnd.sealed.tiff
+application/vnd.sealed.xls
+application/vnd.sealedmedia.softseal.html
+application/vnd.sealedmedia.softseal.pdf
+application/vnd.seemail                                see
+application/vnd.sema                           sema
+application/vnd.semd                           semd
+application/vnd.semf                           semf
+application/vnd.shana.informed.formdata                ifm
+application/vnd.shana.informed.formtemplate    itp
+application/vnd.shana.informed.interchange     iif
+application/vnd.shana.informed.package         ipk
+application/vnd.simtech-mindmapper             twd twds
+application/vnd.smaf                           mmf
+application/vnd.solent.sdkm+xml                        sdkm sdkd
+application/vnd.spotfire.dxp                   dxp
+application/vnd.spotfire.sfs                   sfs
+application/vnd.sss-cod
+application/vnd.sss-dtf
+application/vnd.sss-ntf
+application/vnd.street-stream
+application/vnd.sun.wadl+xml
+application/vnd.sus-calendar                   sus susp
+application/vnd.svd                            svd
+application/vnd.swiftview-ics
+application/vnd.syncml+xml                     xsm
+application/vnd.syncml.dm+wbxml                        bdm
+application/vnd.syncml.dm+xml                  xdm
+application/vnd.syncml.ds.notification
+application/vnd.tao.intent-module-archive      tao
+application/vnd.tmobile-livetv                 tmo
+application/vnd.trid.tpt                       tpt
+application/vnd.triscape.mxs                   mxs
+application/vnd.trueapp                                tra
+application/vnd.truedoc
+application/vnd.ufdl                           ufd ufdl
+application/vnd.uiq.theme                      utz
+application/vnd.umajin                         umj
+application/vnd.unity                          unityweb
+application/vnd.uoml+xml                       uoml
+application/vnd.uplanet.alert
+application/vnd.uplanet.alert-wbxml
+application/vnd.uplanet.bearer-choice
+application/vnd.uplanet.bearer-choice-wbxml
+application/vnd.uplanet.cacheop
+application/vnd.uplanet.cacheop-wbxml
+application/vnd.uplanet.channel
+application/vnd.uplanet.channel-wbxml
+application/vnd.uplanet.list
+application/vnd.uplanet.list-wbxml
+application/vnd.uplanet.listcmd
+application/vnd.uplanet.listcmd-wbxml
+application/vnd.uplanet.signal
+application/vnd.vcx                            vcx
+application/vnd.vd-study
+application/vnd.vectorworks
+application/vnd.vidsoft.vidconference
+application/vnd.visio                          vsd vst vss vsw
+application/vnd.visionary                      vis
+application/vnd.vividence.scriptfile
+application/vnd.vsf                            vsf
+application/vnd.wap.sic
+application/vnd.wap.slc
+application/vnd.wap.wbxml                      wbxml
+application/vnd.wap.wmlc                       wmlc
+application/vnd.wap.wmlscriptc                 wmlsc
+application/vnd.webturbo                       wtb
+application/vnd.wfa.wsc
+application/vnd.wordperfect                    wpd
+application/vnd.wqd                            wqd
+application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf                         stf
+application/vnd.wv.csp+wbxml
+application/vnd.wv.csp+xml
+application/vnd.wv.ssp+xml
+application/vnd.xara                           xar
+application/vnd.xfdl                           xfdl
+application/vnd.xmpie.cpkg
+application/vnd.xmpie.dpkg
+application/vnd.xmpie.plan
+application/vnd.xmpie.ppkg
+application/vnd.xmpie.xlim
+application/vnd.yamaha.hv-dic                  hvd
+application/vnd.yamaha.hv-script               hvs
+application/vnd.yamaha.hv-voice                        hvp
+application/vnd.yamaha.smaf-audio              saf
+application/vnd.yamaha.smaf-phrase             spf
+application/vnd.yellowriver-custom-menu                cmp
+application/vnd.zzazz.deck+xml                 zaz
+application/voicexml+xml                       vxml
+application/watcherinfo+xml
+application/whoispp-query
+application/whoispp-response
+application/winhlp                             hlp
+application/wita
+application/wordperfect5.1
+application/wsdl+xml                           wsdl
+application/wspolicy+xml                       wspolicy
+application/x-ace-compressed                   ace
+application/x-bcpio                            bcpio
+application/x-bittorrent                       torrent
+application/x-bzip                             bz
+application/x-bzip2                            bz2 boz
+application/x-cdlink                           vcd
+application/x-chat                             chat
+application/x-chess-pgn                                pgn
+application/x-compress
+application/x-cpio                             cpio
+application/x-csh                              csh
+application/x-director                         dcr dir dxr fgd
+application/x-dvi                              dvi
+application/x-futuresplash                     spl
+application/x-gtar                             gtar
+application/x-gzip
+application/x-hdf                              hdf
+application/x-java-jnlp-file   jnlp
+application/x-latex                            latex
+application/x-ms-wmd                           wmd
+application/x-ms-wmz                           wmz
+application/x-msaccess                         mdb
+application/x-msbinder                         obd
+application/x-mscardfile                       crd
+application/x-msclip                           clp
+application/x-msdownload                       exe dll com bat msi
+application/x-msmediaview                      mvb m13 m14
+application/x-msmetafile                       wmf
+application/x-msmoney                          mny
+application/x-mspublisher                      pub
+application/x-msschedule                       scd
+application/x-msterminal                       trm
+application/x-mswrite                          wri
+application/x-netcdf                           nc cdf
+application/x-pkcs12                           p12 pfx
+application/x-pkcs7-certificates               p7b spc
+application/x-pkcs7-certreqresp                        p7r
+application/x-rar-compressed                   rar
+application/x-sh                               sh
+application/x-shar                             shar
+application/x-shockwave-flash                  swf
+application/x-stuffit                          sit
+application/x-stuffitx                         sitx
+application/x-sv4cpio                          sv4cpio
+application/x-sv4crc                           sv4crc
+application/x-tar                              tar
+application/x-tcl                              tcl
+application/x-tex                              tex
+application/x-texinfo                          texinfo texi
+application/x-ustar                            ustar
+application/x-wais-source                      src
+application/x-x509-ca-cert                     der crt
+application/x400-bp
+application/xcap-att+xml
+application/xcap-caps+xml
+application/xcap-el+xml
+application/xcap-error+xml
+application/xcap-ns+xml
+application/xenc+xml                           xenc
+application/xhtml+xml                          xhtml xht
+application/xml                                        xml xsl
+application/xml-dtd                            dtd
+application/xml-external-parsed-entity
+application/xmpp+xml
+application/xop+xml                            xop
+application/xslt+xml                           xslt
+application/xspf+xml                           xspf
+application/xv+xml                             mxml xhvml xvml xvm
+application/zip                                        zip
+audio/32kadpcm
+audio/3gpp
+audio/3gpp2
+audio/ac3
+audio/amr
+audio/amr-wb
+audio/amr-wb+
+audio/asc
+audio/basic                                    au snd
+audio/bv16
+audio/bv32
+audio/clearmode
+audio/cn
+audio/dat12
+audio/dls
+audio/dsr-es201108
+audio/dsr-es202050
+audio/dsr-es202211
+audio/dsr-es202212
+audio/dvi4
+audio/eac3
+audio/evrc
+audio/evrc-qcp
+audio/evrc0
+audio/evrc1
+audio/evrcb
+audio/evrcb0
+audio/evrcb1
+audio/g722
+audio/g7221
+audio/g723
+audio/g726-16
+audio/g726-24
+audio/g726-32
+audio/g726-40
+audio/g728
+audio/g729
+audio/g7291
+audio/g729d
+audio/g729e
+audio/gsm
+audio/gsm-efr
+audio/ilbc
+audio/l16
+audio/l20
+audio/l24
+audio/l8
+audio/lpc
+audio/midi                                     mid midi kar rmi
+audio/mobile-xmf
+audio/mp4                                      mp4a
+audio/mp4a-latm                        m4a m4p
+audio/mpa
+audio/mpa-robust
+audio/mpeg                                     mpga mp2 mp2a mp3 m2a m3a
+audio/mpeg4-generic
+audio/parityfec
+audio/pcma
+audio/pcmu
+audio/prs.sid
+audio/qcelp
+audio/red
+audio/rtp-enc-aescm128
+audio/rtp-midi
+audio/rtx
+audio/smv
+audio/smv0
+audio/smv-qcp
+audio/sp-midi
+audio/t140c
+audio/t38
+audio/telephone-event
+audio/tone
+audio/vdvi
+audio/vmr-wb
+audio/vnd.3gpp.iufp
+audio/vnd.4sb
+audio/vnd.audiokoz
+audio/vnd.celp
+audio/vnd.cisco.nse
+audio/vnd.cmles.radio-events
+audio/vnd.cns.anp1
+audio/vnd.cns.inf1
+audio/vnd.digital-winds                                eol
+audio/vnd.dlna.adts
+audio/vnd.dolby.mlp
+audio/vnd.everad.plj
+audio/vnd.hns.audio
+audio/vnd.lucent.voice                         lvp
+audio/vnd.nokia.mobile-xmf
+audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800                      ecelp4800
+audio/vnd.nuera.ecelp7470                      ecelp7470
+audio/vnd.nuera.ecelp9600                      ecelp9600
+audio/vnd.octel.sbc
+audio/vnd.qcelp
+audio/vnd.rhetorex.32kadpcm
+audio/vnd.sealedmedia.softseal.mpeg
+audio/vnd.vmx.cvsd
+audio/wav                                      wav
+audio/x-aiff                                   aif aiff aifc
+audio/x-mpegurl                                        m3u
+audio/x-ms-wax                                 wax
+audio/x-ms-wma                                 wma
+audio/x-pn-realaudio                           ram ra
+audio/x-pn-realaudio-plugin                    rmp
+audio/x-wav                                    wav
+chemical/x-cdx                                 cdx
+chemical/x-cif                                 cif
+chemical/x-cmdf                                        cmdf
+chemical/x-cml                                 cml
+chemical/x-csml                                        csml
+chemical/x-pdb                                 pdb
+chemical/x-xyz                                 xyz
+image/bmp                                      bmp
+image/cgm                                      cgm
+image/fits
+image/g3fax                                    g3
+image/gif                                      gif
+image/ief                                      ief
+image/jp2                      jp2
+image/jpeg                                     jpeg jpg jpe
+image/jpm
+image/jpx
+image/naplps
+image/pict                     pict pic pct
+image/png                                      png
+image/prs.btif                                 btif
+image/prs.pti
+image/svg+xml                                  svg svgz
+image/t38
+image/tiff                                     tiff tif
+image/tiff-fx
+image/vnd.adobe.photoshop                      psd
+image/vnd.cns.inf2
+image/vnd.djvu                                 djvu djv
+image/vnd.dwg                                  dwg
+image/vnd.dxf                                  dxf
+image/vnd.fastbidsheet                         fbs
+image/vnd.fpx                                  fpx
+image/vnd.fst                                  fst
+image/vnd.fujixerox.edmics-mmr                 mmr
+image/vnd.fujixerox.edmics-rlc                 rlc
+image/vnd.globalgraphics.pgb
+image/vnd.microsoft.icon                       ico
+image/vnd.mix
+image/vnd.ms-modi                              mdi
+image/vnd.net-fpx                              npx
+image/vnd.sealed.png
+image/vnd.sealedmedia.softseal.gif
+image/vnd.sealedmedia.softseal.jpg
+image/vnd.svf
+image/vnd.wap.wbmp                             wbmp
+image/vnd.xiff                                 xif
+image/x-cmu-raster                             ras
+image/x-cmx                                    cmx
+image/x-icon
+image/x-macpaint               pntg pnt mac
+image/x-pcx                                    pcx
+image/x-pict                                   pic pct
+image/x-portable-anymap                                pnm
+image/x-portable-bitmap                                pbm
+image/x-portable-graymap                       pgm
+image/x-portable-pixmap                                ppm
+image/x-quicktime              qtif qti
+image/x-rgb                                    rgb
+image/x-xbitmap                                        xbm
+image/x-xpixmap                                        xpm
+image/x-xwindowdump                            xwd
+message/cpim
+message/delivery-status
+message/disposition-notification
+message/external-body
+message/http
+message/news
+message/partial
+message/rfc822                                 eml mime
+message/s-http
+message/sip
+message/sipfrag
+message/tracking-status
+model/iges                                     igs iges
+model/mesh                                     msh mesh silo
+model/vnd.dwf                                  dwf
+model/vnd.flatland.3dml
+model/vnd.gdl                                  gdl
+model/vnd.gs.gdl
+model/vnd.gtw                                  gtw
+model/vnd.moml+xml
+model/vnd.mts                                  mts
+model/vnd.parasolid.transmit.binary
+model/vnd.parasolid.transmit.text
+model/vnd.vtu                                  vtu
+model/vrml                                     wrl vrml
+multipart/alternative
+multipart/appledouble
+multipart/byteranges
+multipart/digest
+multipart/encrypted
+multipart/form-data
+multipart/header-set
+multipart/mixed
+multipart/parallel
+multipart/related
+multipart/report
+multipart/signed
+multipart/voice-message
+text/calendar                                  ics ifb
+text/css                                       css
+text/csv                                       csv
+text/directory
+text/dns
+text/enriched
+text/html                                      html htm
+text/cache-manifest                            manifest
+text/parityfec
+text/plain                                     txt text conf def list log in
+text/prs.fallenstein.rst
+text/prs.lines.tag                             dsc
+text/red
+text/rfc822-headers
+text/richtext                                  rtx
+text/rtf
+text/rtp-enc-aescm128
+text/rtx
+text/sgml                                      sgml sgm
+text/t140
+text/tab-separated-values                      tsv
+text/troff                                     t tr roff man me ms
+text/uri-list                                  uri uris urls
+text/vnd.abc
+text/vnd.curl
+text/vnd.dmclientscript
+text/vnd.esmertec.theme-descriptor
+text/vnd.fly                                   fly
+text/vnd.fmi.flexstor                          flx
+text/vnd.in3d.3dml                             3dml
+text/vnd.in3d.spot                             spot
+text/vnd.iptc.newsml
+text/vnd.iptc.nitf
+text/vnd.latex-z
+text/vnd.motorola.reflex
+text/vnd.ms-mediapackage
+text/vnd.net2phone.commcenter.command
+text/vnd.sun.j2me.app-descriptor               jad
+text/vnd.trolltech.linguist
+text/vnd.wap.si
+text/vnd.wap.sl
+text/vnd.wap.wml                               wml
+text/vnd.wap.wmlscript                         wmls
+text/x-asm                                     s asm
+text/x-c                                       c cc cxx cpp h hh dic
+text/x-fortran                                 f for f77 f90
+text/x-pascal                                  p pas
+text/x-java-source                             java
+text/x-setext                                  etx
+text/x-uuencode                                        uu
+text/x-vcalendar                               vcs
+text/x-vcard                                   vcf
+text/xml
+text/xml-external-parsed-entity
+video/3gpp                                     3gp
+video/3gpp-tt
+video/3gpp2                                    3g2
+video/bmpeg
+video/bt656
+video/celb
+video/dv
+video/h261                                     h261
+video/h263                                     h263
+video/h263-1998
+video/h263-2000
+video/h264                                     h264
+video/jpeg                                     jpgv
+video/jpm                                      jpm jpgm
+video/mj2                                      mj2 mjp2
+video/mp1s
+video/mp2p
+video/mp2t
+video/mp4                                      mp4 mp4v mpg4 m4v
+video/mp4v-es
+video/mpeg                                     mpeg mpg mpe m1v m2v
+video/mpeg4-generic
+video/mpv
+video/nv
+video/parityfec
+video/pointer
+video/quicktime                                        qt mov
+video/raw
+video/rtp-enc-aescm128
+video/rtx
+video/smpte292m
+video/vc1
+video/vnd.dlna.mpeg-tts
+video/vnd.fvt                                  fvt
+video/vnd.hns.video
+video/vnd.motorola.video
+video/vnd.motorola.videop
+video/vnd.mpegurl                              mxu m4u
+video/vnd.nokia.interleaved-multimedia
+video/vnd.nokia.videovoip
+video/vnd.objectvideo
+video/vnd.sealed.mpeg1
+video/vnd.sealed.mpeg4
+video/vnd.sealed.swf
+video/vnd.sealedmedia.softseal.mov
+video/vnd.vivo                                 viv
+video/x-dv                     dv dif
+video/x-fli                                    fli
+video/x-ms-asf                                 asf asx
+video/x-ms-wm                                  wm
+video/x-ms-wmv                                 wmv
+video/x-ms-wmx                                 wmx
+video/x-ms-wvx                                 wvx
+video/x-msvideo                                        avi
+video/x-sgi-movie                              movie
+x-conference/x-cooltalk                                ice