Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F224284
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
220 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/program/include/main.inc b/program/include/main.inc
index a6ad93d2b..87c727700 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -1,1159 +1,1173 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/main.inc |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005-2008, RoundCube Dev, - Switzerland |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Provide basic functions for the webmail package |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
/**
* RoundCube Webmail common functions
*
* @package Core
* @author Thomas Bruederli <roundcube@gmail.com>
*/
require_once('lib/utf7.inc');
require_once('include/rcube_shared.inc');
// fallback if not PHP modules are available
@include_once('lib/des.inc');
@include_once('lib/utf8.class.php');
// define constannts for input reading
define('RCUBE_INPUT_GET', 0x0101);
define('RCUBE_INPUT_POST', 0x0102);
define('RCUBE_INPUT_GPC', 0x0103);
/**
* Return correct name for a specific database table
*
* @param string Table name
* @return string Translated table name
*/
function get_table_name($table)
{
global $CONFIG;
// return table name if configured
$config_key = 'db_table_'.$table;
if (strlen($CONFIG[$config_key]))
return $CONFIG[$config_key];
return $table;
}
/**
* Return correct name for a specific database sequence
* (used for Postgres only)
*
* @param string Secuence name
* @return string Translated sequence name
*/
function get_sequence_name($sequence)
{
// return table name if configured
$config_key = 'db_sequence_'.$sequence;
$opt = rcmail::get_instance()->config->get($config_key);
if (!empty($opt))
{
$db = &rcmail::get_instance()->db;
if ($db->db_provider=='pgsql')
{
$db->db_handle->setOption('disable_smart_seqname', true);
$db->db_handle->setOption('seqname_format', '%s');
}
return $opt;
}
return $sequence;
}
/**
* Get localized text in the desired language
* It's a global wrapper for rcmail::gettext()
*
* @param mixed Named parameters array or label name
* @return string Localized text
* @see rcmail::gettext()
*/
function rcube_label($p)
{
return rcmail::get_instance()->gettext($p);
}
/**
* Overwrite action variable
*
* @param string New action value
*/
function rcmail_overwrite_action($action)
{
$app = rcmail::get_instance();
$app->action = $action;
$app->output->set_env('action', $action);
}
/**
* Compose an URL for a specific action
*
* @param string Request action
* @param array More URL parameters
* @param string Request task (omit if the same)
* @return The application URL
*/
function rcmail_url($action, $p=array(), $task=null)
{
$app = rcmail::get_instance();
return $app->url((array)$p + array('_action' => $action, 'task' => $task));
}
/**
* Add a localized label to the client environment
* @deprecated
*/
function rcube_add_label()
{
global $OUTPUT;
$arg_list = func_get_args();
foreach ($arg_list as $i => $name)
$OUTPUT->add_label($name);
}
/**
* Garbage collector function for temp files.
* Remove temp files older than two days
*/
function rcmail_temp_gc()
{
$tmp = unslashify($CONFIG['temp_dir']);
$expire = mktime() - 172800; // expire in 48 hours
if ($dir = opendir($tmp))
{
while (($fname = readdir($dir)) !== false)
{
if ($fname{0} == '.')
continue;
if (filemtime($tmp.'/'.$fname) < $expire)
@unlink($tmp.'/'.$fname);
}
closedir($dir);
}
}
/**
* Garbage collector for cache entries.
* Remove all expired message cache records
*/
function rcmail_message_cache_gc()
{
global $DB, $CONFIG;
// no cache lifetime configured
if (empty($CONFIG['message_cache_lifetime']))
return;
// get target timestamp
$ts = get_offset_time($CONFIG['message_cache_lifetime'], -1);
$DB->query("DELETE FROM ".get_table_name('messages')."
WHERE created < ".$DB->fromunixtime($ts));
}
/**
* Convert a string from one charset to another.
* Uses mbstring and iconv functions if possible
*
* @param string Input string
* @param string Suspected charset of the input string
* @param string Target charset to convert to; defaults to RCMAIL_CHARSET
* @return Converted string
*/
function rcube_charset_convert($str, $from, $to=NULL)
{
static $mbstring_loaded = null, $convert_warning = false;
$from = strtoupper($from);
$to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to);
$error = false; $conv = null;
if ($from==$to || $str=='' || empty($from))
return $str;
$aliases = array(
'UNKNOWN-8BIT' => 'ISO-8859-15',
'X-UNKNOWN' => 'ISO-8859-15',
'X-USER-DEFINED' => 'ISO-8859-15',
'ISO-8859-8-I' => 'ISO-8859-8',
'KS_C_5601-1987' => 'EUC-KR',
);
// convert charset using iconv module
if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7')
{
$aliases['GB2312'] = 'GB18030';
$_iconv = iconv(($aliases[$from] ? $aliases[$from] : $from), ($aliases[$to] ? $aliases[$to] : $to) . "//IGNORE", $str);
if ($_iconv !== false)
{
return $_iconv;
}
}
// settings for mbstring module (by Tadashi Jokagi)
if (is_null($mbstring_loaded)) {
if ($mbstring_loaded = extension_loaded("mbstring"))
mb_internal_encoding(RCMAIL_CHARSET);
}
// convert charset using mbstring module
if ($mbstring_loaded)
{
$aliases['UTF-7'] = 'UTF7-IMAP';
$aliases['WINDOWS-1257'] = 'ISO-8859-13';
// return if convert succeeded
if (($out = mb_convert_encoding($str, ($aliases[$to] ? $aliases[$to] : $to), ($aliases[$from] ? $aliases[$from] : $from))) != '')
return $out;
}
if (class_exists('utf8'))
$conv = new utf8();
// convert string to UTF-8
if ($from == 'UTF-7')
$str = utf7_to_utf8($str);
else if (($from == 'ISO-8859-1') && function_exists('utf8_encode'))
$str = utf8_encode($str);
else if ($from != 'UTF-8' && $conv)
{
$conv->loadCharset($from);
$str = $conv->strToUtf8($str);
}
else if ($from != 'UTF-8')
$error = true;
// encode string for output
if ($to == 'UTF-7')
return utf8_to_utf7($str);
else if ($to == 'ISO-8859-1' && function_exists('utf8_decode'))
return utf8_decode($str);
else if ($to != 'UTF-8' && $conv)
{
$conv->loadCharset($to);
return $conv->utf8ToStr($str);
}
else if ($to != 'UTF-8')
$error = true;
// report error
if ($error && !$convert_warning)
{
raise_error(array(
'code' => 500,
'type' => 'php',
'file' => __FILE__,
'message' => "Could not convert string charset. Make sure iconv is installed or lib/utf8.class is available"
), true, false);
$convert_warning = true;
}
// return UTF-8 string
return $str;
}
/**
* Replacing specials characters to a specific encoding type
*
* @param string Input string
* @param string Encoding type: text|html|xml|js|url
* @param string Replace mode for tags: show|replace|remove
* @param boolean Convert newlines
* @return The quoted string
*/
function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
{
global $OUTPUT;
static $html_encode_arr = false;
static $js_rep_table = false;
static $xml_rep_table = false;
$charset = $OUTPUT->get_charset();
$is_iso_8859_1 = false;
if ($charset == 'ISO-8859-1') {
$is_iso_8859_1 = true;
}
if (!$enctype)
$enctype = $OUTPUT->type;
// encode for plaintext
if ($enctype=='text')
return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str);
// encode for HTML output
if ($enctype=='html')
{
if (!$html_encode_arr)
{
$html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS);
unset($html_encode_arr['?']);
}
$ltpos = strpos($str, '<');
$encode_arr = $html_encode_arr;
// don't replace quotes and html tags
if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false)
{
unset($encode_arr['"']);
unset($encode_arr['<']);
unset($encode_arr['>']);
unset($encode_arr['&']);
}
else if ($mode=='remove')
$str = strip_tags($str);
// avoid douple quotation of &
$out = preg_replace('/&([a-z]{2,5}|#[0-9]{2,4});/', '&\\1;', strtr($str, $encode_arr));
return $newlines ? nl2br($out) : $out;
}
if ($enctype=='url')
return rawurlencode($str);
// if the replace tables for XML and JS are not yet defined
if ($js_rep_table===false)
{
$js_rep_table = $xml_rep_table = array();
$xml_rep_table['&'] = '&';
for ($c=160; $c<256; $c++) // can be increased to support more charsets
{
$xml_rep_table[Chr($c)] = "&#$c;";
if ($is_iso_8859_1)
$js_rep_table[Chr($c)] = sprintf("\\u%04x", $c);
}
$xml_rep_table['"'] = '"';
}
// encode for XML
if ($enctype=='xml')
return strtr($str, $xml_rep_table);
// encode for javascript use
if ($enctype=='js')
{
if ($charset!='UTF-8')
$str = rcube_charset_convert($str, RCMAIL_CHARSET,$charset);
return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), addslashes(strtr($str, $js_rep_table)));
}
// no encoding given -> return original string
return $str;
}
/**
* Quote a given string.
* Shortcut function for rep_specialchars_output
*
* @return string HTML-quoted string
* @see rep_specialchars_output()
*/
function Q($str, $mode='strict', $newlines=TRUE)
{
return rep_specialchars_output($str, 'html', $mode, $newlines);
}
/**
* Quote a given string for javascript output.
* Shortcut function for rep_specialchars_output
*
* @return string JS-quoted string
* @see rep_specialchars_output()
*/
function JQ($str)
{
return rep_specialchars_output($str, 'js');
}
/**
* Read input value and convert it for internal use
* Performs stripslashes() and charset conversion if necessary
*
* @param string Field name to read
* @param int Source to get value from (GPC)
* @param boolean Allow HTML tags in field value
* @param string Charset to convert into
* @return string Field value or NULL if not available
*/
function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
{
global $OUTPUT;
$value = NULL;
if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
$value = $_GET[$fname];
else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
$value = $_POST[$fname];
else if ($source==RCUBE_INPUT_GPC)
{
if (isset($_POST[$fname]))
$value = $_POST[$fname];
else if (isset($_GET[$fname]))
$value = $_GET[$fname];
else if (isset($_COOKIE[$fname]))
$value = $_COOKIE[$fname];
}
// strip slashes if magic_quotes enabled
if ((bool)get_magic_quotes_gpc())
$value = stripslashes($value);
// remove HTML tags if not allowed
if (!$allow_html)
$value = strip_tags($value);
// convert to internal charset
if (is_object($OUTPUT))
return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
else
return $value;
}
/**
* Remove all non-ascii and non-word chars
* except . and -
*/
function asciiwords($str, $css_id = false)
{
$allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : '');
return preg_replace("/[^$allowed]/i", '', $str);
}
/**
* Remove single and double quotes from given string
*
* @param string Input value
* @return string Dequoted string
*/
function strip_quotes($str)
{
return preg_replace('/[\'"]/', '', $str);
}
/**
* Remove new lines characters from given string
*
* @param string Input value
* @return string Stripped string
*/
function strip_newlines($str)
{
return preg_replace('/[\r\n]/', '', $str);
}
/**
* Create a HTML table based on the given data
*
* @param array Named table attributes
* @param mixed Table row data. Either a two-dimensional array or a valid SQL result set
* @param array List of cols to show
* @param string Name of the identifier col
* @return string HTML table code
*/
function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
{
global $RCMAIL;
$table = new html_table(/*array('cols' => count($a_show_cols))*/);
// add table header
foreach ($a_show_cols as $col)
$table->add_header($col, Q(rcube_label($col)));
$c = 0;
if (!is_array($table_data))
{
$db = $RCMAIL->get_dbh();
while ($table_data && ($sql_arr = $db->fetch_assoc($table_data)))
{
$zebra_class = $c % 2 ? 'even' : 'odd';
$table->add_row(array('id' => 'rcmrow' . $sql_arr[$id_col], 'class' => "contact $zebra_class"));
// format each col
foreach ($a_show_cols as $col)
$table->add($col, Q($sql_arr[$col]));
$c++;
}
}
else
{
foreach ($table_data as $row_data)
{
$zebra_class = $c % 2 ? 'even' : 'odd';
$table->add_row(array('id' => 'rcmrow' . $row_data[$id_col], 'class' => "contact $zebra_class"));
// format each col
foreach ($a_show_cols as $col)
$table->add($col, Q($row_data[$col]));
$c++;
}
}
return $table->show($attrib);
}
/**
* Create an edit field for inclusion on a form
*
* @param string col field name
* @param string value field value
* @param array attrib HTML element attributes for field
* @param string type HTML element type (default 'text')
* @return string HTML field definition
*/
function rcmail_get_edit_field($col, $value, $attrib, $type='text')
{
$fname = '_'.$col;
$attrib['name'] = $fname;
if ($type=='checkbox')
{
$attrib['value'] = '1';
$input = new html_checkbox($attrib);
}
else if ($type=='textarea')
{
$attrib['cols'] = $attrib['size'];
$input = new html_textarea($attrib);
}
else
$input = new html_inputfield($attrib);
// use value from post
if (!empty($_POST[$fname]))
$value = get_input_value($fname, RCUBE_INPUT_POST);
$out = $input->show($value);
return $out;
}
/**
* Replace all css definitions with #container [def]
* and remove css-inlined scripting
*
* @param string CSS source code
* @param string Container ID to use as prefix
* @return string Modified CSS source
*/
function rcmail_mod_css_styles($source, $container_id, $base_url = '')
{
$a_css_values = array();
$last_pos = 0;
// ignore the whole block if evil styles are detected
if (stristr($source, 'expression') || stristr($source, 'behavior'))
return '';
// cut out all contents between { and }
while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
{
$key = sizeof($a_css_values);
$a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1));
$source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2);
$last_pos = $pos+2;
}
// remove html comments and add #container to each tag selector.
// also replace body definition because we also stripped off the <body> tag
$styles = preg_replace(
array(
'/(^\s*<!--)|(-->\s*$)/',
'/(^\s*|,\s*|\}\s*)([a-z0-9\._#][a-z0-9\.\-_]*)/im',
'/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/ime',
'/<<str_replacement\[([0-9]+)\]>>/e',
"/$container_id\s+body/i"
),
array(
'',
"\\1#$container_id \\2",
"sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url('\\2','$base_url')), urlencode($container_id))",
"\$a_css_values[\\1]",
"$container_id div.rcmBody"
),
$source);
return $styles;
}
/**
* Compose a valid attribute string for HTML tags
*
* @param array Named tag attributes
* @param array List of allowed attributes
* @return string HTML formatted attribute string
*/
function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
{
// allow the following attributes to be added to the <iframe> tag
$attrib_str = '';
foreach ($allowed_attribs as $a)
if (isset($attrib[$a]))
$attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '"', $attrib[$a]));
return $attrib_str;
}
/**
* Convert a HTML attribute string attributes to an associative array (name => value)
*
* @param string Input string
* @return array Key-value pairs of parsed attributes
*/
function parse_attrib_string($str)
{
$attrib = array();
preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]+)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER);
// convert attributes to an associative array (name => value)
if ($regs)
foreach ($regs as $attr)
{
$attrib[strtolower($attr[1])] = $attr[3] . $attr[4];
}
return $attrib;
}
/**
* Convert the given date to a human readable form
* This uses the date formatting properties from config
*
* @param mixed Date representation (string or timestamp)
* @param string Date format to use
* @return string Formatted date string
*/
function format_date($date, $format=NULL)
{
global $CONFIG;
$ts = NULL;
if (is_numeric($date))
$ts = $date;
else if (!empty($date))
{
while (($ts = @strtotime($date))===false)
{
// if we have a date in non-rfc format
// remove token from the end and try again
$d = explode(' ', $date);
array_pop($d);
if (!$d) break;
$date = implode(' ', $d);
}
}
if (empty($ts))
return '';
// get user's timezone
if ($CONFIG['timezone'] === 'auto')
$tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600;
else {
$tz = $CONFIG['timezone'];
if ($CONFIG['dst_active'])
$tz++;
}
// convert time to user's timezone
$timestamp = $ts - date('Z', $ts) + ($tz * 3600);
// get current timestamp in user's timezone
$now = time(); // local time
$now -= (int)date('Z'); // make GMT time
$now += ($tz * 3600); // user's time
$now_date = getdate($now);
$today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']);
$week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']);
// define date format depending on current time
if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit && $timestamp < $now)
return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp));
else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit && $timestamp < $now)
$format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
else if (!$format)
$format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
// parse format string manually in order to provide localized weekday and month names
// an alternative would be to convert the date() format string to fit with strftime()
$out = '';
for($i=0; $i<strlen($format); $i++)
{
if ($format{$i}=='\\') // skip escape chars
continue;
// write char "as-is"
if ($format{$i}==' ' || $format{$i-1}=='\\')
$out .= $format{$i};
// weekday (short)
else if ($format{$i}=='D')
$out .= rcube_label(strtolower(date('D', $timestamp)));
// weekday long
else if ($format{$i}=='l')
$out .= rcube_label(strtolower(date('l', $timestamp)));
// month name (short)
else if ($format{$i}=='M')
$out .= rcube_label(strtolower(date('M', $timestamp)));
// month name (long)
else if ($format{$i}=='F')
$out .= rcube_label('long'.strtolower(date('M', $timestamp)));
else if ($format{$i}=='x')
$out .= strftime('%x %X', $timestamp);
else
$out .= date($format{$i}, $timestamp);
}
return $out;
}
/**
* Compose a valid representaion of name and e-mail address
*
* @param string E-mail address
* @param string Person name
* @return string Formatted string
*/
function format_email_recipient($email, $name='')
{
if ($name && $name != $email)
{
// Special chars as defined by RFC 822 need to in quoted string (or escaped).
return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, $email);
}
else
return $email;
}
/****** debugging functions ********/
/**
* Print or write debug messages
*
* @param mixed Debug message or data
*/
function console()
{
$msg = array();
foreach (func_get_args() as $arg)
$msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
if (!($GLOBALS['CONFIG']['debug_level'] & 4))
write_log('console', join(";\n", $msg));
else if ($GLOBALS['OUTPUT']->ajax_call)
print "/*\n " . join(";\n", $msg) . " \n*/\n";
else
{
print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>';
print join(";<br/>\n", $msg);
print "</pre></div>\n";
}
}
/**
* Append a line to a logfile in the logs directory.
* Date will be added automatically to the line.
*
* @param $name name of log file
* @param line Line to append
*/
function write_log($name, $line)
{
global $CONFIG;
if (!is_string($line))
$line = var_export($line, true);
$log_entry = sprintf("[%s]: %s\n",
date("d-M-Y H:i:s O", mktime()),
$line);
if ($CONFIG['log_driver'] == 'syslog') {
if ($name == 'errors')
$prio = LOG_ERR;
else
$prio = LOG_INFO;
syslog($prio, $log_entry);
} else {
// log_driver == 'file' is assumed here
if (empty($CONFIG['log_dir']))
$CONFIG['log_dir'] = INSTALL_PATH.'logs';
// try to open specific log file for writing
if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a')) {
fwrite($fp, $log_entry);
fclose($fp);
}
}
}
/**
* @access private
*/
function rcube_timer()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
/**
* @access private
*/
function rcube_print_time($timer, $label='Timer')
{
static $print_count = 0;
$print_count++;
$now = rcube_timer();
$diff = $now-$timer;
if (empty($label))
$label = 'Timer '.$print_count;
console(sprintf("%s: %0.4f sec", $label, $diff));
}
/**
* Return the mailboxlist in HTML
*
* @param array Named parameters
* @return string HTML code for the gui object
*/
function rcmail_mailbox_list($attrib)
{
global $RCMAIL;
static $a_mailboxes;
$attrib += array('maxlength' => 100, 'relanames' => false);
// add some labels to client
rcube_add_label('purgefolderconfirm');
rcube_add_label('deletemessagesconfirm');
$type = $attrib['type'] ? $attrib['type'] : 'ul';
unset($attrib['type']);
if ($type=='ul' && !$attrib['id'])
$attrib['id'] = 'rcmboxlist';
// get mailbox list
$mbox_name = $RCMAIL->imap->get_mailbox_name();
// build the folders tree
if (empty($a_mailboxes)) {
// get mailbox list
$a_folders = $RCMAIL->imap->list_mailboxes();
$delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
$a_mailboxes = array();
foreach ($a_folders as $folder)
rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
}
if ($type=='select') {
$select = new html_select($attrib);
// add no-selection option
if ($attrib['noselection'])
$select->add(rcube_label($attrib['noselection']), '0');
rcmail_render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']);
$out = $select->show();
}
else {
- $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $attrib['maxlength'], $attrib['realnames']), html::$common_attrib);
- }
-
- if ($type=='ul') {
+ $js_mailboxlist = array();
+ $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib);
+
$RCMAIL->output->add_gui_object('mailboxlist', $attrib['id']);
+ $RCMAIL->output->set_env('mailboxes', $js_mailboxlist);
$RCMAIL->output->set_env('collapsed_folders', $RCMAIL->config->get('collapsed_folders'));
}
return $out;
}
/**
* Return the mailboxlist as html_select object
*
* @param array Named parameters
* @return object html_select HTML drop-down object
*/
function rcmail_mailbox_select($p = array())
{
global $RCMAIL;
$p += array('maxlength' => 100, 'relanames' => false);
$a_mailboxes = array();
foreach ($RCMAIL->imap->list_mailboxes() as $folder)
rcmail_build_folder_tree($a_mailboxes, $folder, $RCMAIL->imap->get_hierarchy_delimiter());
$select = new html_select($p);
if ($p['noselection'])
$select->add($p['noselection'], '');
rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames']);
return $select;
}
/**
* Create a hierarchical array of the mailbox list
* @access private
*/
function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
- {
+{
$pos = strpos($folder, $delm);
- if ($pos !== false)
- {
+ if ($pos !== false) {
$subFolders = substr($folder, $pos+1);
$currentFolder = substr($folder, 0, $pos);
- }
- else
- {
+ $virtual = !isset($arrFolders[$currentFolder]);
+ }
+ else {
$subFolders = false;
$currentFolder = $folder;
- }
+ $virtual = false;
+ }
$path .= $currentFolder;
- if (!isset($arrFolders[$currentFolder]))
- {
+ if (!isset($arrFolders[$currentFolder])) {
$arrFolders[$currentFolder] = array(
'id' => $path,
'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
+ 'virtual' => $virtual,
'folders' => array());
- }
+ }
+ else
+ $arrFolders[$currentFolder]['virtual'] = $virtual;
if (!empty($subFolders))
rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
- }
+}
/**
* Return html for a structured list <ul> for the mailbox tree
* @access private
*/
-function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, $maxlength, $realnames=false, $nestLevel=0)
- {
+function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel=0)
+{
global $RCMAIL, $CONFIG;
+
+ $maxlength = intval($attrib['maxlength']);
+ $realnames = (bool)$attrib['realnames'];
+ $msgcounts = $RCMAIL->imap->get_cache('messagecount');
$idx = 0;
$out = '';
- foreach ($arrFolders as $key => $folder)
- {
+ foreach ($arrFolders as $key => $folder) {
$zebra_class = (($nestLevel+1)*$idx) % 2 == 0 ? 'even' : 'odd';
$title = null;
- if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames)
+ if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) {
$foldername = rcube_label($folder_class);
- else
- {
+ }
+ else {
$foldername = $folder['name'];
// shorten the folder name to a given length
- if ($maxlength && $maxlength>1)
- {
+ if ($maxlength && $maxlength > 1) {
$fname = abbreviate_string($foldername, $maxlength);
if ($fname != $foldername)
$title = $foldername;
$foldername = $fname;
- }
}
+ }
// make folder name safe for ids and class names
$folder_id = asciiwords($folder['id'], true);
$classes = array('mailbox');
// set special class for Sent, Drafts, Trash and Junk
if ($folder['id']==$CONFIG['sent_mbox'])
$classes[] = 'sent';
else if ($folder['id']==$CONFIG['drafts_mbox'])
$classes[] = 'drafts';
else if ($folder['id']==$CONFIG['trash_mbox'])
$classes[] = 'trash';
else if ($folder['id']==$CONFIG['junk_mbox'])
$classes[] = 'junk';
else
$classes[] = asciiwords($folder_class ? $folder_class : strtolower($folder['id']), true);
$classes[] = $zebra_class;
if ($folder['id'] == $mbox_name)
$classes[] = 'selected';
$collapsed = preg_match('/&'.rawurlencode($folder['id']).'&/', $RCMAIL->config->get('collapsed_folders'));
+ $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
+
+ if ($folder['virtual'])
+ $classes[] = 'virtual';
+ else if ($unread)
+ $classes[] = 'unread';
$js_name = JQ($folder['id']);
+ $html_name = Q($foldername . ($unread ? " ($unread)" : ''));
+ $link_attrib = $folder['virtual'] ? array() : array(
+ 'href' => rcmail_url('', array('_mbox' => $folder['id'])),
+ 'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name),
+ 'title' => $title,
+ );
+
$out .= html::tag('li', array(
'id' => "rcmli".$folder_id,
'class' => join(' ', $classes),
'noclose' => true),
- html::a(array(
- 'href' => rcmail_url('', array('_mbox' => $folder['id'])),
- 'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name),
- 'onmouseover' => sprintf("return %s.focus_folder('%s')", JS_OBJECT_NAME, $js_name),
- 'onmouseout' => sprintf("return %s.unfocus_folder('%s')", JS_OBJECT_NAME, $js_name),
- 'onmouseup' => sprintf("return %s.folder_mouse_up('%s')", JS_OBJECT_NAME, $js_name),
- 'title' => $title,
- ), Q($foldername)) .
+ html::a($link_attrib, $html_name) .
(!empty($folder['folders']) ? html::div(array(
'class' => ($collapsed ? 'collapsed' : 'expanded'),
'style' => "position:absolute",
'onclick' => sprintf("%s.command('collapse-folder', '%s')", JS_OBJECT_NAME, $js_name)
), ' ') : ''));
- if (!empty($folder['folders']))
- $out .= "\n<ul" . ($collapsed ? " style=\"display: none;\"" : "") . ">\n" . rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $maxlength, $realnames, $nestLevel+1) . "</ul>\n";
+ $jslist[$folder_id] = array('id' => $folder['id'], 'name' => $foldername, 'virtual' => $folder['virtual']);
+
+ if (!empty($folder['folders'])) {
+ $out .= html::tag('ul', array('style' => ($collapsed ? "display:none;" : null)),
+ rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1));
+ }
$out .= "</li>\n";
$idx++;
- }
+ }
return $out;
- }
+}
/**
* Return html for a flat list <select> for the mailbox tree
* @access private
*/
function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0)
{
$idx = 0;
$out = '';
foreach ($arrFolders as $key=>$folder)
{
if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id'])))
$foldername = rcube_label($folder_class);
else
{
$foldername = $folder['name'];
// shorten the folder name to a given length
if ($maxlength && $maxlength>1)
$foldername = abbreviate_string($foldername, $maxlength);
}
$select->add(str_repeat(' ', $nestLevel*4) . $foldername, $folder['id']);
if (!empty($folder['folders']))
$out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $select, $realnames, $nestLevel+1);
$idx++;
}
return $out;
}
/**
* Return internal name for the given folder if it matches the configured special folders
* @access private
*/
function rcmail_folder_classname($folder_id)
{
global $CONFIG;
$cname = null;
$folder_lc = strtolower($folder_id);
// for these mailboxes we have localized labels and css classes
foreach (array('inbox', 'sent', 'drafts', 'trash', 'junk') as $smbx)
{
if ($folder_lc == $smbx || $folder_id == $CONFIG[$smbx.'_mbox'])
$cname = $smbx;
}
return $cname;
}
/**
* Try to localize the given IMAP folder name.
* UTF-7 decode it in case no localized text was found
*
* @param string Folder name
* @return string Localized folder name in UTF-8 encoding
*/
function rcmail_localize_foldername($name)
{
if ($folder_class = rcmail_folder_classname($name))
return rcube_label($folder_class);
else
return rcube_charset_convert($name, 'UTF-7');
}
?>
diff --git a/program/js/app.js b/program/js/app.js
index 3812a73de..36072e384 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -1,4084 +1,4092 @@
/*
+-----------------------------------------------------------------------+
| RoundCube Webmail Client Script |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005-2008, RoundCube Dev, - Switzerland |
| Licensed under the GNU GPL |
| |
+-----------------------------------------------------------------------+
| Authors: Thomas Bruederli <roundcube@gmail.com> |
| Charles McNulty <charles@charlesmcnulty.com> |
+-----------------------------------------------------------------------+
| Requires: common.js, list.js |
+-----------------------------------------------------------------------+
$Id$
*/
var rcube_webmail_client;
function rcube_webmail()
{
this.env = new Object();
this.labels = new Object();
this.buttons = new Object();
this.gui_objects = new Object();
this.commands = new Object();
this.onloads = new Array();
// create protected reference to myself
rcube_webmail_client = this;
this.ref = 'rcube_webmail_client';
var ref = this;
// webmail client settings
this.dblclick_time = 500;
this.message_time = 3000;
this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi');
// mimetypes supported by the browser (default settings)
this.mimetypes = new Array('text/plain', 'text/html', 'text/xml',
'image/jpeg', 'image/gif', 'image/png',
'application/x-javascript', 'application/pdf',
'application/x-shockwave-flash');
// default environment vars
this.env.keep_alive = 60; // seconds
this.env.request_timeout = 180; // seconds
this.env.draft_autosave = 0; // seconds
this.env.comm_path = './';
this.env.bin_path = './bin/';
this.env.blankpage = 'program/blank.gif';
// set environment variable(s)
this.set_env = function(p, value)
{
if (p != null && typeof(p) == 'object' && !value)
for (var n in p)
this.env[n] = p[n];
else
this.env[p] = value;
};
// add a localized label to the client environment
this.add_label = function(key, value)
{
this.labels[key] = value;
};
// add a button to the button list
this.register_button = function(command, id, type, act, sel, over)
{
if (!this.buttons[command])
this.buttons[command] = new Array();
var button_prop = {id:id, type:type};
if (act) button_prop.act = act;
if (sel) button_prop.sel = sel;
if (over) button_prop.over = over;
this.buttons[command][this.buttons[command].length] = button_prop;
};
// register a specific gui object
this.gui_object = function(name, id)
{
this.gui_objects[name] = id;
};
// execute the given script on load
this.add_onload = function(f)
{
this.onloads[this.onloads.length] = f;
};
// initialize webmail client
this.init = function()
{
var p = this;
this.task = this.env.task;
// check browser
if (!bw.dom || !bw.xmlhttp_test())
{
this.goto_url('error', '_code=0x199');
return;
}
// find all registered gui objects
for (var n in this.gui_objects)
this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
// tell parent window that this frame is loaded
if (this.env.framed && parent.rcmail && parent.rcmail.set_busy)
parent.rcmail.set_busy(false);
// enable general commands
this.enable_command('logout', 'mail', 'addressbook', 'settings', true);
switch (this.task)
{
case 'mail':
if (this.gui_objects.messagelist)
{
this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time});
this.message_list.row_init = function(o){ p.init_message_row(o); };
this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); });
this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); });
this.message_list.addEventListener('select', function(o){ p.msglist_select(o); });
this.message_list.addEventListener('dragstart', function(o){ p.drag_active = true; if (p.preview_timer) clearTimeout(p.preview_timer); });
+ this.message_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); });
this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; });
this.message_list.init();
this.enable_command('toggle_status', 'toggle_flag', true);
if (this.gui_objects.mailcontframe)
{
this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); };
document.onmouseup = function(e){ return p.doc_mouse_up(e); };
}
else
this.message_list.focus();
}
if (this.env.coltypes)
this.set_message_coltypes(this.env.coltypes);
// enable mail commands
this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
if (this.env.search_text != null && document.getElementById('quicksearchbox') != null)
document.getElementById('quicksearchbox').value = this.env.search_text;
if (this.env.action=='show' || this.env.action=='preview')
{
this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource', 'print', 'load-attachment', true);
if (this.env.next_uid)
{
this.enable_command('nextmessage', true);
this.enable_command('lastmessage', true);
}
if (this.env.prev_uid)
{
this.enable_command('previousmessage', true);
this.enable_command('firstmessage', true);
}
}
if (this.env.trash_mailbox && this.env.mailbox != this.env.trash_mailbox)
this.set_alttext('delete', 'movemessagetotrash');
// make preview/message frame visible
if (this.env.action == 'preview' && this.env.framed && parent.rcmail)
{
this.enable_command('compose', 'add-contact', false);
parent.rcmail.show_contentframe(true);
}
if ((this.env.action=='show' || this.env.action=='preview') && this.env.blockedobjects)
{
if (this.gui_objects.remoteobjectsmsg)
this.gui_objects.remoteobjectsmsg.style.display = 'block';
this.enable_command('load-images', 'always-load', true);
}
if (this.env.action=='compose')
{
this.enable_command('add-attachment', 'send-attachment', 'remove-attachment', 'send', true);
if (this.env.spellcheck)
{
this.env.spellcheck.spelling_state_observer = function(s){ ref.set_spellcheck_state(s); };
this.set_spellcheck_state('ready');
if (rcube_find_object('_is_html').value == '1')
this.display_spellcheck_controls(false);
}
if (this.env.drafts_mailbox)
this.enable_command('savedraft', true);
}
if (this.env.messagecount)
this.enable_command('select-all', 'select-none', 'expunge', true);
if (this.purge_mailbox_test())
this.enable_command('purge', true);
this.set_page_buttons();
// focus main window
if (this.env.framed && window.parent)
window.parent.focus();
else
window.focus();
// init message compose form
if (this.env.action=='compose')
this.init_messageform();
// show printing dialog
if (this.env.action=='print')
window.print();
// get unread count for each mailbox
if (this.gui_objects.mailboxlist)
{
this.env.unread_counts = {};
this.gui_objects.folderlist = this.gui_objects.mailboxlist;
this.http_request('getunread', '');
}
// ask user to send MDN
if (this.env.mdn_request && this.env.uid)
{
var mdnurl = '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox);
if (confirm(this.get_label('mdnrequest')))
this.http_post('sendmdn', mdnurl);
else
this.http_post('mark', mdnurl+'&_flag=mdnsent');
}
break;
case 'addressbook':
if (this.gui_objects.contactslist)
{
this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:true, keyboard:true});
this.contact_list.addEventListener('keypress', function(o){ p.contactlist_keypress(o); });
this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); });
this.contact_list.addEventListener('dragstart', function(o){ p.drag_active = true; });
+ this.contact_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); });
this.contact_list.addEventListener('dragend', function(o){ p.drag_active = false; });
this.contact_list.init();
if (this.env.cid)
this.contact_list.highlight_row(this.env.cid);
if (this.gui_objects.contactslist.parentNode)
{
this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
document.onmouseup = function(e){ return p.doc_mouse_up(e); };
}
else
this.contact_list.focus();
}
this.set_page_buttons();
if (this.env.address_sources && !this.env.address_sources[this.env.source].readonly)
this.enable_command('add', true);
if (this.env.cid)
this.enable_command('show', 'edit', true);
if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform)
this.enable_command('save', true);
else
this.enable_command('search', 'reset-search', 'moveto', 'import', true);
if (this.contact_list && this.contact_list.rowcount > 0)
this.enable_command('export', true);
this.enable_command('list', true);
break;
case 'settings':
this.enable_command('preferences', 'identities', 'save', 'folders', true);
if (this.env.action=='identities' || this.env.action=='edit-identity' || this.env.action=='add-identity')
this.enable_command('edit', 'add', 'delete', true);
if (this.env.action=='edit-identity' || this.env.action=='add-identity')
this.enable_command('save', true);
if (this.env.action=='folders')
this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true);
if (this.gui_objects.identitieslist)
{
this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist, {multiselect:false, draggable:false, keyboard:false});
this.identity_list.addEventListener('select', function(o){ p.identity_select(o); });
this.identity_list.init();
this.identity_list.focus();
if (this.env.iid)
this.identity_list.highlight_row(this.env.iid);
}
if (this.gui_objects.subscriptionlist)
this.init_subscription_list();
break;
case 'login':
var input_user = rcube_find_object('rcmloginuser');
var input_pass = rcube_find_object('rcmloginpwd');
var input_tz = rcube_find_object('rcmlogintz');
if (input_user)
input_user.onkeyup = function(e){ return rcmail.login_user_keyup(e); };
if (input_user && input_user.value=='')
input_user.focus();
else if (input_pass)
input_pass.focus();
// detect client timezone
if (input_tz)
input_tz.value = new Date().getTimezoneOffset() / -60;
this.enable_command('login', true);
break;
default:
break;
}
// enable basic commands
this.enable_command('logout', true);
// flag object as complete
this.loaded = true;
// show message
if (this.pending_message)
this.display_message(this.pending_message[0], this.pending_message[1]);
// start keep-alive interval
this.start_keepalive();
// execute all foreign onload scripts
for (var i=0; i<this.onloads.length; i++)
{
if (typeof(this.onloads[i]) == 'string')
eval(this.onloads[i]);
else if (typeof(this.onloads[i]) == 'function')
this.onloads[i]();
}
};
// start interval for keep-alive/recent_check signal
this.start_keepalive = function()
{
if (this.env.keep_alive && !this.env.framed && this.task=='mail' && this.gui_objects.mailboxlist)
this._int = setInterval(function(){ ref.check_for_recent(); }, this.env.keep_alive * 1000);
else if (this.env.keep_alive && !this.env.framed && this.task!='login')
this._int = setInterval(function(){ ref.send_keep_alive(); }, this.env.keep_alive * 1000);
}
this.init_message_row = function(row)
{
var uid = row.uid;
if (uid && this.env.messages[uid])
{
row.deleted = this.env.messages[uid].deleted ? true : false;
row.unread = this.env.messages[uid].unread ? true : false;
row.replied = this.env.messages[uid].replied ? true : false;
row.flagged = this.env.messages[uid].flagged ? true : false;
}
// set eventhandler to message icon
if ((row.icon = row.obj.cells[0].childNodes[0]) && row.icon.nodeName=='IMG')
{
var p = this;
row.icon.id = 'msgicn_'+row.uid;
row.icon._row = row.obj;
row.icon.onmousedown = function(e) { p.command('toggle_status', this); };
}
// global variable 'flagged_col' may be not defined yet
if (!this.env.flagged_col && this.env.coltypes)
{
var found;
if((found = find_in_array('flag', this.env.coltypes)) >= 0)
this.set_env('flagged_col', found+1);
}
// set eventhandler to flag icon, if icon found
if (this.env.flagged_col && (row.flagged_icon = row.obj.cells[this.env.flagged_col].childNodes[0])
&& row.flagged_icon.nodeName=='IMG')
{
var p = this;
row.flagged_icon.id = 'flaggedicn_'+row.uid;
row.flagged_icon._row = row.obj;
row.flagged_icon.onmousedown = function(e) { p.command('toggle_flag', this); };
}
};
// init message compose form: set focus and eventhandlers
this.init_messageform = function()
{
if (!this.gui_objects.messageform)
return false;
//this.messageform = this.gui_objects.messageform;
var input_from = rcube_find_object('_from');
var input_to = rcube_find_object('_to');
var input_cc = rcube_find_object('_cc');
var input_bcc = rcube_find_object('_bcc');
var input_replyto = rcube_find_object('_replyto');
var input_subject = rcube_find_object('_subject');
var input_message = rcube_find_object('_message');
// init live search events
if (input_to)
this.init_address_input_events(input_to);
if (input_cc)
this.init_address_input_events(input_cc);
if (input_bcc)
this.init_address_input_events(input_bcc);
// add signature according to selected identity
if (input_from && input_from.type=='select-one')
this.change_identity(input_from);
if (input_to && input_to.value=='')
input_to.focus();
else if (input_subject && input_subject.value=='')
input_subject.focus();
else if (input_message)
this.set_caret2start(input_message);
// get summary of all field values
this.compose_field_hash(true);
// start the auto-save timer
this.auto_save_start();
};
this.init_address_input_events = function(obj)
{
var handler = function(e){ return ref.ksearch_keypress(e,this); };
var handler2 = function(e){ return ref.ksearch_blur(e,this); };
if (obj.addEventListener)
{
obj.addEventListener(bw.safari ? 'keydown' : 'keypress', handler, false);
obj.addEventListener('blur', handler2, false);
}
else
{
obj.onkeydown = handler;
obj.onblur = handler2;
}
obj.setAttribute('autocomplete', 'off');
};
/*********************************************************/
/********* client command interface *********/
/*********************************************************/
// execute a specific command on the web client
this.command = function(command, props, obj)
{
if (obj && obj.blur)
obj.blur();
if (this.busy)
return false;
// command not supported or allowed
if (!this.commands[command])
{
// pass command to parent window
if (this.env.framed && parent.rcmail && parent.rcmail.command)
parent.rcmail.command(command, props);
return false;
}
// check input before leaving compose step
if (this.task=='mail' && this.env.action=='compose' && (command=='list' || command=='mail' || command=='addressbook' || command=='settings'))
{
if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
return false;
}
// process command
switch (command)
{
case 'login':
if (this.gui_objects.loginform)
this.gui_objects.loginform.submit();
break;
case 'logout':
this.goto_url('logout', '', true);
break;
// commands to switch task
case 'mail':
case 'addressbook':
case 'settings':
this.switch_task(command);
break;
// misc list commands
case 'list':
if (this.task=='mail')
{
if (this.env.search_request<0 || (props != '' && (this.env.search_request && props != this.env.mailbox)))
this.reset_qsearch();
this.list_mailbox(props);
if (this.env.trash_mailbox)
this.set_alttext('delete', this.env.mailbox != this.env.trash_mailbox ? 'movemessagetotrash' : 'deletemessage');
}
else if (this.task=='addressbook')
{
if (this.env.search_request<0 || (this.env.search_request && props != this.env.source))
this.reset_qsearch();
this.list_contacts(props);
this.enable_command('add', (this.env.address_sources && !this.env.address_sources[props].readonly));
}
break;
case 'sort':
// get the type of sorting
var a_sort = props.split('_');
var sort_col = a_sort[0];
var sort_order = a_sort[1] ? a_sort[1].toUpperCase() : null;
var header;
// no sort order specified: toggle
if (sort_order==null)
{
if (this.env.sort_col==sort_col)
sort_order = this.env.sort_order=='ASC' ? 'DESC' : 'ASC';
else
sort_order = this.env.sort_order;
}
if (this.env.sort_col==sort_col && this.env.sort_order==sort_order)
break;
// set table header class
if (header = document.getElementById('rcmHead'+this.env.sort_col))
this.set_classname(header, 'sorted'+(this.env.sort_order.toUpperCase()), false);
if (header = document.getElementById('rcmHead'+sort_col))
this.set_classname(header, 'sorted'+sort_order, true);
// save new sort properties
this.env.sort_col = sort_col;
this.env.sort_order = sort_order;
// reload message list
this.list_mailbox('', '', sort_col+'_'+sort_order);
break;
case 'nextpage':
this.list_page('next');
break;
case 'lastpage':
this.list_page('last');
break;
case 'previouspage':
this.list_page('prev');
break;
case 'firstpage':
this.list_page('first');
break;
case 'expunge':
if (this.env.messagecount)
this.expunge_mailbox(this.env.mailbox);
break;
case 'purge':
case 'empty-mailbox':
if (this.env.messagecount)
this.purge_mailbox(this.env.mailbox);
break;
// common commands used in multiple tasks
case 'show':
if (this.task=='mail')
{
var uid = this.get_single_uid();
if (uid && (!this.env.uid || uid != this.env.uid))
{
if (this.env.mailbox == this.env.drafts_mailbox)
this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
else
this.show_message(uid);
}
}
else if (this.task=='addressbook')
{
var cid = props ? props : this.get_single_cid();
if (cid && !(this.env.action=='show' && cid==this.env.cid))
this.load_contact(cid, 'show');
}
break;
case 'add':
if (this.task=='addressbook')
this.load_contact(0, 'add');
else if (this.task=='settings')
{
this.identity_list.clear_selection();
this.load_identity(0, 'add-identity');
}
break;
case 'edit':
var cid;
if (this.task=='addressbook' && (cid = this.get_single_cid()))
this.load_contact(cid, 'edit');
else if (this.task=='settings' && props)
this.load_identity(props, 'edit-identity');
break;
case 'save-identity':
case 'save':
if (this.gui_objects.editform)
{
var input_pagesize = rcube_find_object('_pagesize');
var input_name = rcube_find_object('_name');
var input_email = rcube_find_object('_email');
// user prefs
if (input_pagesize && isNaN(parseInt(input_pagesize.value)))
{
alert(this.get_label('nopagesizewarning'));
input_pagesize.focus();
break;
}
// contacts/identities
else
{
if (input_name && input_name.value == '')
{
alert(this.get_label('nonamewarning'));
input_name.focus();
break;
}
else if (input_email && !rcube_check_email(input_email.value))
{
alert(this.get_label('noemailwarning'));
input_email.focus();
break;
}
}
this.gui_objects.editform.submit();
}
break;
case 'delete':
// mail task
if (this.task=='mail')
this.delete_messages();
// addressbook task
else if (this.task=='addressbook')
this.delete_contacts();
// user settings task
else if (this.task=='settings')
this.delete_identity();
break;
// mail task commands
case 'move':
case 'moveto':
if (this.task == 'mail')
this.move_messages(props);
else if (this.task == 'addressbook' && this.drag_active)
this.copy_contact(null, props);
break;
case 'mark':
if (props)
this.mark_message(props);
break;
case 'toggle_status':
if (props && !props._row)
break;
var uid;
var flag = 'read';
if (props._row.uid)
{
uid = props._row.uid;
// toggle read/unread
if (this.message_list.rows[uid].deleted) {
flag = 'undelete';
} else if (!this.message_list.rows[uid].unread)
flag = 'unread';
}
this.mark_message(flag, uid);
break;
case 'toggle_flag':
if (props && !props._row)
break;
var uid;
var flag = 'flagged';
if (props._row.uid)
{
uid = props._row.uid;
this.message_list.dont_select = true;
// toggle flagged/unflagged
if (this.message_list.rows[uid].flagged)
flag = 'unflagged';
}
this.mark_message(flag, uid);
break;
case 'always-load':
if (this.env.uid && this.env.sender) {
this.add_contact(urlencode(this.env.sender));
window.setTimeout(function(){ ref.command('load-images'); }, 300);
break;
}
case 'load-images':
if (this.env.uid)
this.show_message(this.env.uid, true, this.env.action=='preview');
break;
case 'load-attachment':
var qstring = '_mbox='+urlencode(this.env.mailbox)+'&_uid='+this.env.uid+'&_part='+props.part;
// open attachment in frame if it's of a supported mimetype
if (this.env.uid && props.mimetype && find_in_array(props.mimetype, this.mimetypes)>=0)
{
if (props.mimetype == 'text/html')
qstring += '&_safe=1';
this.attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment');
if (this.attachment_win)
{
window.setTimeout(function(){ ref.attachment_win.focus(); }, 10);
break;
}
}
this.goto_url('get', qstring+'&_download=1', false);
break;
case 'select-all':
this.message_list.select_all(props);
break;
case 'select-none':
this.message_list.clear_selection();
break;
case 'nextmessage':
if (this.env.next_uid)
this.show_message(this.env.next_uid, false, this.env.action=='preview');
break;
case 'lastmessage':
if (this.env.last_uid)
this.show_message(this.env.last_uid);
break;
case 'previousmessage':
if (this.env.prev_uid)
this.show_message(this.env.prev_uid, false, this.env.action=='preview');
break;
case 'firstmessage':
if (this.env.first_uid)
this.show_message(this.env.first_uid);
break;
case 'checkmail':
this.check_for_recent();
break;
case 'compose':
var url = this.env.comm_path+'&_action=compose';
if (this.task=='mail' && this.env.mailbox==this.env.drafts_mailbox)
{
var uid;
if (uid = this.get_single_uid())
url += '&_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox);
}
// modify url if we're in addressbook
else if (this.task=='addressbook')
{
// switch to mail compose step directly
if (props && props.indexOf('@') > 0)
{
url = this.get_task_url('mail', url);
this.redirect(url + '&_to='+urlencode(props));
break;
}
// use contact_id passed as command parameter
var a_cids = new Array();
if (props)
a_cids[a_cids.length] = props;
// get selected contacts
else if (this.contact_list)
{
var selection = this.contact_list.get_selection();
for (var n=0; n<selection.length; n++)
a_cids[a_cids.length] = selection[n];
}
if (a_cids.length)
this.http_request('mailto', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source), true);
break;
}
else if (props)
url += '&_to='+urlencode(props);
// don't know if this is necessary...
url = url.replace(/&_framed=1/, "");
this.redirect(url);
break;
case 'spellcheck':
if (window.tinyMCE && tinyMCE.get('compose-body')) {
tinyMCE.execCommand('mceSpellCheck', true);
}
else if (this.env.spellcheck && this.env.spellcheck.spellCheck && this.spellcheck_ready) {
this.env.spellcheck.spellCheck(this.env.spellcheck.check_link);
this.set_spellcheck_state('checking');
}
break;
case 'savedraft':
// Reset the auto-save timer
self.clearTimeout(this.save_timer);
if (!this.gui_objects.messageform)
break;
// if saving Drafts is disabled in main.inc.php
// or if compose form did not change
if (!this.env.drafts_mailbox || this.cmp_hash == this.compose_field_hash())
break;
this.set_busy(true, 'savingmessage');
var form = this.gui_objects.messageform;
form.target = "savetarget";
form._draft.value = '1';
form.submit();
break;
case 'send':
if (!this.gui_objects.messageform)
break;
if (!this.check_compose_input())
break;
// Reset the auto-save timer
self.clearTimeout(this.save_timer);
// all checks passed, send message
this.set_busy(true, 'sendingmessage');
var form = this.gui_objects.messageform;
form.target = "savetarget";
form._draft.value = '';
form.submit();
// clear timeout (sending could take longer)
clearTimeout(this.request_timer);
break;
case 'add-attachment':
this.show_attachment_form(true);
case 'send-attachment':
// Reset the auto-save timer
self.clearTimeout(this.save_timer);
this.upload_file(props)
break;
case 'remove-attachment':
this.remove_attachment(props);
break;
case 'reply-all':
case 'reply':
var uid;
if (uid = this.get_single_uid())
this.goto_url('compose', '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(command=='reply-all' ? '&_all=1' : ''), true);
break;
case 'forward':
var uid;
if (uid = this.get_single_uid())
this.goto_url('compose', '_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
break;
case 'print':
var uid;
if (uid = this.get_single_uid())
{
ref.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''));
if (this.printwin)
{
window.setTimeout(function(){ ref.printwin.focus(); }, 20);
if (this.env.action != 'show')
this.mark_message('read', uid);
}
}
break;
case 'viewsource':
var uid;
if (uid = this.get_single_uid())
{
ref.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox));
if (this.sourcewin)
window.setTimeout(function(){ ref.sourcewin.focus(); }, 20);
}
break;
case 'add-contact':
this.add_contact(props);
break;
// quicksearch
case 'search':
if (!props && this.gui_objects.qsearchbox)
props = this.gui_objects.qsearchbox.value;
if (props)
{
this.qsearch(props);
break;
}
// reset quicksearch
case 'reset-search':
var s = this.env.search_request;
this.reset_qsearch();
if (s && this.env.mailbox)
this.list_mailbox(this.env.mailbox);
else if (s && this.task == 'addressbook')
this.list_contacts(this.env.source);
break;
case 'import':
if (this.env.action == 'import' && this.gui_objects.importform) {
var file = document.getElementById('rcmimportfile');
if (file && !file.value) {
alert(this.get_label('selectimportfile'));
break;
}
this.gui_objects.importform.submit();
this.set_busy(true, 'importwait');
this.lock_form(this.gui_objects.importform, true);
}
else
this.goto_url('import');
break;
case 'export':
if (this.contact_list.rowcount > 0) {
var add_url = (this.env.source ? '_source='+urlencode(this.env.source)+'&' : '');
if (this.env.search_request)
add_url += '_search='+this.env.search_request;
this.goto_url('export', add_url);
}
break;
// collapse/expand folder
case 'collapse-folder':
if (props)
this.collapse_folder(props);
break;
// user settings commands
case 'preferences':
this.goto_url('');
break;
case 'identities':
this.goto_url('identities');
break;
case 'delete-identity':
this.delete_identity();
case 'folders':
this.goto_url('folders');
break;
case 'subscribe':
this.subscribe_folder(props);
break;
case 'unsubscribe':
this.unsubscribe_folder(props);
break;
case 'create-folder':
this.create_folder(props);
break;
case 'rename-folder':
this.rename_folder(props);
break;
case 'delete-folder':
this.delete_folder(props);
break;
}
return obj ? false : true;
};
// set command enabled or disabled
this.enable_command = function()
{
var args = arguments;
if(!args.length) return -1;
var command;
var enable = args[args.length-1];
for(var n=0; n<args.length-1; n++)
{
command = args[n];
this.commands[command] = enable;
this.set_button(command, (enable ? 'act' : 'pas'));
}
return true;
};
// lock/unlock interface
this.set_busy = function(a, message)
{
if (a && message)
{
var msg = this.get_label(message);
if (msg==message)
msg = 'Loading...';
this.display_message(msg, 'loading', true);
}
else if (!a)
this.hide_message();
this.busy = a;
//document.body.style.cursor = a ? 'wait' : 'default';
if (this.gui_objects.editform)
this.lock_form(this.gui_objects.editform, a);
// clear pending timer
if (this.request_timer)
clearTimeout(this.request_timer);
// set timer for requests
if (a && this.env.request_timeout)
this.request_timer = window.setTimeout(function(){ ref.request_timed_out(); }, this.env.request_timeout * 1000);
};
// return a localized string
this.get_label = function(name)
{
if (this.labels[name])
return this.labels[name];
else
return name;
};
// switch to another application task
this.switch_task = function(task)
{
if (this.task===task && task!='mail')
return;
var url = this.get_task_url(task);
if (task=='mail')
url += '&_mbox=INBOX';
this.redirect(url);
};
this.get_task_url = function(task, url)
{
if (!url)
url = this.env.comm_path;
return url.replace(/_task=[a-z]+/, '_task='+task);
};
// called when a request timed out
this.request_timed_out = function()
{
this.set_busy(false);
this.display_message('Request timed out!', 'error');
};
/*********************************************************/
/********* event handling methods *********/
/*********************************************************/
this.doc_mouse_up = function(e)
- {
- if (this.message_list)
+ {
+ var model, li;
+
+ if (this.message_list) {
this.message_list.blur();
- else if (this.contact_list)
+ model = this.env.mailboxes;
+ }
+ else if (this.contact_list) {
this.contact_list.blur();
- };
-
- this.focus_folder = function(id)
- {
- var li;
- if (this.drag_active && this.check_droptarget(id) && (li = this.get_folder_li(id)))
- this.set_classname(li, 'droptarget', true);
+ model = this.env.address_sources;
+ }
+
+ // handle mouse release when dragging
+ if (this.drag_active && model) {
+ for (var k in model) {
+ if ((li = this.get_folder_li(k)) && rcube_mouse_is_over(e, li.firstChild) && this.check_droptarget(k)) {
+ this.set_classname(li, 'droptarget', false);
+ this.command('moveto', model[k].id);
+ break;
+ }
+ }
}
+ };
- this.unfocus_folder = function(id)
- {
+ this.drag_move = function(e)
+ {
var li;
- if (this.drag_active && (li = this.get_folder_li(id)))
- this.set_classname(li, 'droptarget', false);
+ var model = this.task == 'mail' ? this.env.mailboxes : this.env.address_sources;
+
+ if (this.gui_objects.folderlist && model) {
+ for (var k in model) {
+ if (li = this.get_folder_li(k))
+ this.set_classname(li, 'droptarget', (rcube_mouse_is_over(e, li.firstChild) && this.check_droptarget(k)));
+ }
}
-
+ };
+
this.collapse_folder = function(id)
{
var div;
if ((li = this.get_folder_li(id)) &&
(div = li.getElementsByTagName("div")[0]) &&
(div.className.match(/collapsed/) || div.className.match(/expanded/)))
{
var ul = li.getElementsByTagName("ul")[0];
if (div.className.match(/collapsed/))
{
ul.style.display = '';
this.set_classname(div, 'collapsed', false);
this.set_classname(div, 'expanded', true);
var reg = new RegExp('&'+escape(id)+'&');
this.set_env('collapsed_folders', this.env.collapsed_folders.replace(reg, ''));
}
else
{
ul.style.display = 'none';
this.set_classname(div, 'expanded', false);
this.set_classname(div, 'collapsed', true);
this.set_env('collapsed_folders', this.env.collapsed_folders+'&'+escape(id)+'&');
// select parent folder if one of its childs is currently selected
if (this.env.mailbox.indexOf(id + this.env.delimiter) == 0)
this.command('list', id);
}
this.http_post('save-pref', '_name=collapsed_folders&_value='+escape(this.env.collapsed_folders));
this.set_unread_count_display(id, false);
}
}
- // onmouseup handler for folder list item
- this.folder_mouse_up = function(id)
- {
- if (this.drag_active)
- {
- this.unfocus_folder(id);
- this.command('moveto', id);
- }
- };
-
this.click_on_list = function(e)
{
if (this.message_list)
this.message_list.focus();
else if (this.contact_list)
this.contact_list.focus();
var mbox_li;
if (mbox_li = this.get_folder_li())
this.set_classname(mbox_li, 'unfocused', true);
- rcube_event.cancel(e);
+ return rcube_event.get_button(e) == 2 ? true : rcube_event.cancel(e);
};
this.msglist_select = function(list)
{
if (this.preview_timer)
clearTimeout(this.preview_timer);
var selected = list.selection.length==1;
// Hide certain command buttons when Drafts folder is selected
if (this.env.mailbox == this.env.drafts_mailbox)
{
this.enable_command('reply', 'reply-all', 'forward', false);
this.enable_command('show', selected);
this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
}
else
{
this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected);
this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
}
// start timer for message preview (wait for double click)
if (selected && this.env.contentframe && !list.multi_selecting)
this.preview_timer = window.setTimeout(function(){ ref.msglist_get_preview(); }, 200);
else if (this.env.contentframe)
this.show_contentframe(false);
};
this.msglist_dbl_click = function(list)
{
if (this.preview_timer)
clearTimeout(this.preview_timer);
var uid = list.get_single_selection();
if (uid && this.env.mailbox == this.env.drafts_mailbox)
this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
else if (uid)
this.show_message(uid, false, false);
};
this.msglist_keypress = function(list)
{
if (list.key_pressed == list.ENTER_KEY)
this.command('show');
else if (list.key_pressed == list.DELETE_KEY)
this.command('delete');
else if (list.key_pressed == list.BACKSPACE_KEY)
this.command('delete');
else
list.shiftkey = false;
};
this.msglist_get_preview = function()
{
var uid = this.get_single_uid();
if (uid && this.env.contentframe && !this.drag_active)
this.show_message(uid, false, true);
else if (this.env.contentframe)
this.show_contentframe(false);
};
this.check_droptarget = function(id)
{
if (this.task == 'mail')
- return (id != this.env.mailbox);
+ return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual);
else if (this.task == 'addressbook')
return (id != this.env.source && this.env.address_sources[id] && !this.env.address_sources[id].readonly);
else if (this.task == 'settings')
return (id != this.env.folder);
};
/*********************************************************/
/********* (message) list functionality *********/
/*********************************************************/
// when user doble-clicks on a row
this.show_message = function(id, safe, preview)
{
var add_url = '';
var action = preview ? 'preview': 'show';
var target = window;
if (preview && this.env.contentframe && window.frames && window.frames[this.env.contentframe])
{
target = window.frames[this.env.contentframe];
add_url = '&_framed=1';
}
if (safe)
add_url = '&_safe=1';
// also send search request to get the right messages
if (this.env.search_request)
add_url += '&_search='+this.env.search_request;
if (id)
{
var url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox)+add_url;
if (action == 'preview' && String(target.location.href).indexOf(url) >= 0)
this.show_contentframe(true);
else
{
this.set_busy(true, 'loading');
target.location.href = this.env.comm_path+url;
}
}
};
this.show_contentframe = function(show)
{
var frm;
if (this.env.contentframe && (frm = rcube_find_object(this.env.contentframe)))
{
if (!show && window.frames[this.env.contentframe])
{
if (window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)<0)
window.frames[this.env.contentframe].location.href = this.env.blankpage;
}
else if (!bw.safari)
frm.style.display = show ? 'block' : 'none';
}
if (!show && this.busy)
this.set_busy(false);
};
// list a specific page
this.list_page = function(page)
{
if (page=='next')
page = this.env.current_page+1;
if (page=='last')
page = this.env.pagecount;
if (page=='prev' && this.env.current_page>1)
page = this.env.current_page-1;
if (page=='first' && this.env.current_page>1)
page = 1;
if (page > 0 && page <= this.env.pagecount)
{
this.env.current_page = page;
if (this.task=='mail')
this.list_mailbox(this.env.mailbox, page);
else if (this.task=='addressbook')
this.list_contacts(this.env.source, page);
}
};
// list messages of a specific mailbox
this.list_mailbox = function(mbox, page, sort)
{
this.last_selected = 0;
var add_url = '';
var target = window;
if (!mbox)
mbox = this.env.mailbox;
// add sort to url if set
if (sort)
add_url += '&_sort=' + sort;
// also send search request to get the right messages
if (this.env.search_request)
add_url += '&_search='+this.env.search_request;
// set page=1 if changeing to another mailbox
if (!page && mbox != this.env.mailbox)
{
page = 1;
this.env.current_page = page;
if (this.message_list)
this.message_list.clear_selection();
this.show_contentframe(false);
}
if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort))
add_url += '&_refresh=1';
this.select_folder(mbox, this.env.mailbox);
this.env.mailbox = mbox;
// load message list remotely
if (this.gui_objects.messagelist)
{
this.list_mailbox_remote(mbox, page, add_url);
return;
}
if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
{
target = window.frames[this.env.contentframe];
add_url += '&_framed=1';
}
// load message list to target frame/window
if (mbox)
{
this.set_busy(true, 'loading');
target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url;
}
};
// send remote request to load message list
this.list_mailbox_remote = function(mbox, page, add_url)
{
// clear message list first
this.message_list.clear();
// send request to server
var url = '_mbox='+urlencode(mbox)+(page ? '&_page='+page : '');
this.set_busy(true, 'loading');
this.http_request('list', url+add_url, true);
};
this.expunge_mailbox = function(mbox)
{
var lock = false;
var add_url = '';
// lock interface if it's the active mailbox
if (mbox == this.env.mailbox)
{
lock = true;
this.set_busy(true, 'loading');
add_url = '&_reload=1';
}
// send request to server
var url = '_mbox='+urlencode(mbox);
this.http_post('expunge', url+add_url, lock);
};
this.purge_mailbox = function(mbox)
{
var lock = false;
var add_url = '';
if (!confirm(this.get_label('purgefolderconfirm')))
return false;
// lock interface if it's the active mailbox
if (mbox == this.env.mailbox)
{
lock = true;
this.set_busy(true, 'loading');
add_url = '&_reload=1';
}
// send request to server
var url = '_mbox='+urlencode(mbox);
this.http_post('purge', url+add_url, lock);
return true;
};
// test if purge command is allowed
this.purge_mailbox_test = function()
{
return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox
|| this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter))
|| this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter))));
};
// move selected messages to the specified mailbox
this.move_messages = function(mbox)
{
// exit if current or no mailbox specified or if selection is empty
if (!mbox || mbox == this.env.mailbox || (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length)))
return;
var lock = false;
var add_url = '&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : '');
// show wait message
if (this.env.action=='show')
{
lock = true;
this.set_busy(true, 'movingmessage');
}
else if (!this.env.flag_for_deletion)
this.show_contentframe(false);
// Hide message command buttons until a message is selected
this.enable_command('reply', 'reply-all', 'forward', 'delete', 'mark', 'print', false);
this._with_selected_messages('moveto', lock, add_url, (this.env.flag_for_deletion ? false : true));
};
// delete selected messages from the current mailbox
this.delete_messages = function()
{
var selection = this.message_list ? this.message_list.get_selection() : new Array();
// exit if no mailbox specified or if selection is empty
if (!this.env.uid && !selection.length)
return;
// if there is a trash mailbox defined and we're not currently in it:
if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() != String(this.env.trash_mailbox).toLowerCase())
{
// if shift was pressed delete it immediately
if (this.message_list && this.message_list.shiftkey)
{
if (confirm(this.get_label('deletemessagesconfirm')))
this.permanently_remove_messages();
}
else
this.move_messages(this.env.trash_mailbox);
}
// if there is a trash mailbox defined but we *are* in it:
else if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() == String(this.env.trash_mailbox).toLowerCase())
this.permanently_remove_messages();
// if there isn't a defined trash mailbox and the config is set to flag for deletion
else if (!this.env.trash_mailbox && this.env.flag_for_deletion)
{
this.mark_message('delete');
if(this.env.action=="show")
this.command('nextmessage','',this);
else if (selection.length == 1)
this.message_list.select_next();
}
// if there isn't a defined trash mailbox and the config is set NOT to flag for deletion
else if (!this.env.trash_mailbox)
this.permanently_remove_messages();
};
// delete the selected messages permanently
this.permanently_remove_messages = function()
{
// exit if no mailbox specified or if selection is empty
if (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length))
return;
this.show_contentframe(false);
this._with_selected_messages('delete', false, '&_from='+(this.env.action ? this.env.action : ''), true);
};
// Send a specifc request with UIDs of all selected messages
// @private
this._with_selected_messages = function(action, lock, add_url, remove)
{
var a_uids = new Array();
if (this.env.uid)
a_uids[0] = this.env.uid;
else
{
var selection = this.message_list.get_selection();
var rows = this.message_list.rows;
var id;
for (var n=0; n<selection.length; n++)
{
id = selection[n];
a_uids[a_uids.length] = id;
if (remove)
this.message_list.remove_row(id, (n == selection.length-1));
else
{
rows[id].deleted = true;
if (rows[id].classname.indexOf('deleted')<0)
{
rows[id].classname += ' deleted';
this.set_classname(rows[id].obj, 'deleted', true);
}
if (this.env.read_when_deleted)
{
rows[id].classname = rows[id].classname.replace(/\s*unread/, '');
this.set_classname(rows[id].obj, 'unread', false);
}
if (rows[id].icon && this.env.deletedicon)
rows[id].icon.src = this.env.deletedicon;
}
}
}
// also send search request to get the right messages
if (this.env.search_request)
add_url += '&_search='+this.env.search_request;
// send request to server
this.http_post(action, '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+add_url, lock);
};
// set a specific flag to one or more messages
this.mark_message = function(flag, uid)
{
var a_uids = new Array();
var r_uids = new Array();
var selection = this.message_list ? this.message_list.get_selection() : new Array();
if (uid)
a_uids[0] = uid;
else if (this.env.uid)
a_uids[0] = this.env.uid;
else if (this.message_list)
{
for (var n=0; n<selection.length; n++)
{
a_uids[a_uids.length] = selection[n];
}
}
if (!this.message_list)
r_uids = a_uids;
else
for (var id, n=0; n<a_uids.length; n++)
{
id = a_uids[n];
if ((flag=='read' && this.message_list.rows[id].unread)
|| (flag=='unread' && !this.message_list.rows[id].unread)
|| (flag=='delete' && !this.message_list.rows[id].deleted)
|| (flag=='undelete' && this.message_list.rows[id].deleted)
|| (flag=='flagged' && !this.message_list.rows[id].flagged)
|| (flag=='unflagged' && this.message_list.rows[id].flagged))
{
r_uids[r_uids.length] = id;
}
}
// nothing to do
if (!r_uids.length)
return;
switch (flag)
{
case 'read':
case 'unread':
this.toggle_read_status(flag, r_uids);
break;
case 'delete':
case 'undelete':
this.toggle_delete_status(r_uids);
break;
case 'flagged':
case 'unflagged':
this.toggle_flagged_status(flag, a_uids);
break;
}
};
// set class to read/unread
this.toggle_read_status = function(flag, a_uids)
{
// mark all message rows as read/unread
var icn_src;
var rows = this.message_list.rows;
for (var i=0; i<a_uids.length; i++)
{
uid = a_uids[i];
if (rows[uid])
{
rows[uid].unread = (flag=='unread' ? true : false);
if (rows[uid].classname.indexOf('unread')<0 && rows[uid].unread)
{
rows[uid].classname += ' unread';
this.set_classname(rows[uid].obj, 'unread', true);
if (this.env.unreadicon)
icn_src = this.env.unreadicon;
}
else if (!rows[uid].unread)
{
rows[uid].classname = rows[uid].classname.replace(/\s*unread/, '');
this.set_classname(rows[uid].obj, 'unread', false);
if (this.env.messageicon)
icn_src = this.env.messageicon;
}
if (rows[uid].icon && icn_src
&& !(rows[uid].replied && this.env.repliedicon)
&& !(rows[uid].deleted && this.env.deletedicon))
rows[uid].icon.src = icn_src;
}
}
this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
};
// set class to read/unread
this.mark_as_read_from_preview = function(uid)
{
var icn_src;
var rows = parent.rcmail.message_list.rows;
if(rows[uid].unread)
{
rows[uid].unread = false;
rows[uid].classname = rows[uid].classname.replace(/\s*unread/, '');
parent.rcmail.set_classname(rows[uid].obj, 'unread', false);
if (rows[uid].replied && parent.rcmail.env.repliedicon)
icn_src = parent.rcmail.env.repliedicon;
else if (rows[uid].deleted && parent.rcmail.env.deletedicon)
icn_src = parent.rcmail.env.deletedicon;
else if (parent.rcmail.env.messageicon)
icn_src = parent.rcmail.env.messageicon;
if (rows[uid].icon && icn_src)
rows[uid].icon.src = icn_src;
}
}
// set image to flagged or unflagged
this.toggle_flagged_status = function(flag, a_uids)
{
// mark all message rows as flagged/unflagged
var icn_src;
var rows = this.message_list.rows;
for (var i=0; i<a_uids.length; i++)
{
uid = a_uids[i];
if (rows[uid])
{
rows[uid].flagged = (flag=='flagged' ? true : false);
if (rows[uid].flagged && this.env.flaggedicon)
icn_src = this.env.flaggedicon;
else if (this.env.unflaggedicon)
icn_src = this.env.unflaggedicon;
if (rows[uid].flagged_icon && icn_src)
rows[uid].flagged_icon.src = icn_src;
}
}
this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag);
};
// mark all message rows as deleted/undeleted
this.toggle_delete_status = function(a_uids)
{
var rows = this.message_list ? this.message_list.rows : new Array();
if (a_uids.length==1)
{
if (!rows.length || (rows[a_uids[0]] && rows[a_uids[0]].classname.indexOf('deleted') < 0))
this.flag_as_deleted(a_uids);
else
this.flag_as_undeleted(a_uids);
return true;
}
var all_deleted = true;
for (var i=0; i<a_uids.length; i++)
{
uid = a_uids[i];
if (rows[uid]) {
if (rows[uid].classname.indexOf('deleted')<0)
{
all_deleted = false;
break;
}
}
}
if (all_deleted)
this.flag_as_undeleted(a_uids);
else
this.flag_as_deleted(a_uids);
return true;
};
this.flag_as_undeleted = function(a_uids)
{
var icn_src;
var rows = this.message_list ? this.message_list.rows : new Array();
for (var i=0; i<a_uids.length; i++)
{
uid = a_uids[i];
if (rows[uid]) {
rows[uid].deleted = false;
if (rows[uid].classname.indexOf('deleted') > 0)
{
rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, '');
this.set_classname(rows[uid].obj, 'deleted', false);
}
if (rows[uid].unread && this.env.unreadicon)
icn_src = this.env.unreadicon;
else if (rows[uid].replied && this.env.repliedicon)
icn_src = this.env.repliedicon;
else if (this.env.messageicon)
icn_src = this.env.messageicon;
if (rows[uid].icon && icn_src)
rows[uid].icon.src = icn_src;
}
}
this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=undelete');
return true;
};
this.flag_as_deleted = function(a_uids)
{
var add_url = '';
var r_uids = new Array();
var rows = this.message_list ? this.message_list.rows : new Array();
for (var i=0; i<a_uids.length; i++)
{
uid = a_uids[i];
if (rows[uid])
{
rows[uid].deleted = true;
if (rows[uid].classname.indexOf('deleted')<0)
{
rows[uid].classname += ' deleted';
this.set_classname(rows[uid].obj, 'deleted', true);
}
if (this.env.read_when_deleted)
{
rows[uid].classname = rows[uid].classname.replace(/\s*unread/, '');
this.set_classname(rows[uid].obj, 'unread', false);
}
if (rows[uid].icon && this.env.deletedicon)
rows[uid].icon.src = this.env.deletedicon;
if (rows[uid].unread)
r_uids[r_uids.length] = uid;
}
}
if (r_uids.length)
add_url = '&_ruid='+r_uids.join(',');
this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=delete'+add_url);
return true;
};
// flag as read without mark request (called from backend)
// argument should be a coma-separated list of uids
this.flag_deleted_as_read = function(uids)
{
var icn_src;
var rows = this.message_list ? this.message_list.rows : new Array();
var str = String(uids);
var a_uids = new Array();
a_uids = str.split(',');
for (var uid, i=0; i<a_uids.length; i++)
{
uid = a_uids[i];
if (rows[uid])
{
rows[uid].unread = false;
rows[uid].read = true;
rows[uid].classname = rows[uid].classname.replace(/\s*unread/, '');
this.set_classname(rows[uid].obj, 'unread', false);
if (rows[uid].icon)
rows[uid].icon.src = this.env.deletedicon;
}
}
};
/*********************************************************/
/********* login form methods *********/
/*********************************************************/
// handler for keyboard events on the _user field
this.login_user_keyup = function(e)
{
var key = rcube_event.get_keycode(e);
var elm;
// enter
if ((key==13) && (elm = rcube_find_object('_pass')))
{
elm.focus();
return false;
}
};
/*********************************************************/
/********* message compose methods *********/
/*********************************************************/
// checks the input fields before sending a message
this.check_compose_input = function()
{
// check input fields
var input_to = rcube_find_object('_to');
var input_cc = rcube_find_object('_cc');
var input_bcc = rcube_find_object('_bcc');
var input_subject = rcube_find_object('_subject');
var input_message = rcube_find_object('_message');
// check for empty recipient
var recipients = input_to.value ? input_to.value : (input_cc.value ? input_cc.value : input_bcc.value);
if (!rcube_check_email(recipients.replace(/^\s+/, '').replace(/[\s,;]+$/, ''), true))
{
alert(this.get_label('norecipientwarning'));
input_to.focus();
return false;
}
// display localized warning for missing subject
if (input_subject && input_subject.value == '')
{
var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject'));
// user hit cancel, so don't send
if (!subject && subject !== '')
{
input_subject.focus();
return false;
}
else
{
input_subject.value = subject ? subject : this.get_label('nosubject');
}
}
// check for empty body
if ((!window.tinyMCE || !tinyMCE.get('compose-body')) && input_message.value == '' && !confirm(this.get_label('nobodywarning')))
{
input_message.focus();
return false;
}
else if (window.tinyMCE && tinyMCE.get('compose-body') && !tinyMCE.get('compose-body').getContent() && !confirm(this.get_label('nobodywarning')))
{
tinyMCE.get('compose-body').focus();
return false;
}
return true;
};
this.display_spellcheck_controls = function(vis)
{
if (this.env.spellcheck) {
this.env.spellcheck.check_link.style.visibility = vis ? 'visible' : 'hidden';
this.env.spellcheck.switch_lan_pic.style.visibility = vis ? 'visible' : 'hidden';
}
};
this.set_spellcheck_state = function(s)
{
this.spellcheck_ready = (s=='check_spelling' || s=='ready');
this.enable_command('spellcheck', this.spellcheck_ready);
};
this.set_draft_id = function(id)
{
var f;
if (f = rcube_find_object('_draft_saveid'))
f.value = id;
};
this.auto_save_start = function()
{
if (this.env.draft_autosave)
this.save_timer = self.setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000);
// Unlock interface now that saving is complete
this.busy = false;
};
this.compose_field_hash = function(save)
{
// check input fields
var input_to = rcube_find_object('_to');
var input_cc = rcube_find_object('_cc');
var input_bcc = rcube_find_object('_bcc');
var input_subject = rcube_find_object('_subject');
var editor, input_message;
var str = '';
if (input_to && input_to.value)
str += input_to.value+':';
if (input_cc && input_cc.value)
str += input_cc.value+':';
if (input_bcc && input_bcc.value)
str += input_bcc.value+':';
if (input_subject && input_subject.value)
str += input_subject.value+':';
if (editor = tinyMCE.get('compose-body'))
str += editor.getContent();
else
{
input_message = rcube_find_object('_message');
str += input_message.value;
}
if (save)
this.cmp_hash = str;
return str;
};
this.change_identity = function(obj)
{
if (!obj || !obj.options)
return false;
var id = obj.options[obj.selectedIndex].value;
var input_message = rcube_find_object('_message');
var message = input_message ? input_message.value : '';
var is_html = (rcube_find_object('_is_html').value == '1');
var sig, p;
if (!this.env.identity)
this.env.identity = id
if (!is_html)
{
// remove the 'old' signature
if (this.env.identity && this.env.signatures && this.env.signatures[this.env.identity])
{
if (this.env.signatures[this.env.identity]['is_html'])
sig = this.env.signatures[this.env.identity]['plain_text'];
else
sig = this.env.signatures[this.env.identity]['text'];
if (sig.indexOf('-- ')!=0)
sig = '-- \n'+sig;
p = message.lastIndexOf(sig);
if (p>=0)
message = message.substring(0, p-1) + message.substring(p+sig.length, message.length);
}
// add the new signature string
if (this.env.signatures && this.env.signatures[id])
{
sig = this.env.signatures[id]['text'];
if (this.env.signatures[id]['is_html'])
{
sig = this.env.signatures[id]['plain_text'];
}
if (sig.indexOf('-- ')!=0)
sig = '-- \n'+sig;
message += '\n'+sig;
}
}
else
{
var editor = tinyMCE.get('compose-body');
if (this.env.signatures)
{
// Append the signature as a div within the body
var sigElem = editor.dom.get("_rc_sig");
var newsig = '';
var htmlsig = true;
if (!sigElem)
{
sigElem = editor.getDoc().createElement("div");
sigElem.setAttribute("id", "_rc_sig");
editor.getBody().appendChild(sigElem);
}
if (this.env.signatures[id])
{
newsig = this.env.signatures[id]['text'];
htmlsig = this.env.signatures[id]['is_html'];
}
if (htmlsig)
sigElem.innerHTML = newsig;
else
sigElem.innerHTML = '<pre>' + newsig + '</pre>';
}
}
if (input_message)
input_message.value = message;
this.env.identity = id;
return true;
};
this.show_attachment_form = function(a)
{
if (!this.gui_objects.uploadbox)
return false;
var elm, list;
if (elm = this.gui_objects.uploadbox)
{
if (a && (list = this.gui_objects.attachmentlist))
{
var pos = rcube_get_object_pos(list);
var left = pos.x;
var top = pos.y + list.offsetHeight + 10;
elm.style.top = top+'px';
elm.style.left = left+'px';
}
elm.style.visibility = a ? 'visible' : 'hidden';
}
// clear upload form
try {
if (!a && this.gui_objects.attachmentform != this.gui_objects.messageform)
this.gui_objects.attachmentform.reset();
}
catch(e){} // ignore errors
return true;
};
// upload attachment file
this.upload_file = function(form)
{
if (!form)
return false;
// get file input fields
var send = false;
for (var n=0; n<form.elements.length; n++)
if (form.elements[n].type=='file' && form.elements[n].value)
{
send = true;
break;
}
// create hidden iframe and post upload form
if (send)
{
var ts = new Date().getTime();
var frame_name = 'rcmupload'+ts;
// have to do it this way for IE
// otherwise the form will be posted to a new window
if(document.all)
{
var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
document.body.insertAdjacentHTML('BeforeEnd',html);
}
else // for standards-compilant browsers
{
var frame = document.createElement('IFRAME');
frame.name = frame_name;
frame.style.border = 'none';
frame.style.width = 0;
frame.style.height = 0;
frame.style.visibility = 'hidden';
document.body.appendChild(frame);
}
form.target = frame_name;
form.action = this.env.comm_path+'&_action=upload';
form.setAttribute('enctype', 'multipart/form-data');
form.submit();
}
// set reference to the form object
this.gui_objects.attachmentform = form;
return true;
};
// add file name to attachment list
// called from upload page
this.add2attachment_list = function(name, content)
{
if (!this.gui_objects.attachmentlist)
return false;
var li = document.createElement('LI');
li.id = name;
li.innerHTML = content;
this.gui_objects.attachmentlist.appendChild(li);
return true;
};
this.remove_from_attachment_list = function(name)
{
if (!this.gui_objects.attachmentlist)
return false;
var list = this.gui_objects.attachmentlist.getElementsByTagName("li");
for (i=0;i<list.length;i++)
if (list[i].id == name)
this.gui_objects.attachmentlist.removeChild(list[i]);
};
this.remove_attachment = function(name)
{
if (name)
this.http_post('remove-attachment', '_file='+urlencode(name));
return true;
};
// send remote request to add a new contact
this.add_contact = function(value)
{
if (value)
this.http_post('addcontact', '_address='+value);
return true;
};
// send remote request to search mail or contacts
this.qsearch = function(value)
{
if (value != '')
{
if (this.message_list)
this.message_list.clear();
else if (this.contact_list) {
this.contact_list.clear(true);
this.show_contentframe(false);
}
// reset vars
this.env.current_page = 1;
this.set_busy(true, 'searching');
this.http_request('search', '_q='+urlencode(value)+(this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : '')+(this.env.source ? '&_source='+urlencode(this.env.source) : ''), true);
}
return true;
};
// reset quick-search form
this.reset_qsearch = function()
{
if (this.gui_objects.qsearchbox)
this.gui_objects.qsearchbox.value = '';
this.env.search_request = null;
return true;
};
this.sent_successfully = function(type, msg)
{
this.list_mailbox();
this.display_message(msg, type, true);
}
/*********************************************************/
/********* keyboard live-search methods *********/
/*********************************************************/
// handler for keyboard events on address-fields
this.ksearch_keypress = function(e, obj)
{
if (typeof(this.env.contacts)!='object' || !this.env.contacts.length)
return true;
if (this.ksearch_timer)
clearTimeout(this.ksearch_timer);
var highlight;
var key = rcube_event.get_keycode(e);
var mod = rcube_event.get_modifier(e);
switch (key)
{
case 38: // key up
case 40: // key down
if (!this.ksearch_pane)
break;
var dir = key==38 ? 1 : 0;
var next;
highlight = document.getElementById('rcmksearchSelected');
if (!highlight)
highlight = this.ksearch_pane.ul.firstChild;
if (highlight && (next = dir ? highlight.previousSibling : highlight.nextSibling))
{
highlight.removeAttribute('id');
this.set_classname(highlight, 'selected', false);
}
if (next)
{
next.setAttribute('id', 'rcmksearchSelected');
this.set_classname(next, 'selected', true);
this.ksearch_selected = next._rcm_id;
}
return rcube_event.cancel(e);
case 9: // tab
if(mod == SHIFT_KEY)
break;
case 13: // enter
if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value)
break;
// insert selected address and hide ksearch pane
this.insert_recipient(this.ksearch_selected);
this.ksearch_hide();
return rcube_event.cancel(e);
case 27: // escape
this.ksearch_hide();
break;
}
// start timer
this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(); }, 200);
this.ksearch_input = obj;
return true;
};
this.insert_recipient = function(id)
{
if (!this.env.contacts[id] || !this.ksearch_input)
return;
// get cursor pos
var inp_value = this.ksearch_input.value.toLowerCase();
var cpos = this.get_caret_pos(this.ksearch_input);
var p = inp_value.lastIndexOf(this.ksearch_value, cpos);
// replace search string with full address
var pre = this.ksearch_input.value.substring(0, p);
var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length);
var insert = this.env.contacts[id]+', ';
this.ksearch_input.value = pre + insert + end;
// set caret to insert pos
cpos = p+insert.length;
if (this.ksearch_input.setSelectionRange)
this.ksearch_input.setSelectionRange(cpos, cpos);
};
// address search processor
this.ksearch_get_results = function()
{
var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
if (inp_value===null)
return;
// get string from current cursor pos to last comma
var cpos = this.get_caret_pos(this.ksearch_input);
var p = inp_value.lastIndexOf(',', cpos-1);
var q = inp_value.substring(p+1, cpos);
// trim query string
q = q.replace(/(^\s+|\s+$)/g, '').toLowerCase();
if (!q.length || q==this.ksearch_value)
{
if (!q.length && this.ksearch_pane && this.ksearch_pane.visible)
this.ksearch_pane.show(0);
return;
}
this.ksearch_value = q;
// start searching the contact list
var a_results = new Array();
var a_result_ids = new Array();
var c=0;
for (var i=0; i<this.env.contacts.length; i++)
{
if (this.env.contacts[i].toLowerCase().indexOf(q)>=0)
{
a_results[c] = this.env.contacts[i];
a_result_ids[c++] = i;
if (c==15) // limit search results
break;
}
}
// display search results
if (c && a_results.length)
{
var p, ul, li;
// create results pane if not present
if (!this.ksearch_pane)
{
ul = document.createElement('UL');
this.ksearch_pane = new rcube_layer('rcmKSearchpane', {vis:0, zindex:30000});
this.ksearch_pane.elm.appendChild(ul);
this.ksearch_pane.ul = ul;
}
else
ul = this.ksearch_pane.ul;
// remove all search results
ul.innerHTML = '';
// add each result line to list
for (i=0; i<a_results.length; i++)
{
li = document.createElement('LI');
li.innerHTML = a_results[i].replace(/</, '<').replace(/>/, '>');
li._rcm_id = a_result_ids[i];
ul.appendChild(li);
}
// check if last selected item is still in result list
if (this.ksearch_selected!==null)
{
p = find_in_array(this.ksearch_selected, a_result_ids);
if (p>=0 && ul.childNodes)
{
ul.childNodes[p].setAttribute('id', 'rcmksearchSelected');
this.set_classname(ul.childNodes[p], 'selected', true);
}
else
this.ksearch_selected = null;
}
// if no item selected, select the first one
if (this.ksearch_selected===null)
{
ul.firstChild.setAttribute('id', 'rcmksearchSelected');
this.set_classname(ul.firstChild, 'selected', true);
this.ksearch_selected = a_result_ids[0];
}
// move the results pane right under the input box and make it visible
var pos = rcube_get_object_pos(this.ksearch_input);
this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight);
this.ksearch_pane.show(1);
}
// hide results pane
else
this.ksearch_hide();
};
this.ksearch_blur = function(e, obj)
{
if (this.ksearch_timer)
clearTimeout(this.ksearch_timer);
this.ksearch_value = '';
this.ksearch_input = null;
this.ksearch_hide();
};
this.ksearch_hide = function()
{
this.ksearch_selected = null;
if (this.ksearch_pane)
this.ksearch_pane.show(0);
};
/*********************************************************/
/********* address book methods *********/
/*********************************************************/
this.contactlist_keypress = function(list)
{
if (list.key_pressed == list.DELETE_KEY)
this.command('delete');
};
this.contactlist_select = function(list)
{
if (this.preview_timer)
clearTimeout(this.preview_timer);
var id, frame, ref = this;
if (id = list.get_single_selection())
this.preview_timer = window.setTimeout(function(){ ref.load_contact(id, 'show'); }, 200);
else if (this.env.contentframe)
this.show_contentframe(false);
this.enable_command('compose', list.selection.length > 0);
this.enable_command('edit', (id && this.env.address_sources && !this.env.address_sources[this.env.source].readonly) ? true : false);
this.enable_command('delete', list.selection.length && this.env.address_sources && !this.env.address_sources[this.env.source].readonly);
return false;
};
this.list_contacts = function(src, page)
{
var add_url = '';
var target = window;
if (!src)
src = this.env.source;
if (page && this.current_page==page && src == this.env.source)
return false;
if (src != this.env.source)
{
page = 1;
this.env.current_page = page;
this.reset_qsearch();
}
this.select_folder(src, this.env.source);
this.env.source = src;
// load contacts remotely
if (this.gui_objects.contactslist)
{
this.list_contacts_remote(src, page);
return;
}
if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
{
target = window.frames[this.env.contentframe];
add_url = '&_framed=1';
}
// also send search request to get the correct listing
if (this.env.search_request)
add_url += '&_search='+this.env.search_request;
this.set_busy(true, 'loading');
target.location.href = this.env.comm_path+(src ? '&_source='+urlencode(src) : '')+(page ? '&_page='+page : '')+add_url;
};
// send remote request to load contacts list
this.list_contacts_remote = function(src, page)
{
// clear message list first
this.contact_list.clear(true);
this.show_contentframe(false);
this.enable_command('delete', 'compose', false);
// send request to server
var url = (src ? '_source='+urlencode(src) : '') + (page ? (src?'&':'') + '_page='+page : '');
this.env.source = src;
// also send search request to get the right messages
if (this.env.search_request)
url += '&_search='+this.env.search_request;
this.set_busy(true, 'loading');
this.http_request('list', url, true);
};
// load contact record
this.load_contact = function(cid, action, framed)
{
var add_url = '';
var target = window;
if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
{
add_url = '&_framed=1';
target = window.frames[this.env.contentframe];
this.show_contentframe(true);
}
else if (framed)
return false;
if (action && (cid || action=='add') && !this.drag_active)
{
this.set_busy(true);
target.location.href = this.env.comm_path+'&_action='+action+'&_source='+urlencode(this.env.source)+'&_cid='+urlencode(cid) + add_url;
}
return true;
};
// copy a contact to the specified target (group or directory)
this.copy_contact = function(cid, to)
{
if (!cid)
cid = this.contact_list.get_selection().join(',');
if (to != this.env.source && cid && this.env.address_sources[to] && !this.env.address_sources[to].readonly)
this.http_post('copy', '_cid='+urlencode(cid)+'&_source='+urlencode(this.env.source)+'&_to='+urlencode(to));
};
this.delete_contacts = function()
{
// exit if no mailbox specified or if selection is empty
var selection = this.contact_list.get_selection();
if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
return;
var a_cids = new Array();
var qs = '';
if (this.env.cid)
a_cids[a_cids.length] = this.env.cid;
else
{
var id;
for (var n=0; n<selection.length; n++)
{
id = selection[n];
a_cids[a_cids.length] = id;
this.contact_list.remove_row(id, (n == selection.length-1));
}
// hide content frame if we delete the currently displayed contact
if (selection.length == 1)
this.show_contentframe(false);
}
// also send search request to get the right records from the next page
if (this.env.search_request)
qs += '&_search='+this.env.search_request;
// send request to server
this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs);
return true;
};
// update a contact record in the list
this.update_contact_row = function(cid, cols_arr)
{
var row;
if (this.contact_list.rows[cid] && (row = this.contact_list.rows[cid].obj))
{
for (var c=0; c<cols_arr.length; c++)
if (row.cells[c])
row.cells[c].innerHTML = cols_arr[c];
return true;
}
return false;
};
/*********************************************************/
/********* user settings methods *********/
/*********************************************************/
this.init_subscription_list = function()
{
var p = this;
this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist, {multiselect:false, draggable:true, keyboard:false, toggleselect:true});
this.subscription_list.addEventListener('select', function(o){ p.subscription_select(o); });
this.subscription_list.addEventListener('dragstart', function(o){ p.drag_active = true; });
this.subscription_list.addEventListener('dragend', function(o){ p.subscription_move_folder(o); });
this.subscription_list.row_init = function (row)
{
var anchors = row.obj.getElementsByTagName('A');
if (anchors[0])
anchors[0].onclick = function() { p.rename_folder(row.id); return false; };
if (anchors[1])
anchors[1].onclick = function() { p.delete_folder(row.id); return false; };
row.obj.onmouseover = function() { p.focus_subscription(row.id); };
row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
}
this.subscription_list.init();
}
this.identity_select = function(list)
{
var id;
if (id = list.get_single_selection())
this.load_identity(id, 'edit-identity');
};
// load contact record
this.load_identity = function(id, action)
{
if (action=='edit-identity' && (!id || id==this.env.iid))
return false;
var add_url = '';
var target = window;
if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
{
add_url = '&_framed=1';
target = window.frames[this.env.contentframe];
document.getElementById(this.env.contentframe).style.visibility = 'inherit';
}
if (action && (id || action=='add-identity'))
{
this.set_busy(true);
target.location.href = this.env.comm_path+'&_action='+action+'&_iid='+id+add_url;
}
return true;
};
this.delete_identity = function(id)
{
// exit if no mailbox specified or if selection is empty
var selection = this.identity_list.get_selection();
if (!(selection.length || this.env.iid))
return;
if (!id)
id = this.env.iid ? this.env.iid : selection[0];
// if (this.env.framed && id)
this.goto_url('delete-identity', '_iid='+id, true);
return true;
};
this.focus_subscription = function(id)
{
var row, folder;
var reg = RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
if (this.drag_active && this.env.folder && (row = document.getElementById(id)))
if (this.env.subscriptionrows[id] &&
(folder = this.env.subscriptionrows[id][0]))
{
if (this.check_droptarget(folder) &&
!this.env.subscriptionrows[this.get_folder_row_id(this.env.folder)][2] &&
(folder != this.env.folder.replace(reg, '')) &&
(!folder.match(new RegExp('^'+RegExp.escape(this.env.folder+this.env.delimiter)))))
{
this.set_env('dstfolder', folder);
this.set_classname(row, 'droptarget', true);
}
}
else if (this.env.folder.match(new RegExp(RegExp.escape(this.env.delimiter))))
{
this.set_env('dstfolder', this.env.delimiter);
this.set_classname(this.subscription_list.frame, 'droptarget', true);
}
}
this.unfocus_subscription = function(id)
{
var row;
this.set_env('dstfolder', null);
if (this.env.subscriptionrows[id] &&
(row = document.getElementById(id)))
this.set_classname(row, 'droptarget', false);
else
this.set_classname(this.subscription_list.frame, 'droptarget', false);
}
this.subscription_select = function(list)
{
var id, folder;
if ((id = list.get_single_selection()) &&
this.env.subscriptionrows['rcmrow'+id] &&
(folder = this.env.subscriptionrows['rcmrow'+id][0]))
this.set_env('folder', folder);
else
this.set_env('folder', null);
if (this.gui_objects.createfolderhint)
this.gui_objects.createfolderhint.innerHTML = this.env.folder ? this.get_label('addsubfolderhint') : '';
};
this.subscription_move_folder = function(list)
{
var reg = RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
if (this.env.folder && this.env.dstfolder && (this.env.dstfolder != this.env.folder) &&
(this.env.dstfolder != this.env.folder.replace(reg, '')))
{
var reg = new RegExp('[^'+RegExp.escape(this.env.delimiter)+']*['+RegExp.escape(this.env.delimiter)+']', 'g');
var basename = this.env.folder.replace(reg, '');
var newname = this.env.dstfolder==this.env.delimiter ? basename : this.env.dstfolder+this.env.delimiter+basename;
this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.folder)+'&_folder_newname='+urlencode(newname));
}
this.drag_active = false;
this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
};
// tell server to create and subscribe a new mailbox
this.create_folder = function(name)
{
if (this.edit_folder)
this.reset_folder_rename();
var form;
if ((form = this.gui_objects.editform) && form.elements['_folder_name'])
{
name = form.elements['_folder_name'].value;
if (name.indexOf(this.env.delimiter)>=0)
{
alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
return false;
}
if (this.env.folder && name != '')
name = this.env.folder+this.env.delimiter+name;
this.http_post('create-folder', '_name='+urlencode(name), true);
}
else if (form.elements['_folder_name'])
form.elements['_folder_name'].focus();
};
// start renaming the mailbox name.
// this will replace the name string with an input field
this.rename_folder = function(id)
{
var temp, row, form;
// reset current renaming
if (temp = this.edit_folder)
{
this.reset_folder_rename();
if (temp == id)
return;
}
if (id && this.env.subscriptionrows[id] && (row = document.getElementById(id)))
{
var reg = new RegExp('.*['+RegExp.escape(this.env.delimiter)+']');
this.name_input = document.createElement('INPUT');
this.name_input.value = this.env.subscriptionrows[id][0].replace(reg, '');
this.name_input.style.width = '100%';
reg = new RegExp('['+RegExp.escape(this.env.delimiter)+']?[^'+RegExp.escape(this.env.delimiter)+']+$');
this.name_input.__parent = this.env.subscriptionrows[id][0].replace(reg, '');
this.name_input.onkeypress = function(e){ rcmail.name_input_keypress(e); };
row.cells[0].replaceChild(this.name_input, row.cells[0].firstChild);
this.edit_folder = id;
this.name_input.select();
if (form = this.gui_objects.editform)
form.onsubmit = function(){ return false; };
}
};
// remove the input field and write the current mailbox name to the table cell
this.reset_folder_rename = function()
{
var cell = this.name_input ? this.name_input.parentNode : null;
if (cell && this.edit_folder && this.env.subscriptionrows[this.edit_folder])
cell.innerHTML = this.env.subscriptionrows[this.edit_folder][1];
this.edit_folder = null;
};
// handler for keyboard events on the input field
this.name_input_keypress = function(e)
{
var key = rcube_event.get_keycode(e);
// enter
if (key==13)
{
var newname = this.name_input ? this.name_input.value : null;
if (this.edit_folder && newname)
{
if (newname.indexOf(this.env.delimiter)>=0)
{
alert(this.get_label('forbiddencharacter')+' ('+this.env.delimiter+')');
return false;
}
if (this.name_input.__parent)
newname = this.name_input.__parent + this.env.delimiter + newname;
this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.subscriptionrows[this.edit_folder][0])+'&_folder_newname='+urlencode(newname), true);
}
}
// escape
else if (key==27)
this.reset_folder_rename();
};
// delete a specific mailbox with all its messages
this.delete_folder = function(id)
{
var folder = this.env.subscriptionrows[id][0];
if (this.edit_folder)
this.reset_folder_rename();
if (folder && confirm(this.get_label('deletefolderconfirm')))
{
this.http_post('delete-folder', '_mboxes='+urlencode(folder));
this.set_env('folder', null);
if (this.gui_objects.createfolderhint)
this.gui_objects.createfolderhint.innerHTML = '';
}
};
// add a new folder to the subscription list by cloning a folder row
this.add_folder_row = function(name, display_name, replace, before)
{
if (!this.gui_objects.subscriptionlist)
return false;
// find not protected folder
for (var refid in this.env.subscriptionrows)
if (this.env.subscriptionrows[refid]!=null && !this.env.subscriptionrows[refid][2])
break;
var refrow, form;
var tbody = this.gui_objects.subscriptionlist.tBodies[0];
var id = 'rcmrow'+(tbody.childNodes.length+1);
var selection = this.subscription_list.get_single_selection();
if (replace && replace.id)
{
id = replace.id;
refid = replace.id;
}
if (!id || !(refrow = document.getElementById(refid)))
{
// Refresh page if we don't have a table row to clone
this.goto_url('folders');
}
else
{
// clone a table row if there are existing rows
var row = this.clone_table_row(refrow);
row.id = id;
if (before && (before = this.get_folder_row_id(before)))
- tbody.insertBefore(row, document.getElementById(before));
+ tbody.insertBefore(row, document.getElementById(before));
else
- tbody.appendChild(row);
+ tbody.appendChild(row);
if (replace)
- tbody.removeChild(replace);
+ tbody.removeChild(replace);
}
// add to folder/row-ID map
this.env.subscriptionrows[row.id] = [name, display_name, 0];
// set folder name
row.cells[0].innerHTML = display_name;
// set messages count to zero
if (!replace)
row.cells[1].innerHTML = '*';
if (!replace && row.cells[2] && row.cells[2].firstChild.tagName=='INPUT')
{
row.cells[2].firstChild.value = name;
row.cells[2].firstChild.checked = true;
}
// add new folder to rename-folder list and clear input field
if (!replace && (form = this.gui_objects.editform))
{
if (form.elements['_folder_oldname'])
form.elements['_folder_oldname'].options[form.elements['_folder_oldname'].options.length] = new Option(name,name);
if (form.elements['_folder_name'])
form.elements['_folder_name'].value = '';
}
this.init_subscription_list();
if (selection && document.getElementById('rcmrow'+selection))
this.subscription_list.select_row(selection);
if (document.getElementById(id).scrollIntoView)
document.getElementById(id).scrollIntoView();
};
// replace an existing table row with a new folder line
this.replace_folder_row = function(oldfolder, newfolder, display_name, before)
{
var id = this.get_folder_row_id(oldfolder);
var row = document.getElementById(id);
// replace an existing table row (if found)
this.add_folder_row(newfolder, display_name, row, before);
// rename folder in rename-folder dropdown
var form, elm;
if ((form = this.gui_objects.editform) && (elm = form.elements['_folder_oldname']))
{
for (var i=0;i<elm.options.length;i++)
{
if (elm.options[i].value == oldfolder)
{
elm.options[i].text = display_name;
elm.options[i].value = newfolder;
break;
}
}
form.elements['_folder_newname'].value = '';
}
};
// remove the table row of a specific mailbox from the table
// (the row will not be removed, just hidden)
this.remove_folder_row = function(folder)
{
var row;
var id = this.get_folder_row_id(folder);
if (id && (row = document.getElementById(id)))
row.style.display = 'none';
// remove folder from rename-folder list
var form;
if ((form = this.gui_objects.editform) && form.elements['_folder_oldname'])
{
for (var i=0;i<form.elements['_folder_oldname'].options.length;i++)
{
if (form.elements['_folder_oldname'].options[i].value == folder)
{
form.elements['_folder_oldname'].options[i] = null;
break;
}
}
}
if (form && form.elements['_folder_newname'])
form.elements['_folder_newname'].value = '';
};
this.subscribe_folder = function(folder)
{
if (folder)
this.http_post('subscribe', '_mbox='+urlencode(folder));
};
this.unsubscribe_folder = function(folder)
{
if (folder)
this.http_post('unsubscribe', '_mbox='+urlencode(folder));
};
// helper method to find a specific mailbox row ID
this.get_folder_row_id = function(folder)
{
for (var id in this.env.subscriptionrows)
if (this.env.subscriptionrows[id] && this.env.subscriptionrows[id][0] == folder)
break;
return id;
};
// duplicate a specific table row
this.clone_table_row = function(row)
{
var cell, td;
var new_row = document.createElement('TR');
- for(var n=0; n<row.childNodes.length; n++)
+ for(var n=0; n<row.cells.length; n++)
{
- cell = row.childNodes[n];
+ cell = row.cells[n];
td = document.createElement('TD');
if (cell.className)
td.className = cell.className;
if (cell.align)
td.setAttribute('align', cell.align);
td.innerHTML = cell.innerHTML;
new_row.appendChild(td);
}
return new_row;
};
/*********************************************************/
/********* GUI functionality *********/
/*********************************************************/
// eable/disable buttons for page shifting
this.set_page_buttons = function()
{
this.enable_command('nextpage', (this.env.pagecount > this.env.current_page));
this.enable_command('lastpage', (this.env.pagecount > this.env.current_page));
this.enable_command('previouspage', (this.env.current_page > 1));
this.enable_command('firstpage', (this.env.current_page > 1));
}
// set button to a specific state
this.set_button = function(command, state)
{
var a_buttons = this.buttons[command];
var button, obj;
if(!a_buttons || !a_buttons.length)
return false;
for(var n=0; n<a_buttons.length; n++)
{
button = a_buttons[n];
obj = document.getElementById(button.id);
// get default/passive setting of the button
if (obj && button.type=='image' && !button.status) {
button.pas = obj._original_src ? obj._original_src : obj.src;
// respect PNG fix on IE browsers
if (obj.runtimeStyle && obj.runtimeStyle.filter && obj.runtimeStyle.filter.match(/src=['"]([^'"]+)['"]/))
button.pas = RegExp.$1;
}
else if (obj && !button.status)
button.pas = String(obj.className);
// set image according to button state
if (obj && button.type=='image' && button[state])
{
button.status = state;
obj.src = button[state];
}
// set class name according to button state
else if (obj && typeof(button[state])!='undefined')
{
button.status = state;
obj.className = button[state];
}
// disable/enable input buttons
if (obj && button.type=='input')
{
button.status = state;
obj.disabled = !state;
}
}
};
// display a specific alttext
this.set_alttext = function(command, label)
{
if (!this.buttons[command] || !this.buttons[command].length)
return;
var button, obj, link;
for (var n=0; n<this.buttons[command].length; n++)
{
button = this.buttons[command][n];
obj = document.getElementById(button.id);
if (button.type=='image' && obj)
{
obj.setAttribute('alt', this.get_label(label));
if ((link = obj.parentNode) && link.tagName == 'A')
link.setAttribute('title', this.get_label(label));
}
else if (obj)
obj.setAttribute('title', this.get_label(label));
}
};
// mouse over button
this.button_over = function(command, id)
{
var a_buttons = this.buttons[command];
var button, img;
if(!a_buttons || !a_buttons.length)
return false;
for(var n=0; n<a_buttons.length; n++)
{
button = a_buttons[n];
if(button.id==id && button.status=='act')
{
img = document.getElementById(button.id);
if (img && button.over)
img.src = button.over;
}
}
};
// mouse down on button
this.button_sel = function(command, id)
{
var a_buttons = this.buttons[command];
var button, img;
if(!a_buttons || !a_buttons.length)
return;
for(var n=0; n<a_buttons.length; n++)
{
button = a_buttons[n];
if(button.id==id && button.status=='act')
{
img = document.getElementById(button.id);
if (img && button.sel)
img.src = button.sel;
}
}
};
// mouse out of button
this.button_out = function(command, id)
{
var a_buttons = this.buttons[command];
var button, img;
if(!a_buttons || !a_buttons.length)
return;
for(var n=0; n<a_buttons.length; n++)
{
button = a_buttons[n];
if(button.id==id && button.status=='act')
{
img = document.getElementById(button.id);
if (img && button.act)
img.src = button.act;
}
}
};
// set/unset a specific class name
this.set_classname = function(obj, classname, set)
{
var reg = new RegExp('\s*'+classname, 'i');
if (!set && obj.className.match(reg))
obj.className = obj.className.replace(reg, '');
else if (set && !obj.className.match(reg))
obj.className += ' '+classname;
};
// write to the document/window title
this.set_pagetitle = function(title)
{
if (title && document.title)
document.title = title;
}
// display a system message
this.display_message = function(msg, type, hold)
{
if (!this.loaded) // save message in order to display after page loaded
{
this.pending_message = new Array(msg, type);
return true;
}
// pass command to parent window
if (this.env.framed && parent.rcmail)
return parent.rcmail.display_message(msg, type, hold);
if (!this.gui_objects.message)
return false;
if (this.message_timer)
clearTimeout(this.message_timer);
var cont = msg;
if (type)
cont = '<div class="'+type+'">'+cont+'</div>';
var _rcube = this;
this.gui_objects.message.innerHTML = cont;
this.gui_objects.message.style.display = 'block';
if (type!='loading')
this.gui_objects.message.onmousedown = function(){ _rcube.hide_message(); return true; };
if (!hold)
this.message_timer = window.setTimeout(function(){ ref.hide_message(); }, this.message_time);
};
// make a message row disapear
this.hide_message = function()
{
if (this.gui_objects.message)
{
this.gui_objects.message.style.display = 'none';
this.gui_objects.message.onmousedown = null;
}
};
// mark a mailbox as selected and set environment variable
this.select_folder = function(name, old)
{
if (this.gui_objects.folderlist)
{
var current_li, target_li;
if ((current_li = this.get_folder_li(old)))
{
this.set_classname(current_li, 'selected', false);
this.set_classname(current_li, 'unfocused', false);
}
if ((target_li = this.get_folder_li(name)))
{
this.set_classname(target_li, 'unfocused', false);
this.set_classname(target_li, 'selected', true);
}
}
};
// helper method to find a folder list item
this.get_folder_li = function(name)
{
if (this.gui_objects.folderlist)
{
name = String(name).replace(this.identifier_expr, '');
return document.getElementById('rcmli'+name);
}
return null;
};
// for reordering column array, Konqueror workaround
this.set_message_coltypes = function(coltypes)
{
this.coltypes = coltypes;
// set correct list titles
var cell, col;
var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null;
for (var n=0; thead && n<this.coltypes.length; n++)
{
col = this.coltypes[n];
if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to'))
{
// if we have links for sorting, it's a bit more complicated...
if (cell.firstChild && cell.firstChild.tagName=='A')
{
cell.firstChild.innerHTML = this.get_label(this.coltypes[n]);
cell.firstChild.onclick = function(){ return rcmail.command('sort', this.__col, this); };
cell.firstChild.__col = col;
}
else
cell.innerHTML = this.get_label(this.coltypes[n]);
cell.id = 'rcmHead'+col;
}
else if (col == 'subject' && this.message_list)
this.message_list.subject_col = n+1;
else if (col == 'flag' && this.env.unflaggedicon)
{
cell.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />';
}
}
};
// create a table row in the message list
this.add_message_row = function(uid, cols, flags, attachment, attop)
{
if (!this.gui_objects.messagelist || !this.message_list)
return false;
var tbody = this.gui_objects.messagelist.tBodies[0];
var rowcount = tbody.rows.length;
var even = rowcount%2;
this.env.messages[uid] = {deleted:flags.deleted?1:0,
replied:flags.replied?1:0,
unread:flags.unread?1:0,
flagged:flags.flagged?1:0};
var row = document.createElement('TR');
row.id = 'rcmrow'+uid;
row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : '')+(flags.deleted ? ' deleted' : '');
if (this.message_list.in_selection(uid))
row.className += ' selected';
var icon = flags.deleted && this.env.deletedicon ? this.env.deletedicon:
(flags.unread && this.env.unreadicon ? this.env.unreadicon :
(flags.replied && this.env.repliedicon ? this.env.repliedicon : this.env.messageicon));
var col = document.createElement('TD');
col.className = 'icon';
col.innerHTML = icon ? '<img src="'+icon+'" alt="" />' : '';
row.appendChild(col);
// add each submitted col
for (var n = 0; n < this.coltypes.length; n++)
{
var c = this.coltypes[n];
col = document.createElement('TD');
col.className = String(c).toLowerCase();
if (c=='flag')
{
if (flags.flagged && this.env.flaggedicon)
col.innerHTML = '<img src="'+this.env.flaggedicon+'" alt="" />';
else if(this.env.unflaggedicon)
col.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />';
}
else
col.innerHTML = cols[c];
row.appendChild(col);
}
col = document.createElement('TD');
col.className = 'icon';
col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : '';
row.appendChild(col);
this.message_list.insert_row(row, attop);
};
// replace content of row count display
this.set_rowcount = function(text)
{
if (this.gui_objects.countdisplay)
this.gui_objects.countdisplay.innerHTML = text;
// update page navigation buttons
this.set_page_buttons();
};
// replace content of mailboxname display
this.set_mailboxname = function(content)
{
if (this.gui_objects.mailboxname && content)
this.gui_objects.mailboxname.innerHTML = content;
};
// replace content of quota display
this.set_quota = function(content)
{
if (this.gui_objects.quotadisplay && content)
this.gui_objects.quotadisplay.innerHTML = content;
};
// update the mailboxlist
this.set_unread_count = function(mbox, count, set_title)
{
if (!this.gui_objects.mailboxlist)
return false;
this.env.unread_counts[mbox] = count;
this.set_unread_count_display(mbox, set_title);
}
// update the mailbox count display
this.set_unread_count_display = function(mbox, set_title)
{
var reg, text_obj, item, mycount, childcount, div;
if (item = this.get_folder_li(mbox))
{
mycount = this.env.unread_counts[mbox];
text_obj = item.getElementsByTagName('a')[0];
reg = /\s+\([0-9]+\)$/i;
childcount = 0;
if ((div = item.getElementsByTagName('div')[0]) &&
div.className.match(/collapsed/))
{
// add children's counters
for (var k in this.env.unread_counts)
if (k.indexOf(mbox + this.env.delimiter) == 0) {
childcount += this.env.unread_counts[k];
}
}
if (mycount && text_obj.innerHTML.match(reg))
text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+mycount+')');
else if (mycount)
text_obj.innerHTML += ' ('+mycount+')';
else
text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
// set parent's display
reg = new RegExp(RegExp.escape(this.env.delimiter) + '[^' + RegExp.escape(this.env.delimiter) + ']+');
if (mbox.match(reg))
this.set_unread_count_display(mbox.replace(reg, ''), false);
// set the right classes
this.set_classname(item, 'unread', (mycount+childcount)>0 ? true : false);
}
// set unread count to window title
reg = /^\([0-9]+\)\s+/i;
if (set_title && document.title)
{
var doc_title = String(document.title);
var new_title = "";
if (mycount && doc_title.match(reg))
new_title = doc_title.replace(reg, '('+mycount+') ');
else if (mycount)
new_title = '('+mycount+') '+doc_title;
else
new_title = doc_title.replace(reg, '');
this.set_pagetitle(new_title);
}
};
// update parent's mailboxlist (from preview)
this.set_unread_count_from_preview = function(mbox, count, set_title)
{
parent.rcmail.set_unread_count(mbox, count, set_title);
}
// add row to contacts list
this.add_contact_row = function(cid, cols, select)
{
if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
return false;
var tbody = this.gui_objects.contactslist.tBodies[0];
var rowcount = tbody.rows.length;
var even = rowcount%2;
var row = document.createElement('TR');
row.id = 'rcmrow'+cid;
row.className = 'contact '+(even ? 'even' : 'odd');
if (this.contact_list.in_selection(cid))
row.className += ' selected';
// add each submitted col
for (var c in cols)
{
col = document.createElement('TD');
col.className = String(c).toLowerCase();
col.innerHTML = cols[c];
row.appendChild(col);
}
this.contact_list.insert_row(row);
this.enable_command('export', (this.contact_list.rowcount > 0));
};
this.toggle_editor = function(checkbox, textAreaId)
{
var ischecked = checkbox.checked;
if (ischecked)
{
tinyMCE.execCommand('mceAddControl', true, textAreaId);
}
else
{
tinyMCE.execCommand('mceRemoveControl', true, textAreaId);
}
};
this.toggle_prefer_html = function(checkbox)
{
var addrbook_show_images;
if (addrbook_show_images = document.getElementById('rcmfd_addrbook_show_images'))
addrbook_show_images.disabled = !checkbox.checked;
}
/********************************************************/
/********* remote request methods *********/
/********************************************************/
this.redirect = function(url, lock)
{
if (lock || lock === null)
this.set_busy(true);
if (this.env.framed && window.parent)
parent.location.href = url;
else
location.href = url;
};
this.goto_url = function(action, query, lock)
{
var querystring = query ? '&'+query : '';
this.redirect(this.env.comm_path+'&_action='+action+querystring, lock);
};
this.http_sockets = new Array();
// find a non-busy socket or create a new one
this.get_request_obj = function()
{
for (var n=0; n<this.http_sockets.length; n++)
{
if (!this.http_sockets[n].busy)
return this.http_sockets[n];
}
// create a new XMLHTTP object
var i = this.http_sockets.length;
this.http_sockets[i] = new rcube_http_request();
return this.http_sockets[i];
};
// send a http request to the server
this.http_request = function(action, querystring, lock)
{
var request_obj = this.get_request_obj();
querystring += (querystring ? '&' : '') + '_remote=1';
// add timestamp to request url to avoid cacheing problems in Safari
if (bw.safari)
querystring += '&_ts='+(new Date().getTime());
// send request
if (request_obj)
{
console.log('HTTP request: '+this.env.comm_path+'&_action='+action+'&'+querystring);
if (lock)
this.set_busy(true);
var rcm = this;
request_obj.__lock = lock ? true : false;
request_obj.__action = action;
request_obj.onerror = function(o){ ref.http_error(o); };
request_obj.oncomplete = function(o){ ref.http_response(o); };
request_obj.GET(this.env.comm_path+'&_action='+action+'&'+querystring);
}
};
// send a http POST request to the server
this.http_post = function(action, postdata, lock)
{
var request_obj;
if (postdata && typeof(postdata) == 'object')
postdata._remote = 1;
else
postdata += (postdata ? '&' : '') + '_remote=1';
// send request
if (request_obj = this.get_request_obj())
{
console.log('HTTP POST: '+this.env.comm_path+'&_action='+action);
if (lock)
this.set_busy(true);
var rcm = this;
request_obj.__lock = lock ? true : false;
request_obj.__action = action;
request_obj.onerror = function(o){ rcm.http_error(o); };
request_obj.oncomplete = function(o){ rcm.http_response(o); };
request_obj.POST(this.env.comm_path+'&_action='+action, postdata);
}
};
// handle HTTP response
this.http_response = function(request_obj)
{
var ctype = request_obj.get_header('Content-Type');
if (ctype){
ctype = String(ctype).toLowerCase();
var ctype_array=ctype.split(";");
ctype = ctype_array[0];
}
if (request_obj.__lock)
this.set_busy(false);
console.log(request_obj.get_text());
// if we get javascript code from server -> execute it
if (request_obj.get_text() && (ctype=='text/javascript' || ctype=='application/x-javascript'))
eval(request_obj.get_text());
// process the response data according to the sent action
switch (request_obj.__action) {
case 'delete':
if (this.task == 'addressbook') {
var uid = this.contact_list.get_selection();
this.enable_command('compose', (uid && this.contact_list.rows[uid]));
this.enable_command('delete', 'edit', (uid && this.contact_list.rows[uid] && this.env.address_sources && !this.env.address_sources[this.env.source].readonly));
this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
}
case 'moveto':
if (this.env.action == 'show')
this.command('list');
else if (this.message_list)
this.message_list.init();
break;
case 'purge':
case 'expunge':
if (!this.env.messagecount && this.task == 'mail') {
// clear preview pane content
if (this.env.contentframe)
this.show_contentframe(false);
// disable commands useless when mailbox is empty
this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource',
'print', 'load-attachment', 'purge', 'expunge', 'select-all', 'select-none', 'sort', false);
}
break;
case 'check-recent':
case 'getunread':
case 'list':
if (this.task == 'mail') {
if (this.message_list)
this.msglist_select(this.message_list);
this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0));
this.enable_command('purge', this.purge_mailbox_test());
}
else if (this.task == 'addressbook')
this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
break;
}
request_obj.reset();
};
// handle HTTP request errors
this.http_error = function(request_obj)
{
//alert('Error sending request: '+request_obj.url+' => HTTP '+request_obj.xmlhttp.status);
if (request_obj.__lock)
this.set_busy(false);
request_obj.reset();
request_obj.__lock = false;
this.display_message('Unknown Server Error!', 'error');
};
// use an image to send a keep-alive siganl to the server
this.send_keep_alive = function()
{
var d = new Date();
this.http_request('keep-alive', '_t='+d.getTime());
};
// send periodic request to check for recent messages
this.check_for_recent = function()
{
if (this.busy)
return;
this.set_busy(true, 'checkingmail');
this.http_request('check-recent', (this.env.search_request ? '_search='+this.env.search_request+'&' : '') + '_t='+(new Date().getTime()), true);
};
/********************************************************/
/********* helper methods *********/
/********************************************************/
// check if we're in show mode or if we have a unique selection
// and return the message uid
this.get_single_uid = function()
{
return this.env.uid ? this.env.uid : (this.message_list ? this.message_list.get_single_selection() : null);
};
// same as above but for contacts
this.get_single_cid = function()
{
return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null);
};
this.get_caret_pos = function(obj)
{
if (typeof(obj.selectionEnd)!='undefined')
return obj.selectionEnd;
else if (document.selection && document.selection.createRange)
{
var range = document.selection.createRange();
if (range.parentElement()!=obj)
return 0;
var gm = range.duplicate();
if (obj.tagName=='TEXTAREA')
gm.moveToElementText(obj);
else
gm.expand('textedit');
gm.setEndPoint('EndToStart', range);
var p = gm.text.length;
return p<=obj.value.length ? p : -1;
}
else
return obj.value.length;
};
this.set_caret2start = function(obj)
{
if (obj.createTextRange)
{
var range = obj.createTextRange();
range.collapse(true);
range.select();
}
else if (obj.setSelectionRange)
obj.setSelectionRange(0,0);
obj.focus();
};
// set all fields of a form disabled
this.lock_form = function(form, lock)
{
if (!form || !form.elements)
return;
var type;
for (var n=0; n<form.elements.length; n++)
{
type = form.elements[n];
if (type=='hidden')
continue;
form.elements[n].disabled = lock;
}
};
} // end object rcube_webmail
/**
* Class for sending HTTP requests
* @constructor
*/
function rcube_http_request()
{
this.url = '';
this.busy = false;
this.xmlhttp = null;
// reset object properties
this.reset = function()
{
// set unassigned event handlers
this.onloading = function(){ };
this.onloaded = function(){ };
this.oninteractive = function(){ };
this.oncomplete = function(){ };
this.onabort = function(){ };
this.onerror = function(){ };
this.url = '';
this.busy = false;
this.xmlhttp = null;
}
// create HTMLHTTP object
this.build = function()
{
if (window.XMLHttpRequest)
this.xmlhttp = new XMLHttpRequest();
else if (window.ActiveXObject)
{
try { this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
catch(e) { this.xmlhttp = null; }
}
else
{
}
}
// send GET request
this.GET = function(url)
{
this.build();
if (!this.xmlhttp)
{
this.onerror(this);
return false;
}
var _ref = this;
this.url = url;
this.busy = true;
this.xmlhttp.onreadystatechange = function(){ _ref.xmlhttp_onreadystatechange(); };
this.xmlhttp.open('GET', url, true);
this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
this.xmlhttp.send(null);
};
this.POST = function(url, body, contentType)
{
// default value for contentType if not provided
if (typeof(contentType) == 'undefined')
contentType = 'application/x-www-form-urlencoded';
this.build();
if (!this.xmlhttp)
{
this.onerror(this);
return false;
}
var req_body = body;
if (typeof(body) == 'object')
{
req_body = '';
for (var p in body)
req_body += (req_body ? '&' : '') + p+'='+urlencode(body[p]);
}
var ref = this;
this.url = url;
this.busy = true;
this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); };
this.xmlhttp.open('POST', url, true);
this.xmlhttp.setRequestHeader('Content-Type', contentType);
this.xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid'));
this.xmlhttp.send(req_body);
};
// handle onreadystatechange event
this.xmlhttp_onreadystatechange = function()
{
if(this.xmlhttp.readyState == 1)
this.onloading(this);
else if(this.xmlhttp.readyState == 2)
this.onloaded(this);
else if(this.xmlhttp.readyState == 3)
this.oninteractive(this);
else if(this.xmlhttp.readyState == 4)
{
try {
if (this.xmlhttp.status == 0)
this.onabort(this);
else if(this.xmlhttp.status == 200)
this.oncomplete(this);
else
this.onerror(this);
this.busy = false;
}
catch(err)
{
this.onerror(this);
this.busy = false;
}
}
}
// getter method for HTTP headers
this.get_header = function(name)
{
return this.xmlhttp.getResponseHeader(name);
};
this.get_text = function()
{
return this.xmlhttp.responseText;
};
this.get_xml = function()
{
return this.xmlhttp.responseXML;
};
this.reset();
} // end class rcube_http_request
// helper function to call the init method with a delay
function call_init(o)
{
window.setTimeout('if (window[\''+o+'\'] && window[\''+o+'\'].init) { '+o+'.init(); }',
bw.win ? 500 : 200);
}
diff --git a/program/js/common.js b/program/js/common.js
index 6e4c643fa..209ce1070 100644
--- a/program/js/common.js
+++ b/program/js/common.js
@@ -1,611 +1,627 @@
/*
+-----------------------------------------------------------------------+
| RoundCube common js library |
| |
| This file is part of the RoundCube web development suite |
| Copyright (C) 2005-2007, RoundCube Dev, - Switzerland |
| Licensed under the GNU GPL |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
// Constants
var CONTROL_KEY = 1;
var SHIFT_KEY = 2;
var CONTROL_SHIFT_KEY = 3;
/**
* Default browser check class
* @construcotr
*/
function roundcube_browser()
{
this.ver = parseFloat(navigator.appVersion);
this.appver = navigator.appVersion;
this.agent = navigator.userAgent;
this.name = navigator.appName;
this.vendor = navigator.vendor ? navigator.vendor : '';
this.vendver = navigator.vendorSub ? parseFloat(navigator.vendorSub) : 0;
this.product = navigator.product ? navigator.product : '';
this.platform = String(navigator.platform).toLowerCase();
this.lang = (navigator.language) ? navigator.language.substring(0,2) :
(navigator.browserLanguage) ? navigator.browserLanguage.substring(0,2) :
(navigator.systemLanguage) ? navigator.systemLanguage.substring(0,2) : 'en';
this.win = (this.platform.indexOf('win')>=0) ? true : false;
this.mac = (this.platform.indexOf('mac')>=0) ? true : false;
this.linux = (this.platform.indexOf('linux')>=0) ? true : false;
this.unix = (this.platform.indexOf('unix')>=0) ? true : false;
this.dom = document.getElementById ? true : false;
this.dom2 = (document.addEventListener && document.removeEventListener);
this.ie = (document.all) ? true : false;
this.ie4 = (this.ie && !this.dom);
this.ie5 = (this.dom && this.appver.indexOf('MSIE 5')>0);
this.ie6 = (this.dom && this.appver.indexOf('MSIE 6')>0);
this.mz = (this.dom && this.ver>=5); // (this.dom && this.product=='Gecko')
this.ns = ((this.ver<5 && this.name=='Netscape') || (this.ver>=5 && this.vendor.indexOf('Netscape')>=0));
this.ns6 = (this.ns && parseInt(this.vendver)==6); // (this.mz && this.ns) ? true : false;
this.ns7 = (this.ns && parseInt(this.vendver)==7); // this.agent.indexOf('Netscape/7')>0);
this.safari = (this.agent.toLowerCase().indexOf('safari')>0 || this.agent.toLowerCase().indexOf('applewebkit')>0);
this.konq = (this.agent.toLowerCase().indexOf('konqueror')>0);
this.opera = (window.opera) ? true : false;
if(this.opera && window.RegExp)
this.vendver = (/opera(\s|\/)([0-9\.]+)/i.test(navigator.userAgent)) ? parseFloat(RegExp.$2) : -1;
else if(!this.vendver && this.safari)
this.vendver = (/(safari|applewebkit)\/([0-9]+)/i.test(this.agent)) ? parseInt(RegExp.$2) : 0;
else if((!this.vendver && this.mz) || this.agent.indexOf('Camino')>0)
this.vendver = (/rv:([0-9\.]+)/.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
else if(this.ie && window.RegExp)
this.vendver = (/msie\s+([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
else if(this.konq && window.RegExp)
this.vendver = (/khtml\/([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
// get real language out of safari's user agent
if(this.safari && (/;\s+([a-z]{2})-[a-z]{2}\)/i.test(this.agent)))
this.lang = RegExp.$1;
this.dhtml = ((this.ie4 && this.win) || this.ie5 || this.ie6 || this.ns4 || this.mz);
this.vml = (this.win && this.ie && this.dom && !this.opera);
this.pngalpha = (this.mz || (this.opera && this.vendver>=6) || (this.ie && this.mac && this.vendver>=5) ||
(this.ie && this.win && this.vendver>=5.5) || this.safari);
this.opacity = (this.mz || (this.ie && this.vendver>=5.5 && !this.opera) || (this.safari && this.vendver>=100));
this.cookies = navigator.cookieEnabled;
// test for XMLHTTP support
this.xmlhttp_test = function()
{
var activeX_test = new Function("try{var o=new ActiveXObject('Microsoft.XMLHTTP');return true;}catch(err){return false;}");
this.xmlhttp = (window.XMLHttpRequest || (window.ActiveXObject && activeX_test())) ? true : false;
return this.xmlhttp;
}
}
// static functions for event handling
var rcube_event = {
/**
* returns the event target element
*/
get_target: function(e)
{
e = e || window.event;
return e && e.target ? e.target : e.srcElement;
},
/**
* returns the event key code
*/
get_keycode: function(e)
{
e = e || window.event;
return e && e.keyCode ? e.keyCode : (e && e.which ? e.which : 0);
},
+/**
+ * returns the event key code
+ */
+get_button: function(e)
+{
+ e = e || window.event;
+ return e && (typeof e.button != 'undefined') ? e.button : (e && e.which ? e.which : 0);
+},
+
/**
* returns modifier key (constants defined at top of file)
*/
get_modifier: function(e)
{
var opcode = 0;
e = e || window.event;
if (bw.mac && e)
{
opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
return opcode;
}
if (e)
{
opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
return opcode;
}
},
/**
* Return absolute mouse position of an event
*/
get_mouse_pos: function(e)
{
if (!e) e = window.event;
var mX = (e.pageX) ? e.pageX : e.clientX;
var mY = (e.pageY) ? e.pageY : e.clientY;
if (document.body && document.all)
{
mX += document.body.scrollLeft;
mY += document.body.scrollTop;
}
return { x:mX, y:mY };
},
/**
* Add an object method as event listener to a certain element
*/
add_listener: function(p)
{
if (!p.object || !p.method) // not enough arguments
return;
if (!p.element)
p.element = document;
if (!p.object._rc_events)
p.object._rc_events = [];
var key = p.event + '*' + p.method;
if (!p.object._rc_events[key])
p.object._rc_events[key] = function(e){ return p.object[p.method](e); };
if (p.element.addEventListener)
p.element.addEventListener(p.event, p.object._rc_events[key], false);
else if (p.element.attachEvent)
{
// IE allows multiple events with the same function to be applied to the same object
// forcibly detach the event, then attach
p.element.detachEvent('on'+p.event, p.object._rc_events[key]);
p.element.attachEvent('on'+p.event, p.object._rc_events[key]);
}
else
p.element['on'+p.event] = p.object._rc_events[key];
},
/**
* Remove event listener
*/
remove_listener: function(p)
{
if (!p.element)
p.element = document;
var key = p.event + '*' + p.method;
if (p.object && p.object._rc_events && p.object._rc_events[key]) {
if (p.element.removeEventListener)
p.element.removeEventListener(p.event, p.object._rc_events[key], false);
else if (p.element.detachEvent)
p.element.detachEvent('on'+p.event, p.object._rc_events[key]);
else
p.element['on'+p.event] = null;
}
},
/**
* Prevent event propagation and bubbeling
*/
cancel: function(evt)
{
var e = evt ? evt : window.event;
if (e.preventDefault)
e.preventDefault();
if (e.stopPropagation)
e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
};
var rcube_layer_objects = new Array();
/**
* RoundCube generic layer (floating box) class
*
* @constructor
*/
function rcube_layer(id, attributes)
{
this.name = id;
// create a new layer in the current document
this.create = function(arg)
{
var l = (arg.x) ? arg.x : 0;
var t = (arg.y) ? arg.y : 0;
var w = arg.width;
var h = arg.height;
var z = arg.zindex;
var vis = arg.vis;
var parent = arg.parent;
var obj;
obj = document.createElement('DIV');
with(obj)
{
id = this.name;
with(style)
{
position = 'absolute';
visibility = (vis) ? (vis==2) ? 'inherit' : 'visible' : 'hidden';
left = l+'px';
top = t+'px';
if(w) width = w+'px';
if(h) height = h+'px';
if(z) zIndex = z;
}
}
if(parent) parent.appendChild(obj);
else document.body.appendChild(obj);
this.elm = obj;
};
// create new layer
if(attributes!=null)
{
this.create(attributes);
this.name = this.elm.id;
}
else // just refer to the object
this.elm = document.getElementById(id);
if(!this.elm)
return false;
// ********* layer object properties *********
this.css = this.elm.style;
this.event = this.elm;
this.width = this.elm.offsetWidth;
this.height = this.elm.offsetHeight;
this.x = parseInt(this.elm.offsetLeft);
this.y = parseInt(this.elm.offsetTop);
this.visible = (this.css.visibility=='visible' || this.css.visibility=='show' || this.css.visibility=='inherit') ? true : false;
this.id = rcube_layer_objects.length;
this.obj = 'rcube_layer_objects['+this.id+']';
rcube_layer_objects[this.id] = this;
// ********* layer object methods *********
// move the layer to a specific position
this.move = function(x, y)
{
this.x = x;
this.y = y;
this.css.left = Math.round(this.x)+'px';
this.css.top = Math.round(this.y)+'px';
}
// move the layer for a specific step
this.shift = function(x,y)
{
x = Math.round(x*100)/100;
y = Math.round(y*100)/100;
this.move(this.x+x, this.y+y);
}
// change the layers width and height
this.resize = function(w,h)
{
this.css.width = w+'px';
this.css.height = h+'px';
this.width = w;
this.height = h;
}
// cut the layer (top,width,height,left)
this.clip = function(t,w,h,l)
{
this.css.clip='rect('+t+' '+w+' '+h+' '+l+')';
this.clip_height = h;
this.clip_width = w;
}
// show or hide the layer
this.show = function(a)
{
if(a==1)
{
this.css.visibility = 'visible';
this.visible = true;
}
else if(a==2)
{
this.css.visibility = 'inherit';
this.visible = true;
}
else
{
this.css.visibility = 'hidden';
this.visible = false;
}
}
// write new content into a Layer
this.write = function(cont)
{
this.elm.innerHTML = cont;
}
// set the given color to the layer background
this.set_bgcolor = function(c)
{
if(!c || c=='#')
c = 'transparent';
this.css.backgroundColor = c;
}
// set the opacity of a layer to the given ammount (in %)
this.set_opacity = function(v)
{
if(!bw.opacity)
return;
var op = v<=1 ? Math.round(v*100) : parseInt(v);
if(bw.ie)
this.css.filter = 'alpha(opacity:'+op+')';
else if(bw.safari)
{
this.css.opacity = op/100;
this.css.KhtmlOpacity = op/100;
}
else if(bw.mz)
this.css.MozOpacity = op/100;
}
}
// check if input is a valid email address
// By Cal Henderson <cal@iamcal.com>
// http://code.iamcal.com/php/rfc822/
function rcube_check_email(input, inline)
{
if (input && window.RegExp)
{
var qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
var dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
var atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
var quoted_pair = '\\x5c[\\x00-\\x7f]';
var domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d';
var quoted_string = '\\x22('+qtext+'|'+quoted_pair+')*\\x22';
var sub_domain = '('+atom+'|'+domain_literal+')';
var word = '('+atom+'|'+quoted_string+')';
var domain = sub_domain+'(\\x2e'+sub_domain+')*';
var local_part = word+'(\\x2e'+word+')*';
var addr_spec = local_part+'\\x40'+domain;
var delim = '[,;\s\n]';
var reg1 = inline ? new RegExp('(^|<|'+delim+')'+addr_spec+'($|>|'+delim+')', 'i') : new RegExp('^'+addr_spec+'$', 'i');
return reg1.test(input) ? true : false;
}
return false;
}
// find a value in a specific array and returns the index
function find_in_array()
{
var args = find_in_array.arguments;
if(!args.length) return -1;
var haystack = typeof(args[0])=='object' ? args[0] : args.length>1 && typeof(args[1])=='object' ? args[1] : new Array();
var needle = typeof(args[0])!='object' ? args[0] : args.length>1 && typeof(args[1])!='object' ? args[1] : '';
var nocase = args.length==3 ? args[2] : false;
if(!haystack.length) return -1;
for(var i=0; i<haystack.length; i++)
if(nocase && haystack[i].toLowerCase()==needle.toLowerCase())
return i;
else if(haystack[i]==needle)
return i;
return -1;
}
// make a string URL safe
function urlencode(str)
{
return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
}
// get any type of html objects by id/name
function rcube_find_object(id, d)
{
var n, f, obj, e;
if(!d) d = document;
if(d.getElementsByName && (e = d.getElementsByName(id)))
obj = e[0];
if(!obj && d.getElementById)
obj = d.getElementById(id);
if(!obj && d.all)
obj = d.all[id];
if(!obj && d.images.length)
obj = d.images[id];
if(!obj && d.forms.length)
for(f=0; f<d.forms.length; f++)
{
if(d.forms[f].name == id)
obj = d.forms[f];
else if(d.forms[f].elements[id])
obj = d.forms[f].elements[id];
}
if(!obj && d.layers)
{
if(d.layers[id]) obj = d.layers[id];
for(n=0; !obj && n<d.layers.length; n++)
obj = rcube_find_object(id, d.layers[n].document);
}
return obj;
}
// return the absolute position of an object within the document
function rcube_get_object_pos(obj)
{
if(typeof(obj)=='string')
obj = rcube_find_object(obj);
if(!obj) return {x:0, y:0};
var iX = (bw.layers) ? obj.x : obj.offsetLeft;
var iY = (bw.layers) ? obj.y : obj.offsetTop;
if(bw.ie || bw.mz)
{
var elm = obj.offsetParent;
while(elm && elm!=null)
{
- iX += elm.offsetLeft;
- iY += elm.offsetTop;
+ iX += elm.offsetLeft - (elm.parentNode && elm.parentNode.scrollLeft ? elm.parentNode.scrollLeft : 0);
+ iY += elm.offsetTop - (elm.parentNode && elm.parentNode.scrollTop ? elm.parentNode.scrollTop : 0);
elm = elm.offsetParent;
}
}
- //if(bw.mac && bw.ie5) iX += document.body.leftMargin;
- //if(bw.mac && bw.ie5) iY += document.body.topMargin;
-
return {x:iX, y:iY};
}
+// determine whether the mouse is over the given object or not
+function rcube_mouse_is_over(ev, obj)
+{
+ var mouse = rcube_event.get_mouse_pos(ev);
+ var pos = rcube_get_object_pos(obj);
+
+ return ((mouse.x >= pos.x) && (mouse.x < (pos.x + obj.offsetWidth)) &&
+ (mouse.y >= pos.y) && (mouse.y < (pos.y + obj.offsetHeight)));
+}
+
/**
* Return the currently applied value of a css property
*
* @param {Object} html_element Node reference
* @param {String} css_property Property name to read in Javascript notation (eg. 'textAlign')
* @param {String} mozilla_equivalent Equivalent property name in CSS notation (eg. 'text-align')
* @return CSS property value
* @type String
*/
function get_elements_computed_style(html_element, css_property, mozilla_equivalent)
{
if (arguments.length==2)
mozilla_equivalent = css_property;
var el = html_element;
if (typeof(html_element)=='string')
el = rcube_find_object(html_element);
if (el && el.currentStyle)
return el.currentStyle[css_property];
else if (el && document.defaultView && document.defaultView.getComputedStyle)
return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozilla_equivalent);
else
return false;
}
// cookie functions by GoogieSpell
function setCookie(name, value, expires, path, domain, secure)
{
var curCookie = name + "=" + escape(value) +
(expires ? "; expires=" + expires.toGMTString() : "") +
(path ? "; path=" + path : "") +
(domain ? "; domain=" + domain : "") +
(secure ? "; secure" : "");
document.cookie = curCookie;
}
roundcube_browser.prototype.set_cookie = setCookie;
function getCookie(name)
{
var dc = document.cookie;
var prefix = name + "=";
var begin = dc.indexOf("; " + prefix);
if (begin == -1)
{
begin = dc.indexOf(prefix);
if (begin != 0) return null;
}
else
begin += 2;
var end = document.cookie.indexOf(";", begin);
if (end == -1)
end = dc.length;
return unescape(dc.substring(begin + prefix.length, end));
}
roundcube_browser.prototype.get_cookie = getCookie;
// tiny replacement for Firebox functionality
function rcube_console()
{
this.log = function(msg)
{
box = rcube_find_object('console');
if (box)
if (msg[msg.length-1]=='\n')
box.value += msg+'--------------------------------------\n';
else
box.value += msg+'\n--------------------------------------\n';
};
this.reset = function()
{
box = rcube_find_object('console');
if (box)
box.value = '';
};
}
var bw = new roundcube_browser();
if (!window.console)
console = new rcube_console();
// Add escape() method to RegExp object
// http://dev.rubyonrails.org/changeset/7271
RegExp.escape = function(str)
{
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}
diff --git a/program/js/list.js b/program/js/list.js
index f33c67c7b..926d98aa7 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -1,798 +1,803 @@
/*
+-----------------------------------------------------------------------+
| RoundCube List Widget |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2006-2008, RoundCube Dev, - Switzerland |
| Licensed under the GNU GPL |
| |
+-----------------------------------------------------------------------+
| Authors: Thomas Bruederli <roundcube@gmail.com> |
| Charles McNulty <charles@charlesmcnulty.com> |
+-----------------------------------------------------------------------+
| Requires: common.js |
+-----------------------------------------------------------------------+
$Id: list.js 344 2006-09-18 03:49:28Z thomasb $
*/
/**
* RoundCube List Widget class
* @contructor
*/
function rcube_list_widget(list, p)
{
// static contants
this.ENTER_KEY = 13;
this.DELETE_KEY = 46;
this.BACKSPACE_KEY = 8;
this.list = list ? list : null;
this.frame = null;
this.rows = [];
this.selection = [];
this.rowcount = 0;
this.subject_col = -1;
this.shiftkey = false;
this.multiselect = false;
this.multi_selecting = false;
this.draggable = false;
this.keyboard = false;
this.toggleselect = false;
this.dont_select = false;
this.drag_active = false;
this.last_selected = 0;
this.shift_start = 0;
this.in_selection_before = false;
this.focused = false;
this.drag_mouse_start = null;
this.dblclick_time = 600;
this.row_init = function(){};
- this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragend:[] };
+ this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragmove:[], dragend:[] };
// overwrite default paramaters
if (p && typeof(p)=='object')
for (var n in p)
this[n] = p[n];
}
rcube_list_widget.prototype = {
/**
* get all message rows from HTML table and init each row
*/
init: function()
{
if (this.list && this.list.tBodies[0])
{
this.rows = new Array();
this.rowcount = 0;
var row;
for(var r=0; r<this.list.tBodies[0].childNodes.length; r++)
{
row = this.list.tBodies[0].childNodes[r];
while (row && (row.nodeType != 1 || row.style.display == 'none'))
{
row = row.nextSibling;
r++;
}
this.init_row(row);
this.rowcount++;
}
this.frame = this.list.parentNode;
// set body events
if (this.keyboard) {
rcube_event.add_listener({element:document, event:'keyup', object:this, method:'key_press'});
rcube_event.add_listener({element:document, event:'keydown', object:this, method:'key_down'});
}
}
},
/**
*
*/
init_row: function(row)
{
// make references in internal array and set event handlers
if (row && String(row.id).match(/rcmrow([a-z0-9\-_=]+)/i))
{
var p = this;
var uid = RegExp.$1;
row.uid = uid;
this.rows[uid] = {uid:uid, id:row.id, obj:row, classname:row.className};
// set eventhandlers to table row
row.onmousedown = function(e){ return p.drag_row(e, this.uid); };
row.onmouseup = function(e){ return p.click_row(e, this.uid); };
if (document.all)
row.onselectstart = function() { return false; };
this.row_init(this.rows[uid]);
}
},
/**
*
*/
clear: function(sel)
{
var tbody = document.createElement('TBODY');
this.list.insertBefore(tbody, this.list.tBodies[0]);
this.list.removeChild(this.list.tBodies[1]);
this.rows = new Array();
this.rowcount = 0;
if (sel) this.clear_selection();
},
/**
* 'remove' message row from list (just hide it)
*/
remove_row: function(uid, sel_next)
{
if (this.rows[uid].obj)
this.rows[uid].obj.style.display = 'none';
if (sel_next)
this.select_next();
this.rows[uid] = null;
this.rowcount--;
},
/**
*
*/
insert_row: function(row, attop)
{
var tbody = this.list.tBodies[0];
if (attop && tbody.rows.length)
tbody.insertBefore(row, tbody.firstChild);
else
tbody.appendChild(row);
this.init_row(row);
this.rowcount++;
},
/**
* Set focur to the list
*/
focus: function(e)
{
this.focused = true;
for (var n=0; n<this.selection.length; n++)
{
id = this.selection[n];
if (this.rows[id] && this.rows[id].obj)
{
this.set_classname(this.rows[id].obj, 'selected', true);
this.set_classname(this.rows[id].obj, 'unfocused', false);
}
}
if (e || (e = window.event))
rcube_event.cancel(e);
},
/**
* remove focus from the list
*/
blur: function()
{
var id;
this.focused = false;
for (var n=0; n<this.selection.length; n++)
{
id = this.selection[n];
if (this.rows[id] && this.rows[id].obj)
{
this.set_classname(this.rows[id].obj, 'selected', false);
this.set_classname(this.rows[id].obj, 'unfocused', true);
}
}
},
/**
* onmousedown-handler of message list row
*/
drag_row: function(e, id)
{
// don't do anything (another action processed before)
var evtarget = rcube_event.get_target(e);
if (this.dont_select || (evtarget && (evtarget.tagName == 'INPUT' || evtarget.tagName == 'IMG')))
return false;
-
+
+ // accept right-clicks
+ if (rcube_event.get_button(e) == 2)
+ return true;
+
this.in_selection_before = this.in_selection(id) ? id : false;
// selects currently unselected row
if (!this.in_selection_before)
{
var mod_key = rcube_event.get_modifier(e);
this.select_row(id, mod_key, false);
}
if (this.draggable && this.selection.length)
{
this.drag_start = true;
this.drag_mouse_start = rcube_event.get_mouse_pos(e);
rcube_event.add_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'});
rcube_event.add_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'});
}
return false;
},
/**
* onmouseup-handler of message list row
*/
click_row: function(e, id)
{
var now = new Date().getTime();
var mod_key = rcube_event.get_modifier(e);
var evtarget = rcube_event.get_target(e);
if ((evtarget && (evtarget.tagName == 'INPUT' || evtarget.tagName == 'IMG')))
return false;
// don't do anything (another action processed before)
if (this.dont_select)
{
this.dont_select = false;
return false;
}
var dblclicked = now - this.rows[id].clicked < this.dblclick_time;
// unselects currently selected row
if (!this.drag_active && this.in_selection_before == id && !dblclicked)
this.select_row(id, mod_key, false);
this.drag_start = false;
this.in_selection_before = false;
// row was double clicked
if (this.rows && dblclicked && this.in_selection(id))
this.trigger_event('dblclick');
else
this.trigger_event('click');
if (!this.drag_active)
rcube_event.cancel(e);
this.rows[id].clicked = now;
return false;
},
/**
* get next and previous rows that are not hidden
*/
get_next_row: function()
{
if (!this.rows)
return false;
var last_selected_row = this.rows[this.last_selected];
var new_row = last_selected_row ? last_selected_row.obj.nextSibling : null;
while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none'))
new_row = new_row.nextSibling;
return new_row;
},
get_prev_row: function()
{
if (!this.rows)
return false;
var last_selected_row = this.rows[this.last_selected];
var new_row = last_selected_row ? last_selected_row.obj.previousSibling : null;
while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none'))
new_row = new_row.previousSibling;
return new_row;
},
// selects or unselects the proper row depending on the modifier key pressed
select_row: function(id, mod_key, with_mouse)
{
var select_before = this.selection.join(',');
if (!this.multiselect)
mod_key = 0;
if (!this.shift_start)
this.shift_start = id
if (!mod_key)
{
this.shift_start = id;
this.highlight_row(id, false);
this.multi_selecting = false;
}
else
{
switch (mod_key)
{
case SHIFT_KEY:
this.shift_select(id, false);
break;
case CONTROL_KEY:
if (!with_mouse)
this.highlight_row(id, true);
break;
case CONTROL_SHIFT_KEY:
this.shift_select(id, true);
break;
default:
this.highlight_row(id, false);
break;
}
this.multi_selecting = true;
}
// trigger event if selection changed
if (this.selection.join(',') != select_before)
this.trigger_event('select');
if (this.last_selected != 0 && this.rows[this.last_selected])
this.set_classname(this.rows[this.last_selected].obj, 'focused', false);
// unselect if toggleselect is active and the same row was clicked again
if (this.toggleselect && this.last_selected == id)
{
this.clear_selection();
id = null;
}
else
this.set_classname(this.rows[id].obj, 'focused', true);
if (!this.selection.length)
this.shift_start = null;
this.last_selected = id;
},
/**
* Alias method for select_row
*/
select: function(id)
{
this.select_row(id, false);
this.scrollto(id);
},
/**
* Select row next to the last selected one.
* Either below or above.
*/
select_next: function()
{
var next_row = this.get_next_row();
var prev_row = this.get_prev_row();
var new_row = (next_row) ? next_row : prev_row;
if (new_row)
this.select_row(new_row.uid, false, false);
},
/**
* Perform selection when shift key is pressed
*/
shift_select: function(id, control)
{
if (!this.rows[this.shift_start] || !this.selection.length)
this.shift_start = id;
var from_rowIndex = this.rows[this.shift_start].obj.rowIndex;
var to_rowIndex = this.rows[id].obj.rowIndex;
var i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex);
var j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex);
// iterate through the entire message list
for (var n in this.rows)
{
if ((this.rows[n].obj.rowIndex >= i) && (this.rows[n].obj.rowIndex <= j))
{
if (!this.in_selection(n))
this.highlight_row(n, true);
}
else
{
if (this.in_selection(n) && !control)
this.highlight_row(n, true);
}
}
},
/**
* Check if given id is part of the current selection
*/
in_selection: function(id)
{
for(var n in this.selection)
if (this.selection[n]==id)
return true;
return false;
},
/**
* Select each row in list
*/
select_all: function(filter)
{
if (!this.rows || !this.rows.length)
return false;
// reset but remember selection first
var select_before = this.selection.join(',');
this.clear_selection();
for (var n in this.rows)
{
if (!filter || this.rows[n][filter]==true)
{
this.last_selected = n;
this.highlight_row(n, true);
}
}
// trigger event if selection changed
if (this.selection.join(',') != select_before)
this.trigger_event('select');
this.focus();
return true;
},
/**
* Unselect all selected rows
*/
clear_selection: function()
{
var num_select = this.selection.length;
for (var n=0; n<this.selection.length; n++)
if (this.rows[this.selection[n]])
{
this.set_classname(this.rows[this.selection[n]].obj, 'selected', false);
this.set_classname(this.rows[this.selection[n]].obj, 'unfocused', false);
}
this.selection = new Array();
if (num_select)
this.trigger_event('select');
},
/**
* Getter for the selection array
*/
get_selection: function()
{
return this.selection;
},
/**
* Return the ID if only one row is selected
*/
get_single_selection: function()
{
if (this.selection.length == 1)
return this.selection[0];
else
return null;
},
/**
* Highlight/unhighlight a row
*/
highlight_row: function(id, multiple)
{
if (this.rows[id] && !multiple)
{
if (this.selection.length > 1 || !this.in_selection(id))
{
this.clear_selection();
this.selection[0] = id;
this.set_classname(this.rows[id].obj, 'selected', true);
}
}
else if (this.rows[id])
{
if (!this.in_selection(id)) // select row
{
this.selection[this.selection.length] = id;
this.set_classname(this.rows[id].obj, 'selected', true);
}
else // unselect row
{
var p = find_in_array(id, this.selection);
var a_pre = this.selection.slice(0, p);
var a_post = this.selection.slice(p+1, this.selection.length);
this.selection = a_pre.concat(a_post);
this.set_classname(this.rows[id].obj, 'selected', false);
this.set_classname(this.rows[id].obj, 'unfocused', false);
}
}
},
/**
* Handler for keyboard events
*/
key_press: function(e)
{
if (this.focused != true)
return true;
var keyCode = rcube_event.get_keycode(e);
var mod_key = rcube_event.get_modifier(e);
switch (keyCode)
{
case 40:
case 38:
case 63233: // "down", in safari keypress
case 63232: // "up", in safari keypress
// Stop propagation so that the browser doesn't scroll
rcube_event.cancel(e);
return this.use_arrow_key(keyCode, mod_key);
default:
this.shiftkey = e.shiftKey;
this.key_pressed = keyCode;
this.trigger_event('keypress');
- if (this.key_pressed == list.BACKSPACE_KEY)
+ if (this.key_pressed == this.BACKSPACE_KEY)
return rcube_event.cancel(e);
}
return true;
},
/**
* Handler for keydown events
*/
key_down: function(e)
{
switch (rcube_event.get_keycode(e))
{
case 40:
case 38:
case 63233:
case 63232:
if (!rcube_event.get_modifier(e) && this.focused)
return rcube_event.cancel(e);
default:
}
return true;
},
/**
* Special handling method for arrow keys
*/
use_arrow_key: function(keyCode, mod_key)
{
var new_row;
// Safari uses the nonstandard keycodes 63232/63233 for up/down, if we're
// using the keypress event (but not the keydown or keyup event).
if (keyCode == 40 || keyCode == 63233) // down arrow key pressed
new_row = this.get_next_row();
else if (keyCode == 38 || keyCode == 63232) // up arrow key pressed
new_row = this.get_prev_row();
if (new_row)
{
this.select_row(new_row.uid, mod_key, true);
this.scrollto(new_row.uid);
}
return false;
},
/**
* Try to scroll the list to make the specified row visible
*/
scrollto: function(id)
{
var row = this.rows[id].obj;
if (row && this.frame)
{
var scroll_to = Number(row.offsetTop);
if (scroll_to < Number(this.frame.scrollTop))
this.frame.scrollTop = scroll_to;
else if (scroll_to + Number(row.offsetHeight) > Number(this.frame.scrollTop) + Number(this.frame.offsetHeight))
this.frame.scrollTop = (scroll_to + Number(row.offsetHeight)) - Number(this.frame.offsetHeight);
}
},
/**
* Handler for mouse move events
*/
drag_mouse_move: function(e)
{
if (this.drag_start)
{
// check mouse movement, of less than 3 pixels, don't start dragging
var m = rcube_event.get_mouse_pos(e);
if (!this.drag_mouse_start || (Math.abs(m.x - this.drag_mouse_start.x) < 3 && Math.abs(m.y - this.drag_mouse_start.y) < 3))
return false;
if (!this.draglayer)
this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000});
// get subjects of selectedd messages
var names = '';
var c, i, node, subject, obj;
for(var n=0; n<this.selection.length; n++)
{
if (n>12) // only show 12 lines
{
names += '...';
break;
}
if (this.rows[this.selection[n]].obj)
{
obj = this.rows[this.selection[n]].obj;
subject = '';
for(c=0, i=0; i<obj.childNodes.length; i++)
{
if (obj.childNodes[i].nodeName == 'TD')
{
if (((node = obj.childNodes[i].firstChild) && (node.nodeType==3 || node.nodeName=='A')) &&
(this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c)))
{
subject = node.nodeType==3 ? node.data : node.innerHTML;
names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />';
break;
}
c++;
}
}
}
}
this.draglayer.write(names);
this.draglayer.show(1);
this.drag_active = true;
this.trigger_event('dragstart');
}
if (this.drag_active && this.draglayer)
{
var pos = rcube_event.get_mouse_pos(e);
this.draglayer.move(pos.x+20, pos.y-5);
+ this.trigger_event('dragmove', e);
}
this.drag_start = false;
return false;
},
/**
* Handler for mouse up events
*/
drag_mouse_up: function(e)
{
document.onmousemove = null;
if (this.draglayer && this.draglayer.visible)
this.draglayer.show(0);
this.drag_active = false;
this.trigger_event('dragend');
rcube_event.remove_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'});
rcube_event.remove_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'});
this.focus();
return rcube_event.cancel(e);
},
/**
* set/unset a specific class name
*/
set_classname: function(obj, classname, set)
{
var reg = new RegExp('\s*'+classname, 'i');
if (!set && obj.className.match(reg))
obj.className = obj.className.replace(reg, '');
else if (set && !obj.className.match(reg))
obj.className += ' '+classname;
},
/**
* Setter for object event handlers
*
* @param {String} Event name
* @param {Function} Handler function
* @return Listener ID (used to remove this handler later on)
*/
addEventListener: function(evt, handler)
{
if (this.events[evt]) {
var handle = this.events[evt].length;
this.events[evt][handle] = handler;
return handle;
}
else
return false;
},
/**
* Removes a specific event listener
*
* @param {String} Event name
* @param {Int} Listener ID to remove
*/
removeEventListener: function(evt, handle)
{
if (this.events[evt] && this.events[evt][handle])
this.events[evt][handle] = null;
},
/**
* This will execute all registered event handlers
* @private
*/
-trigger_event: function(evt)
+trigger_event: function(evt, p)
{
if (this.events[evt] && this.events[evt].length) {
for (var i=0; i<this.events[evt].length; i++)
if (typeof(this.events[evt][i]) == 'function')
- this.events[evt][i](this);
+ this.events[evt][i](this, p);
}
}
};
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index 43d117430..98cf29f10 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -1,247 +1,217 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/addressbook/func.inc |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005-2007, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Provide addressbook functionality and GUI objects |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
// instantiate a contacts object according to the given source
$CONTACTS = $RCMAIL->get_address_book(($source = get_input_value('_source', RCUBE_INPUT_GPC)));
$CONTACTS->set_pagesize($CONFIG['pagesize']);
// set list properties and session vars
if (!empty($_GET['_page']))
$CONTACTS->set_page(($_SESSION['page'] = intval($_GET['_page'])));
else
$CONTACTS->set_page(isset($_SESSION['page']) ?$_SESSION['page'] : 1);
// set message set for search result
if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
$CONTACTS->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
// set data source env
$OUTPUT->set_env('source', $source ? $source : '0');
$OUTPUT->set_env('readonly', $CONTACTS->readonly, false);
// add list of address sources to client env
$js_list = array();
if (strtolower($CONFIG['address_book_type']) != 'ldap') {
// We are using the DB address book, add it.
$js_list = array("0" => array('id' => 0, 'readonly' => false));
}
if (is_array($CONFIG['ldap_public'])) {
foreach ($CONFIG['ldap_public'] as $id => $prop)
$js_list[$id] = array('id' => $id, 'readonly' => !$prop['writable']);
}
$OUTPUT->set_env('address_sources', $js_list);
function rcmail_directory_list($attrib)
{
global $CONFIG, $OUTPUT;
if (!$attrib['id'])
$attrib['id'] = 'rcmdirectorylist';
+ $out = '';
$local_id = '0';
$current = get_input_value('_source', RCUBE_INPUT_GPC);
- $line_templ = '<li id="%s" class="%s"><a href="%s"' .
- ' onclick="return %s.command(\'list\',\'%s\',this)"' .
- ' onmouseover="return %s.focus_folder(\'%s\')"' .
- ' onmouseout="return %s.unfocus_folder(\'%s\')"' .
- ' onmouseup="return %s.folder_mouse_up(\'%s\')">%s'.
- "</a></li>\n";
+ $line_templ = html::tag('li', array('id' => 'rcmli%s', 'class' => '%s'),
+ html::a(array('href' => '%s', 'onclick' => "return ".JS_OBJECT_NAME.".command('list','%s',this)"), '%s'));
- // allow the following attributes to be added to the <ul> tag
- $out = '<ul' . create_attrib_string($attrib, array('style', 'class', 'id')) . ">\n";
if (strtolower($CONFIG['address_book_type']) != 'ldap') {
- $out .= sprintf($line_templ,
- 'rcmli'.$local_id,
- !$current ? 'selected' : '',
- Q(rcmail_url(null, array('_source' => 0))),
- JS_OBJECT_NAME,
- $local_id,
- JS_OBJECT_NAME,
- $local_id,
- JS_OBJECT_NAME,
- $local_id,
- JS_OBJECT_NAME,
- $local_id,
- rcube_label('personaladrbook'));
+ $out .= sprintf($line_templ, $local_id, (!$current ? 'selected' : ''),
+ Q(rcmail_url(null, array('_source' => $local_id))), $local_id, rcube_label('personaladrbook'));
} // end if
else {
// DB address book not used, see if a source is set, if not use the
// first LDAP directory.
if (!$current) {
$current = key((array)$CONFIG['ldap_public']);
} // end if
} // end else
- foreach ((array)$CONFIG['ldap_public'] as $id => $prop)
- {
+ foreach ((array)$CONFIG['ldap_public'] as $id => $prop) {
$js_id = JQ($id);
$dom_id = preg_replace('/[^a-z0-9\-_]/i', '', $id);
- $out .= sprintf($line_templ,
- 'rcmli'.$dom_id,
- $current == $id ? 'selected' : '',
- Q(rcmail_url(null, array('_source' => $id))),
- JS_OBJECT_NAME,
- $js_id,
- JS_OBJECT_NAME,
- $js_id,
- JS_OBJECT_NAME,
- $js_id,
- JS_OBJECT_NAME,
- $js_id,
- !empty($prop['name']) ? Q($prop['name']) : Q($id));
+ $out .= sprintf($line_templ, $dom_id, ($current == $id ? 'selected' : ''),
+ Q(rcmail_url(null, array('_source' => $id))), $js_id, (!empty($prop['name']) ? Q($prop['name']) : Q($id)));
}
- $out .= '</ul>';
-
$OUTPUT->add_gui_object('folderlist', $attrib['id']);
- return $out;
+ return html::tag('ul', $attrib, $out, html::$common_attrib);
}
// return the message list as HTML table
function rcmail_contacts_list($attrib)
{
global $CONTACTS, $OUTPUT;
// count contacts for this user
$result = $CONTACTS->list_records();
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcmAddressList';
// define list of cols to be displayed
$a_show_cols = array('name');
// create XHTML table
$out = rcube_table_output($attrib, $result->records, $a_show_cols, $CONTACTS->primary_key);
// set client env
$OUTPUT->add_gui_object('contactslist', $attrib['id']);
$OUTPUT->set_env('current_page', (int)$CONTACTS->list_page);
$OUTPUT->set_env('pagecount', ceil($result->count/$CONTACTS->page_size));
$OUTPUT->include_script('list.js');
// add some labels to client
rcube_add_label('deletecontactconfirm');
return $out;
}
function rcmail_js_contacts_list($result, $prefix='')
{
global $OUTPUT;
if (empty($result) || $result->count == 0)
return;
// define list of cols to be displayed
$a_show_cols = array('name');
while ($row = $result->next())
{
$a_row_cols = array();
// format each col
foreach ($a_show_cols as $col)
$a_row_cols[$col] = Q($row[$col]);
$OUTPUT->command($prefix.'add_contact_row', $row['ID'], $a_row_cols);
}
}
// similar function as /steps/settings/identities.inc::rcmail_identity_frame()
function rcmail_contact_frame($attrib)
{
global $OUTPUT;
if (!$attrib['id'])
$attrib['id'] = 'rcmcontactframe';
$attrib['name'] = $attrib['id'];
$attrib_str = create_attrib_string($attrib, array('name', 'id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
$OUTPUT->set_env('contentframe', $attrib['name']);
$OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
return '<iframe'. $attrib_str . '></iframe>';
}
function rcmail_rowcount_display($attrib)
{
global $OUTPUT;
if (!$attrib['id'])
$attrib['id'] = 'rcmcountdisplay';
$OUTPUT->add_gui_object('countdisplay', $attrib['id']);
// allow the following attributes to be added to the <span> tag
$attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
$out = '<span' . $attrib_str . '>';
$out .= rcmail_get_rowcount_text();
$out .= '</span>';
return $out;
}
function rcmail_get_rowcount_text()
{
global $CONTACTS;
// read nr of contacts
$result = $CONTACTS->get_result();
if (!$result)
$result = $CONTACTS->count();
if ($result->count == 0)
$out = rcube_label('nocontactsfound');
else
$out = rcube_label(array(
'name' => 'contactsfromto',
'vars' => array(
'from' => $result->first + 1,
'to' => min($result->count, $result->first + $CONTACTS->page_size),
'count' => $result->count)
));
return $out;
}
$OUTPUT->set_pagetitle(rcube_label('addressbook'));
// register UI objects
$OUTPUT->add_handlers(array(
'directorylist' => 'rcmail_directory_list',
'addresslist' => 'rcmail_contacts_list',
'addressframe' => 'rcmail_contact_frame',
'recordscountdisplay' => 'rcmail_rowcount_display',
'searchform' => array($OUTPUT, 'search_form')
));
?>
diff --git a/skins/default/addresses.css b/skins/default/addresses.css
index 27612c304..8ae298781 100644
--- a/skins/default/addresses.css
+++ b/skins/default/addresses.css
@@ -1,197 +1,200 @@
/***** RoundCube|Mail address book task styles *****/
#abooktoolbar
{
position: absolute;
top: 45px;
left: 200px;
height: 35px;
}
#abooktoolbar a
{
padding-right: 10px;
}
#abookcountbar
{
position: absolute;
bottom: 16px;
left: 185px;
width: 240px;
height: 20px;
text-align: left;
}
#abookcountbar span
{
font-size: 11px;
color: #333333;
}
#directorylist, #addresslist, #importbox
{
position: absolute;
top: 85px;
bottom: 40px;
border: 1px solid #999999;
background-color: #F9F9F9;
overflow: auto;
/* css hack for IE */
height: expression((parseInt(document.documentElement.clientHeight)-135)+'px');
}
#directorylist
{
left: 20px;
- width: 150px;
+ width: 170px;
+ overflow: hidden;
}
#addresslist
{
- left: 185px;
+ left: 200px;
width: 340px;
}
#importbox
{
left: 20px;
right: 40px;
height: auto;
bottom: auto;
padding-bottom: 4ex;
}
#importbox a
{
color: blue;
}
#directorylist ul
{
list-style: none;
margin: 0;
padding: 0;
}
#directorylist ul li
{
font-size: 11px;
border-bottom: 1px solid #EBEBEB;
+ white-space: nowrap;
}
#directorylist ul li a
{
display: block;
padding-left: 6px;
padding-top: 2px;
padding-bottom: 2px;
text-decoration: none;
+ white-space: nowrap;
}
#directorylist li.selected
{
background-color: #929292;
border-bottom: 1px solid #898989;
}
#directorylist li.selected a
{
color: #FFF;
font-weight: bold;
}
#directorylist li.droptarget
{
background-color: #FFFFA6;
}
#contacts-table
{
width: 100%;
table-layout: fixed;
/* css hack for IE */
width: expression(document.getElementById('addresslist').clientWidth);
}
#contacts-table tbody td
{
cursor: default;
}
#contacts-box
{
position: absolute;
top: 85px;
- left: 540px;
+ left: 555px;
right: 20px;
bottom: 40px;
border: 1px solid #999999;
overflow: hidden;
/* css hack for IE */
width: expression((parseInt(document.documentElement.clientWidth)-45-document.getElementById('addresslist').offsetLeft-document.getElementById('addresslist').offsetWidth)+'px');
height: expression((parseInt(document.documentElement.clientHeight)-135)+'px');
}
#addressviewsplitter
{
background-position: 4px center;
}
#addressviewsplitter .splitterLine
{
margin-left: 3px;
width: 6px;
}
body.iframe,
#contact-frame
{
background-color: #F9F9F9;
}
#contact-frame
{
border: none;
/* visibility: hidden; */
}
#contact-title, #groups-title
{
height: 12px !important;
/* height: 20px; */
padding: 4px 5px 3px 5px;
border-bottom: 1px solid #999;
color: #333;
font-size: 11px;
font-weight: bold;
background-color: #EBEBEB;
background-image: url(images/listheader_aqua.gif);
white-space: nowrap;
}
#contact-title
{
padding: 4px 10px 3px 10px;
}
#contact-details
{
padding: 15px 10px 10px 10px;
}
#contact-details table td.title
{
color: #666;
font-weight: bold;
text-align: right;
padding-right: 10px;
}
diff --git a/skins/default/mail.css b/skins/default/mail.css
index 2de775688..7807ca231 100644
--- a/skins/default/mail.css
+++ b/skins/default/mail.css
@@ -1,946 +1,951 @@
/***** RoundCube|Mail mail task styles *****/
#messagetoolbar
{
position: absolute;
top: 45px;
left: 200px;
right: 200px;
height: 35px;
white-space: nowrap;
/* border: 1px solid #cccccc; */
}
#messagetoolbar a
{
padding-right: 10px;
}
#messagetoolbar select
{
font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
font-size: 11px;
color: #333333;
}
#messagetoolbar select.mboxlist
{
position: absolute;
left: 375px;
top: 10px;
}
#messagetoolbar select.mboxlist option
{
padding-left: 15px;
}
#messagetoolbar select.mboxlist option[value="0"]
{
padding-left: 2px;
}
#markmessagemenu
{
position: absolute;
top: 32px;
left: 90px;
width: auto;
visibility: hidden;
background-color: #F9F9F9;
border: 1px solid #CCC;
padding: 1px;
opacity: 0.9;
z-index: 240;
}
ul.toolbarmenu
{
margin: 0;
padding: 0;
list-style: none;
}
ul.toolbarmenu li
{
font-size: 11px;
white-space: nowrap;
min-width: 130px;
width: auto !important;
width: 130px;
}
ul.toolbarmenu li a
{
display: block;
color: #a0a0a0;
padding: 2px 8px 3px 12px;
text-decoration: none;
}
ul.toolbarmenu li a.active:hover
{
background-color: #ddd;
}
#listcontrols
{
position: absolute;
left: 200px;
bottom: 20px;
height: 16px;
width: 500px;
font-size: 11px;
}
#listcontrols a,
#listcontrols a:active,
#listcontrols a:visited,
#mailboxcontrols a,
#mailboxcontrols a:active,
#mailboxcontrols a:visited,
td.formlinks a,
td.formlinks a:visited
{
color: #999999;
font-size: 11px;
text-decoration: none;
}
#listcontrols a.active,
#listcontrols a.active:active,
#listcontrols a.active:visited,
#mailboxcontrols a.active,
#mailboxcontrols a.active:active,
#mailboxcontrols a.active:visited,
ul.toolbarmenu li a.active,
ul.toolbarmenu li a.active:active,
ul.toolbarmenu li a.active:visited,
td.formlinks a,
td.formlinks a:visited
{
color: #CC0000;
}
#listcontrols a.active:hover,
#mailboxcontrols a.active:hover
{
text-decoration: underline;
}
#messagecountbar
{
position: absolute;
bottom: 16px;
right: 20px;
width: 300px;
height: 20px;
text-align: right;
white-space: nowrap;
}
#messagecountbar span
{
font-size: 11px;
color: #333333;
}
#messagepartcontainer
{
position: absolute;
top: 80px;
left: 20px;
right: 20px;
bottom: 20px;
/* css hack for IE */
width: expression((parseInt(document.documentElement.clientWidth)-40)+'px');
height: expression((parseInt(document.documentElement.clientHeight)-100)+'px');
}
#mailcontframe
{
position: absolute;
top: 85px;
left: 200px;
right: 20px;
bottom: 40px;
border: 1px solid #999999;
background-color: #F9F9F9;
overflow: auto;
/* css hack for IE */
width: expression((parseInt(document.documentElement.clientWidth)-220)+'px');
height: expression((parseInt(document.documentElement.clientHeight)-125)+'px');
}
#mailpreviewframe
{
position: absolute;
top: 305px;
left: 200px;
right: 20px;
bottom: 40px;
border: 1px solid #999999;
background-color: #F9F9F9;
/* css hack for IE */
width: expression((parseInt(document.documentElement.clientWidth)-220)+'px');
height: expression((parseInt(document.documentElement.clientHeight)-135-document.getElementById('mailcontframe').offsetHeight)+'px');
}
#messagecontframe
{
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
/* css hack for IE */
width: expression((parseInt(document.documentElement.clientWidth)-220)+'px');
height: expression((parseInt(document.documentElement.clientHeight)-135-document.getElementById('mailcontframe').offsetHeight)+'px');
}
#messagepartframe
{
width: 100%;
height: 100%;
border: 1px solid #999999;
background-color: #F9F9F9;
}
#partheader
{
position: absolute;
top: 10px;
left: 220px;
right: 20px;
height: 40px;
/* css hack for IE */
width: expression((parseInt(document.documentElement.clientWidth)-240)+'px');
}
#partheader table td
{
padding-left: 2px;
padding-right: 4px;
vertical-align: middle;
font-size: 11px;
}
#partheader table td.title
{
color: #666666;
font-weight: bold;
}
/** mailbox list styles */
#mailboxlist-header
{
display: block;
height: 12px;
margin: 0;
padding: 3px 10px 4px 10px;
background-color: #EBEBEB;
background-image: url(images/listheader_aqua.gif);
border-bottom: 1px solid #999;
color: #333333;
font-size: 11px;
font-weight: bold;
}
#mailboxlist-container
{
position: absolute;
top: 85px;
left: 20px;
width: 170px;
bottom: 40px;
border: 1px solid #999;
background-color: #F9F9F9;
overflow: auto;
/* css hack for IE */
height: expression((parseInt(document.documentElement.clientHeight)-125)+'px');
}
#mailboxlist
{
position:relative;
width: 100%;
height: auto;
margin: 0px;
padding: 0px;
list-style-image: none;
list-style-type: none;
overflow: hidden;
white-space: nowrap;
}
#mailboxlist li
{
display: block;
position: relative;
font-size: 11px;
background: url(images/icons/folder-closed.png) no-repeat;
background-position: 5px 1px;
border-bottom: 1px solid #EBEBEB;
}
#mailboxlist li div
{
position: absolute;
left: 8px !important;
left: -16px;
top: 2px;
width: 14px;
height: 16px;
}
#mailboxlist li div.collapsed,
#mailboxlist li div.expanded
{
cursor: pointer;
}
#mailboxlist li div.collapsed
{
background: url(images/icons/collapsed.png) bottom right no-repeat;
}
#mailboxlist li div.expanded
{
background: url(images/icons/expanded.png) bottom right no-repeat;
}
#mailboxlist li.inbox
{
background-image: url(images/icons/folder-inbox.png);
}
#mailboxlist li.drafts
{
background-image: url(images/icons/folder-drafts.png);
}
#mailboxlist li.sent
{
background-image: url(images/icons/folder-sent.png);
}
#mailboxlist li.junk
{
background-image: url(images/icons/folder-junk.png);
}
#mailboxlist li.trash
{
background-image: url(images/icons/folder-trash.png);
}
#mailboxlist li a
{
cursor: default;
display: block;
position: relative;
padding-left: 25px;
padding-top: 2px;
padding-bottom: 2px;
text-decoration: none;
}
#mailboxlist li.unread
{
font-weight: bold;
}
+#mailboxlist li.virtual > a
+{
+ color: #666;
+}
+
#mailboxlist li.selected,
#mailboxlist li.droptarget li.selected
{
background-color: #929292;
}
#mailboxlist li.selected > a,
#mailboxlist li.droptarget li.selected a
{
color: #FFF;
font-weight: bold;
}
#mailboxlist li.droptarget
{
background-color: #FFFFA6;
}
/* styles for nested folders */
#mailboxlist ul {
list-style: none;
padding: 0;
margin:0;
border-top: 1px solid #EBEBEB;
padding-left: 15px;
background-position: 25px 1px;
background-color: #F9F9F9;
color: blue;
font-weight: normal;
}
#mailboxcontrols
{
position: absolute;
left: 20px;
width: 185px;
bottom: 20px;
height: 16px;
overflow: hidden;
font-size: 11px;
}
/** message list styles */
body.messagelist
{
margin: 0px;
background-color: #F9F9F9;
}
#messagelist
{
width: 100%;
display: table;
table-layout: fixed;
/* css hack for IE */
width: expression(parseInt(document.getElementById('mailcontframe').clientWidth)+'px');
}
/* safari hack \*/
html>body*#messagelist[id$="messagelist"]:not([class="none"]) { table-layout: auto; }
/**/
#messagelist thead tr td
{
height: 20px;
padding-top: 0px;
padding-bottom: 0px;
padding-left: 2px;
padding-right: 4px;
vertical-align: middle;
border-bottom: 1px solid #999999;
color: #333333;
background-color: #EBEBEB;
background-image: url(images/listheader_aqua.gif);
font-size: 11px;
font-weight: bold;
}
#messagelist thead tr td.sortedASC,
#messagelist thead tr td.sortedDESC
{
background-image: url(images/listheader_dark.gif);
}
#messagelist thead tr td.sortedASC a
{
background: url(images/sort_asc.gif) top right no-repeat;
}
#messagelist thead tr td.sortedDESC a
{
background: url(images/sort_desc.gif) top right no-repeat;
}
#messagelist thead tr td a
{
display: block;
width: auto !important;
width: 100%;
color: #333333;
text-decoration: none;
}
#messagelist tbody tr td
{
height: 16px;
padding: 2px;
padding-right: 4px;
font-size: 11px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
border-bottom: 1px solid #EBEBEB;
cursor: default;
}
#messagelist tbody tr td a
{
color: #000;
text-decoration: none;
white-space: nowrap;
cursor: inherit;
}
#messagelist col
{
display: table-column;
text-align: left;
vertical-align: middle;
}
#messagelist tr td.icon,
#messagelist tr td.flag
{
width: 16px;
vertical-align: middle;
cursor: pointer;
}
#messagelist tr td.subject
{
overflow: hidden;
vertical-align: middle;
}
#messagelist tr td.size
{
width: 70px;
text-align: right;
vertical-align: middle;
}
#messagelist tr td.from,
#messagelist tr td.to
{
width: 180px;
vertical-align: middle;
}
#messagelist tr td.date
{
width: 118px;
vertical-align: middle;
}
#messagelist tr.message
{
background-color: #FFFFFF;
}
/*
#messagelist tr.odd
{
background-color: #F9F9F9;
}
*/
#messagelist tr.unread
{
font-weight: bold;
background-color: #FFFFFF;
}
#messagelist tr.selected td
{
color: #FFFFFF;
background-color: #CC3333;
}
#messagelist tr.unfocused td
{
color: #FFFFFF;
background-color: #929292;
}
#messagelist tr.selected td a
{
color: #FFFFFF;
}
#messagelist tr.unfocused td a
{
color: #FFFFFF;
}
#messagelist tr.deleted td a
{
color: #CCCCCC;
}
#messagelist tr.deleted td,
#messagelist tr.deleted td a
{
color: #CCCCCC;
}
#quotadisplay
{
color: #666666;
font-size: 11px;
}
#quotadisplay img
{
vertical-align: middle;
margin-left: 4px;
border: 1px solid #666666;
}
/** message view styles */
#messageframe
{
position: absolute;
top: 85px;
left: 200px;
right: 20px;
bottom: 40px;
border: 1px solid #999;
background-color: #FFF;
overflow: auto;
/* css hack for IE */
/* margin-bottom: 10px; */
width: expression((parseInt(document.documentElement.clientWidth)-220)+'px');
height: expression((parseInt(document.documentElement.clientHeight)-125)+'px');
}
div.messageheaderbox
{
margin: 6px 8px 0px 8px;
border: 1px solid #ccc;
}
table.headers-table
{
width: 100%;
background-color: #EBEBEB;
table-layout: fixed;
}
#messagebody table.headers-table
{
width: auto;
margin: 6px 8px;
background-color: #F4F4F4;
border: 1px solid #ccc;
}
table.headers-table tr td
{
font-size: 11px;
border-bottom:1px solid #FFFFFF;
}
table.headers-table td.header-title
{
width: 80px;
color: #666666;
font-weight: bold;
text-align: right;
white-space: nowrap;
padding-right: 4px;
}
table.headers-table tr td.subject
{
width: 95%;
font-weight: bold;
}
#attachment-list
{
margin: 0px;
padding: 0px 0px 0px 72px;
min-height: 16px;
list-style-image: none;
list-style-type: none;
background: url(images/icons/attachment.png) 60px 2px no-repeat #DFDFDF;
/* IE6 hack */
_height: expression(Math.min(16, parseInt(document.documentElement.clientHeight))+'px');
}
#attachment-list:after
{
content: ".";
display: block;
height: 0;
font-size: 0;
clear: both;
visibility: hidden;
}
#attachment-list li
{
float: left;
height: 18px;
font-size: 11px;
padding: 2px 0px 0px 15px;
white-space: nowrap;
}
#attachment-list li a
{
text-decoration: none;
}
#attachment-list li a:hover
{
text-decoration: underline;
}
#messagebody
{
position:relative;
min-height: 300px;
padding-bottom: 10px;
background-color: #FFFFFF;
}
div.message-part
{
padding: 10px 8px;
border-top: 1px solid #ccc;
/* overflow: hidden; */
}
#messagebody div:first-child
{
border-top: 0;
}
div.message-part a,
div.message-htmlpart a
{
color: #0000CC;
}
div.message-part div.pre
{
margin: 0px;
padding: 0px;
font-family: monospace;
white-space: -moz-pre-wrap !important;
white-space: pre;
word-wrap: break-word; /* IE (and Safari) */
}
div.message-part blockquote
{
color: blue;
border-left: 2px solid blue;
border-right: 2px solid blue;
background-color: #F6F6F6;
margin: 2px 0px 2px 0px;
padding: 1px 8px 1px 10px;
}
div.message-part blockquote blockquote
{
color: green;
border-left: 2px solid green;
border-right: 2px solid green;
}
div.message-part blockquote blockquote blockquote
{
color: #990000;
border-left: 2px solid #bb0000;
border-right: 2px solid #bb0000;
}
body.iframe div.message-htmlpart
{
margin: 8px;
}
div.message-htmlpart div.rcmBody
{
margin: 8px;
}
#remote-objects-message
{
display: none;
margin: 8px;
min-height: 20px;
padding: 10px 10px 6px 46px;
}
#remote-objects-message a
{
color: #666666;
padding-left: 10px;
}
#remote-objects-message a:hover
{
color: #333333;
}
/** message compose styles */
#compose-toolbar
{
white-space: nowrap;
}
#priority-selector,
#receipt-selector
{
padding-left: 30px;
white-space: nowrap;
}
#compose-container
{
position: absolute;
top: 90px;
left: 200px;
right: 25px;
bottom: 30px;
padding: 0px;
margin: 0px;
/* css hack for IE */
width: expression((parseInt(document.documentElement.clientWidth)-220)+'px');
height: expression((parseInt(document.documentElement.clientHeight)-130)+'px');
}
/*
#compose-headers
{
position: absolute;
top: 70px;
left: 200px;
height: 84px;
border-top: 1px solid #cccccc;
overflow: auto;
}
#compose-headers td
{
padding-top: 1px;
padding-bottom: 1px;
border-right: 1px solid #cccccc;
border-bottom: 1px solid #cccccc;
}
*/
#compose-headers
{
width: 100%;
}
/*
#compose-headers td
{
width: 100%;
}
*/
#compose-headers td.top
{
vertical-align: top;
}
#compose-headers td.title,
#compose-subject td.title
{
width: 80px !important;
color: #666666;
font-size: 11px;
font-weight: bold;
padding-right: 10px;
white-space: nowrap;
}
#compose-body,
#compose-headers td textarea,
#compose-headers td input
{
width: 100%;
width: expression('99%');
}
#compose-headers td textarea
{
height: 38px;
}
#compose-cc,
#compose-bcc,
#compose-replyto
{
display: none;
}
#compose-body
{
margin-top: 5px;
margin-bottom: 10px;
height: 90%;
min-height: 280px;
font-size: 9pt;
font-family: "Courier New", Courier, monospace;
}
#compose-attachments
{
position: absolute;
top: 100px;
left: 20px;
width: 160px;
}
#compose-attachments ul
{
margin: 0px;
padding: 0px;
border: 1px solid #CCCCCC;
background-color: #F9F9F9;
list-style-image: none;
list-style-type: none;
}
#compose-attachments ul li
{
height: 18px;
font-size: 11px;
padding-left: 2px;
padding-top: 2px;
padding-right: 4px;
border-bottom: 1px solid #EBEBEB;
white-space: nowrap;
overflow: hidden;
}
#attachment-title
{
background: url(images/icons/attachment.png) top left no-repeat;
padding: 0px 0px 3px 22px;
}
#attachment-form
{
position: absolute;
top: 150px;
left: 20px;
z-index: 200;
padding: 6px;
visibility: hidden;
border: 1px solid #CCCCCC;
background-color: #F9F9F9;
}
#attachment-form div
{
padding: 2px;
}
#attachment-form div.buttons
{
margin-top: 4px;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Mar 1, 8:47 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166476
Default Alt Text
(220 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment