From d58c39126f6e1754e29b6f3bbc01f0f6a3ea2581 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Mon, 02 Jun 2014 10:35:12 -0400
Subject: [PATCH] Some more improvemements on content structure, text representation and keyboard navigation within the mail view

---
 program/js/list.js                        |    3 -
 program/include/rcmail_output_html.php    |    4 +-
 skins/larry/templates/messagepart.html    |    4 +-
 program/steps/mail/list_contacts.inc      |    2 
 skins/larry/templates/messagepreview.html |    4 +-
 skins/larry/templates/mail.html           |    1 
 skins/classic/mail.css                    |    1 
 program/include/rcmail.php                |    2 
 program/localization/en_US/labels.inc     |    3 +
 program/steps/mail/search_contacts.inc    |    2 
 skins/larry/styles.css                    |    6 +++
 program/js/app.js                         |   35 ++++++++++++++---
 skins/larry/templates/compose.html        |   10 ++--
 13 files changed, 54 insertions(+), 23 deletions(-)

diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index cc70739..8e66e85 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -1501,7 +1501,7 @@
             $html_name = $this->Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $unread)) : '');
             $link_attrib = $folder['virtual'] ? array() : array(
                 'href' => $this->url(array('_mbox' => $folder['id'])),
-                'onclick' => sprintf("return %s.command('list','%s',this)", rcmail_output::JS_OBJECT_NAME, $js_name),
+                'onclick' => sprintf("return %s.command('list','%s',this,event)", rcmail_output::JS_OBJECT_NAME, $js_name),
                 'rel' => $folder['id'],
                 'title' => $title,
             );
diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php
index 1fa0376..e8b5c98 100644
--- a/program/include/rcmail_output_html.php
+++ b/program/include/rcmail_output_html.php
@@ -859,10 +859,10 @@
         }
 
         // localize title and summary attributes
-        if (!empty($attrib['title']) && $this->app->text_exists($attrib['title'])) {
+        if ($command != 'button' && !empty($attrib['title']) && $this->app->text_exists($attrib['title'])) {
             $attrib['title'] = $this->app->gettext($attrib['title']);
         }
-        if (!empty($attrib['summary']) && $this->app->text_exists($attrib['summary'])) {
+        if ($command != 'button' && !empty($attrib['summary']) && $this->app->text_exists($attrib['summary'])) {
             $attrib['summary'] = $this->app->gettext($attrib['summary']);
         }
 
diff --git a/program/js/app.js b/program/js/app.js
index bf163c6..e7f9d02 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -342,7 +342,16 @@
             .addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); })
             .addEventListener('select', function(o) { ref.compose_recipient_select(o); })
             .addEventListener('dblclick', function(o) { ref.compose_add_recipient('to'); })
-            .addEventListener('keypress', function(o) { if (o.key_pressed == o.ENTER_KEY) ref.compose_add_recipient('to'); })
+            .addEventListener('keypress', function(o) {
+              if (o.key_pressed == o.ENTER_KEY) {
+                if (!ref.compose_add_recipient('to')) {
+                  // execute link action on <enter> if not a recipient entry
+                  if (o.last_selected && String(o.last_selected).charAt(0) == 'G') {
+                    $(o.rows[o.last_selected].obj).find('a').first().click();
+                  }
+                }
+              }
+            })
             .init();
         }
 
