Thomas Bruederli
2014-06-02 d58c39126f6e1754e29b6f3bbc01f0f6a3ea2581
Some more improvemements on content structure, text representation and keyboard navigation within the mail view
13 files modified
77 ■■■■ changed files
program/include/rcmail.php 2 ●●● patch | view | raw | blame | history
program/include/rcmail_output_html.php 4 ●●●● patch | view | raw | blame | history
program/js/app.js 35 ●●●● patch | view | raw | blame | history
program/js/list.js 3 ●●●● patch | view | raw | blame | history
program/localization/en_US/labels.inc 3 ●●●●● patch | view | raw | blame | history
program/steps/mail/list_contacts.inc 2 ●●● patch | view | raw | blame | history
program/steps/mail/search_contacts.inc 2 ●●● patch | view | raw | blame | history
skins/classic/mail.css 1 ●●●● patch | view | raw | blame | history
skins/larry/styles.css 6 ●●●●● patch | view | raw | blame | history
skins/larry/templates/compose.html 10 ●●●● patch | view | raw | blame | history
skins/larry/templates/mail.html 1 ●●●● patch | view | raw | blame | history
skins/larry/templates/messagepart.html 4 ●●●● patch | view | raw | blame | history
skins/larry/templates/messagepreview.html 4 ●●●● patch | view | raw | blame | history
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,
            );
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']);
        }
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)
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)
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';
?>
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);
        }
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');
        }
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
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;
}
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>
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>
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>
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>