@@ -602,7 +611,7 @@
   {
     var ret, uid, cid, url, flag, aborted = false;
 
-    if (obj && obj.blur)
+    if (obj && obj.blur && !(event || rcube_event.is_keyboard(event)))
       obj.blur();
 
     // do nothing if interface is locked by other command (with exception for searching reset)
@@ -1647,9 +1656,12 @@
   {
     // Helper method to move focus to the next/prev active menu item
     var focus_menu_item = function(dir) {
-      var obj, mod = dir < 0 ? 'prevAll' : 'nextAll', limit = dir < 0 ? 'last' : 'first';
+      var obj, item, mod = dir < 0 ? 'prevAll' : 'nextAll', limit = dir < 0 ? 'last' : 'first';
       if (ref.focused_menu && (obj = $('#'+ref.focused_menu))) {
-        return obj.find(':focus').closest('li')[mod](':has(:not([aria-disabled=true]))').find('a,input')[limit]().focus().length;
+        item = obj.find(':focus').closest('li')[mod](':has(:not([aria-disabled=true]))').find('a,input')[limit]();
+        if (!item.length)
+          item = obj.find(':focus').closest('ul')[mod](':has(:not([aria-disabled=true]))').find('a,input')[limit]();
+        return item.focus().length;
       }
 
       return 0;
@@ -2402,7 +2414,6 @@
   this.clear_message_list = function()
   {
     this.env.messages = {};
-    this.last_selected = 0;
 
     this.show_contentframe(false);
     if (this.message_list)
@@ -3490,6 +3501,8 @@
       input.val(oldval + recipients.join(delim + ' ') + delim + ' ');
       this.triggerEvent('add-recipient', { field:field, recipients:recipients });
     }
+
+    return recipients.length;
   };
 
   // checks the input fields before sending a message
@@ -7405,7 +7418,8 @@
           this.enable_command('set-listmode', this.env.threads && !is_multifolder);
 
           if ((response.action == 'list' || response.action == 'search') && this.message_list) {
-            this.message_list.focus();
+            if (this.message_list.rowcount > 0)
+              this.message_list.focus();
             this.msglist_select(this.message_list);
             this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount });
           }
@@ -7417,11 +7431,18 @@
             this.enable_command('search-create', this.env.source == '');
             this.enable_command('search-delete', this.env.search_id);
             this.update_group_commands();
-            this.contact_list.focus();
+            if (this.contact_list.rowcount > 0)
+              this.contact_list.focus();
             this.triggerEvent('listupdate', { folder:this.env.source, rowcount:this.contact_list.rowcount });
           }
         }
         break;
+
+      case 'list-contacts':
+      case 'search-contacts':
+        if (this.contact_list && this.contact_list.rowcount > 0)
+          this.contact_list.focus();
+        break;
     }
 
     if (response.unlock)
diff --git a/program/js/list.js b/program/js/list.js
index 0bf5d56..65d4a92 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -274,11 +274,10 @@
 
   this.rows = {};
   this.rowcount = 0;
+  this.last_selected = 0;
 
   if (sel)
     this.clear_selection();
-  else
-    this.last_selected = 0;
 
   // reset scroll position (in Opera)
   if (this.frame)
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index 0b5ff8c..874f19b 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -591,11 +591,14 @@
 $labels['arialabelmailimportdialog'] = 'Message import dialog';
 $labels['arialabelmessagenav'] = 'Message navigation';
 $labels['arialabelmessagebody'] = 'Message Body';
+$labels['arialabelmessageactions'] = 'Message actions';
 $labels['arialabelcontactquicksearch'] = 'Contacts search form';
 $labels['arialabelcontactsearchbox'] = 'Contact search input';
 $labels['arialabelmessageheaders'] = 'Message headers';
 $labels['arialabelcomposeoptions'] = 'Composition options';
 $labels['arialabelresponsesmenu'] = 'Canned responses menu';
 $labels['arialabelattachmentuploadform'] = 'Attachment upload form';
+$labels['arialabelattachmentpreview'] = 'Attachment preview';
+$labels['ariasummarycomposecontacts'] = 'List of contacts and groups to select as recipients';
 
 ?>
diff --git a/program/steps/mail/list_contacts.inc b/program/steps/mail/list_contacts.inc
index 0ee8113..4f17bef 100644
--- a/program/steps/mail/list_contacts.inc
+++ b/program/steps/mail/list_contacts.inc
@@ -110,7 +110,7 @@
             $keyname = $row['_type'] == 'group' ? 'contactgroup' : 'contact';
 
             $OUTPUT->command('add_contact_row', $row_id, array(
-                $keyname => html::span(array('title' => $email), rcube::Q($name ? $name : $email) .
+                $keyname => html::a(array('title' => $email), rcube::Q($name ? $name : $email) .
                     ($name && count($emails) > 1 ? '&nbsp;' . html::span('email', rcube::Q($email)) : '')
                 )), $classname);
         }
diff --git a/program/steps/mail/search_contacts.inc b/program/steps/mail/search_contacts.inc
index d565816..ccef32d 100644
--- a/program/steps/mail/search_contacts.inc
+++ b/program/steps/mail/search_contacts.inc
@@ -87,7 +87,7 @@
             $row_id = $row['ID'].'-'.$i;
             $jsresult[$row_id] = format_email_recipient($email, $name);
             $OUTPUT->command('add_contact_row', $row_id, array(
-                'contact' => html::span(array('title' => $email), rcube::Q($name ? $name : $email) .
+                'contact' => html::a(array('title' => $email), rcube::Q($name ? $name : $email) .
                     ($name && count($emails) > 1 ? '&nbsp;' . html::span('email', rcube::Q($email)) : '')
                 )), 'person');
         }
diff --git a/skins/classic/mail.css b/skins/classic/mail.css
index 0a4653a..3310ac5 100644
--- a/skins/classic/mail.css
+++ b/skins/classic/mail.css
@@ -780,6 +780,7 @@
   -o-text-overflow: ellipsis;
   border-bottom: 1px solid #EBEBEB;
   cursor: default;
+  outline: none;
 }
 
 .messagelist tbody tr td a
diff --git a/skins/larry/styles.css b/skins/larry/styles.css
index d7ad74c..add4732 100644
--- a/skins/larry/styles.css
+++ b/skins/larry/styles.css
@@ -1187,6 +1187,12 @@
 	outline: none;
 }
 
+.listing tbody td a {
+	color: #376572;
+	text-shadow: 0px 1px 1px #fff;
+	text-decoration: none;
+}
+
 .webkit .listing tbody td {
 	height: 14px;
 }
diff --git a/skins/larry/templates/compose.html b/skins/larry/templates/compose.html
index 87993ed..c643b36 100644
--- a/skins/larry/templates/compose.html
+++ b/skins/larry/templates/compose.html
@@ -52,7 +52,7 @@
 			<roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" content=" " />
 		</div>
 	</div>
-	<roundcube:object name="addressbooks" id="directorylist" class="treelist listing" />
+	<roundcube:object name="addressbooks" id="directorylist" class="treelist listing" summary="ariasummarycomposecontacts" />
 	<div class="scroller withfooter" tabindex="-1">
 		<roundcube:object name="addresslist" id="contacts-table" class="listing iconized" noheader="true" />
 	</div>
@@ -93,25 +93,25 @@
 	</tr><tr id="compose-cc">
 		<td class="title top">
 			<label for="_cc"><roundcube:label name="cc" /></label>
-			<a href="#cc" onclick="return UI.hide_header_row('cc');" class="iconbutton cancel" title="<roundcube:label name='delete' /> <roundcube:label name='cc' />" tabindex="3">x</a>
+			<a href="#cc" onclick="return UI.hide_header_row('cc');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="cc" /></a>
 		</td>
 		<td class="editfield"><roundcube:object name="composeHeaders" part="cc" form="form" id="_cc" cols="70" rows="1" tabindex="1" /></td>
 	</tr><tr id="compose-bcc">
 		<td class="title top">
 			<label for="_bcc"><roundcube:label name="bcc" /></label>
-			<a href="#bcc" onclick="return UI.hide_header_row('bcc');" class="iconbutton cancel" title="<roundcube:label name='delete' /> <roundcube:label name='bcc' />" tabindex="3">x</a>
+			<a href="#bcc" onclick="return UI.hide_header_row('bcc');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="bcc" /></a>
 		</td>
 		<td class="editfield"><roundcube:object name="composeHeaders" part="bcc" form="form" id="_bcc" cols="70" rows="1" tabindex="1" /></td>
 	</tr><tr id="compose-replyto">
 		<td class="title top">
 			<label for="_replyto"><roundcube:label name="replyto" /></label>
-			<a href="#replyto" onclick="return UI.hide_header_row('replyto');" class="iconbutton cancel" title="<roundcube:label name='delete' /> <roundcube:label name='replyto' />" tabindex="3">x</a>
+			<a href="#replyto" onclick="return UI.hide_header_row('replyto');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="replyto" /></a>
 		</td>
 		<td class="editfield"><roundcube:object name="composeHeaders" part="replyto" form="form" id="_replyto" size="70" tabindex="1" /></td>
 	</tr><tr id="compose-followupto">
 		<td class="title top">
 			<label for="_followupto"><roundcube:label name="followupto" /></label>
-			<a href="#followupto" onclick="return UI.hide_header_row('followupto');" class="iconbutton cancel" title="<roundcube:label name='delete' /> <roundcube:label name='followupto' />" tabindex="3">x</a>
+			<a href="#followupto" onclick="return UI.hide_header_row('followupto');" class="iconbutton cancel" title="<roundcube:label name='delete' />" tabindex="3"><roundcube:label name="delete" /> <roundcube:label name="followupto" /></a>
 		</td>
 		<td class="editfield"><roundcube:object name="composeHeaders" part="followupto" form="form" id="_followupto" size="70" tabindex="1" /></td>
 	</tr><tr>
diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html
index 92b01e0..cf8ff2f 100644
--- a/skins/larry/templates/mail.html
+++ b/skins/larry/templates/mail.html
@@ -75,6 +75,7 @@
 <div id="folderlist-footer" class="boxfooter">
 	<roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="UI.toggle_popup('mailboxmenu',event);return false" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false"aria-owns="mailboxmenu-menu" />
 	<roundcube:if condition="env:quota" />
+		<span class="voice"><roundcube:label name="quota"></span>
 		<roundcube:object name="quotaDisplay" id="quotadisplay" class="countdisplay" display="text" />
 	<roundcube:endif />
 </div>
diff --git a/skins/larry/templates/messagepart.html b/skins/larry/templates/messagepart.html
index 2df9c7b..edf275f 100644
--- a/skins/larry/templates/messagepart.html
+++ b/skins/larry/templates/messagepart.html
@@ -29,9 +29,9 @@
 </div>
 
 <div id="messagepartcontainer" class="uibox" role="main" aria-labelledby="aria-label-messagepart">
-	<h2 id="aria-label-messagepart" class="voice">Attachment preview</h2>
+	<h2 id="aria-label-messagepart" class="voice"><roundcube:label name="arialabelattachmentpreview" /></h2>
 	<div class="iframebox">
-	<roundcube:object name="messagePartFrame" id="messagepartframe" frameborder="0" title="Attachment preview" />
+	<roundcube:object name="messagePartFrame" id="messagepartframe" frameborder="0" title="arialabelattachmentpreview" />
 	</div>
 </div>
 
diff --git a/skins/larry/templates/messagepreview.html b/skins/larry/templates/messagepreview.html
index 97efdf3..82ecd7a 100644
--- a/skins/larry/templates/messagepreview.html
+++ b/skins/larry/templates/messagepreview.html
@@ -10,7 +10,7 @@
 
 <!-- record navigation -->
 <div id="countcontrols" role="toolbar" aria-labelledby="aria-label-messagetoolbar">
-<h2 id="aria-label-messagetoolbar" class="voice">Message actions</h2>
+<h2 id="aria-label-messagetoolbar" class="voice"><roundcube:label name="arialabelmessageactions" /></h2>
 <roundcube:if condition="env:optional_format=='text'" />
 	<span class="buttongroup">
 		<roundcube:button command="change-format" prop="html" type="link" class="button first changeformat html selected" innerClass="icon" title="changeformathtml" content="HTML" /><roundcube:button command="change-format" prop="text" type="link" class="button last changeformat text" classSel="button changeformat text pressed" innerClass="icon" title="changeformattext" content="Text" />
@@ -60,7 +60,7 @@
 <roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" />
 </div>
 <div class="leftcol" role="region" aria-labelledby="aria-label-messagebody">
-<h2 id="aria-label-messagebody" class="voice">Message Body</h2>
+<h2 id="aria-label-messagebody" class="voice"><roundcube:label name="arialabelmessagebody" /></h2>
 <roundcube:object name="messageObjects" id="message-objects" />
 <roundcube:object name="messageBody" id="messagebody" headertableclass="message-partheaders headers-table" />
 </div>

--
Gitblit v1.9.1