thomascube
2011-07-30 fcc7f861b170596c6970aecb1ddc87a3567b112f
program/js/app.js
@@ -138,14 +138,6 @@
      return;
    }
    // Enable debug console
    if (!window.console || !window.console.log) {
      window.console = new rcube_console();
    }
    else {
      $('#console').hide();
    }
    // find all registered gui containers
    for (var n in this.gui_containers)
      this.gui_containers[n] = $('#'+this.gui_containers[n]);
@@ -209,7 +201,7 @@
          $(this.gui_objects.qsearchbox).focusin(function() { rcmail.message_list.blur(); });
        }
        if (this.env.trash_mailbox && this.env.mailbox != this.env.trash_mailbox)
        if (!this.env.flag_for_deletion && this.env.trash_mailbox && this.env.mailbox != this.env.trash_mailbox)
          this.set_alttext('delete', 'movemessagetotrash');
        this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', 'forward',
@@ -260,7 +252,10 @@
        }
        // show printing dialog
        else if (this.env.action == 'print' && this.env.uid)
          window.print();
          if (bw.safari)
            window.setTimeout('window.print()', 10);
          else
            window.print();
        // get unread count for each mailbox
        if (this.gui_objects.mailboxlist) {
@@ -338,6 +333,10 @@
        this.enable_command('add', 'import', this.env.writable_source);
        this.enable_command('list', 'listgroup', 'advanced-search', true);
        // load contacts of selected source
        if (!this.env.action)
          this.command('list', this.env.source);
        break;
@@ -433,6 +432,11 @@
    this.start_keepalive();
  };
  this.log = function(msg)
  {
    if (window.console && console.log)
      console.log(msg);
  };
  /*********************************************************/
  /*********       client command interface        *********/
@@ -526,7 +530,7 @@
          this.list_mailbox(props);
          if (this.env.trash_mailbox)
          if (this.env.trash_mailbox && !this.env.flag_for_deletion)
            this.set_alttext('delete', this.env.mailbox != this.env.trash_mailbox ? 'movemessagetotrash' : 'deletemessage');
        }
        else if (this.task == 'addressbook') {
@@ -1876,9 +1880,7 @@
    if (action == 'preview' && String(target.location.href).indexOf(url) >= 0)
      this.show_contentframe(true);
    else {
      if (!this.env.frame_lock) {
        (this.is_framed() ? parent.rcmail : this).env.frame_lock = this.set_busy(true, 'loading');
      }
      this.lock_frame();
      this.location_href(this.env.comm_path+url, target);
      // mark as read and change mbox unread counter
@@ -1911,6 +1913,12 @@
    if (!show && this.busy)
      this.set_busy(false, null, this.env.frame_lock);
  };
  this.lock_frame = function()
  {
    if (!this.env.frame_lock)
      (this.is_framed() ? parent.rcmail : this).env.frame_lock = this.set_busy(true, 'loading');
  };
  // list a specific page
@@ -2867,12 +2875,21 @@
      input_subject = $("input[name='_subject']"),
      input_message = $("[name='_message']").get(0),
      html_mode = $("input[name='_is_html']").val() == '1',
      ac_fields = ['cc', 'bcc', 'replyto', 'followupto'];
      ac_fields = ['cc', 'bcc', 'replyto', 'followupto'],
      ac_props;
    // configure parallel autocompletion
    if (this.env.autocomplete_threads > 0) {
      ac_props = {
        threads: this.env.autocomplete_threads,
        sources: this.env.autocomplete_sources,
      };
    }
    // init live search events
    this.init_address_input_events(input_to);
    this.init_address_input_events(input_to, ac_props);
    for (var i in ac_fields) {
      this.init_address_input_events($("[name='_"+ac_fields[i]+"']"));
      this.init_address_input_events($("[name='_"+ac_fields[i]+"']"), ac_props);
    }
    if (!html_mode) {
@@ -2900,9 +2917,9 @@
    this.auto_save_start();
  };
  this.init_address_input_events = function(obj)
  this.init_address_input_events = function(obj, props)
  {
    obj[bw.ie || bw.safari || bw.chrome ? 'keydown' : 'keypress'](function(e){ return ref.ksearch_keydown(e, this); })
    obj[bw.ie || bw.safari || bw.chrome ? 'keydown' : 'keypress'](function(e) { return ref.ksearch_keydown(e, this, props); })
      .attr('autocomplete', 'off');
  };
@@ -3437,14 +3454,14 @@
  /*********************************************************/
  // handler for keyboard events on address-fields
  this.ksearch_keydown = function(e, obj)
  this.ksearch_keydown = function(e, obj, props)
  {
    if (this.ksearch_timer)
      clearTimeout(this.ksearch_timer);
    var highlight;
    var key = rcube_event.get_keycode(e);
    var mod = rcube_event.get_modifier(e);
    var highlight,
      key = rcube_event.get_keycode(e),
      mod = rcube_event.get_modifier(e);
    switch (key) {
      case 38:  // key up
@@ -3467,8 +3484,8 @@
        if (mod == SHIFT_KEY)
          break;
     case 13:  // enter
        if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value)
      case 13:  // enter
        if (this.ksearch_selected === null || !this.ksearch_value)
          break;
        // insert selected address and hide ksearch pane
@@ -3488,7 +3505,7 @@
    }
    // start timer
    this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(); }, 200);
    this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(props); }, 200);
    this.ksearch_input = obj;
    return true;
@@ -3516,21 +3533,25 @@
    var inp_value = this.ksearch_input.value,
      cpos = this.get_caret_pos(this.ksearch_input),
      p = inp_value.lastIndexOf(this.ksearch_value, cpos),
      trigger = false,
      insert = '',
      // replace search string with full address
      pre = inp_value.substring(0, p),
      end = inp_value.substring(p+this.ksearch_value.length, inp_value.length);
    this.ksearch_destroy();
    // insert all members of a group
    if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].id) {
      insert += this.env.contacts[id].name + ', ';
      this.group2expand = $.extend({}, this.env.contacts[id]);
      this.group2expand.input = this.ksearch_input;
      this.http_request('group-expand', '_source='+urlencode(this.env.contacts[id].source)+'&_gid='+urlencode(this.env.contacts[id].id), false);
      this.http_request('mail/group-expand', '_source='+urlencode(this.env.contacts[id].source)+'&_gid='+urlencode(this.env.contacts[id].id), false);
    }
    else if (typeof this.env.contacts[id] === 'string')
    else if (typeof this.env.contacts[id] === 'string') {
      insert = this.env.contacts[id] + ', ';
      trigger = true;
    }
    this.ksearch_input.value = pre + insert + end;
@@ -3538,18 +3559,22 @@
    cpos = p+insert.length;
    if (this.ksearch_input.setSelectionRange)
      this.ksearch_input.setSelectionRange(cpos, cpos);
    if (trigger)
      this.triggerEvent('autocomplete_insert', { field:this.ksearch_input, insert:insert });
  };
  this.replace_group_recipients = function(id, recipients)
  {
    if (this.group2expand && this.group2expand.id == id) {
      this.group2expand.input.value = this.group2expand.input.value.replace(this.group2expand.name, recipients);
      this.triggerEvent('autocomplete_insert', { field:this.group2expand.input, insert:recipients });
      this.group2expand = null;
    }
  };
  // address search processor
  this.ksearch_get_results = function()
  this.ksearch_get_results = function(props)
  {
    var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
@@ -3594,59 +3619,102 @@
    if (old_value && old_value.length && this.env.contacts && !this.env.contacts.length && q.indexOf(old_value) == 0)
      return;
    var lock = this.display_message(this.get_label('searching'), 'loading');
    this.http_post('autocomplete', '_search='+urlencode(q), lock);
    this.ksearch_destroy();
    var i, lock, source, xhr, reqid = new Date().getTime(),
      threads = props && props.threads ? props.threads : 1,
      sources = props && props.sources ? props.sources : [],
      action = props && props.action ? props.action : 'mail/autocomplete';
    this.ksearch_data = {id: reqid, sources: sources.slice(), action: action, locks: [], requests: []};
    for (i=0; i<threads; i++) {
      source = this.ksearch_data.sources.shift();
      if (threads > 1 && source === null)
        break;
      lock = this.display_message(this.get_label('searching'), 'loading');
      xhr = this.http_post(action, '_search='+urlencode(q)+'&_id='+reqid
        + (source ? '&_source='+urlencode(source) : ''), lock);
      this.ksearch_data.locks.push(lock);
      this.ksearch_data.requests.push(xhr);
    }
  };
  this.ksearch_query_results = function(results, search)
  this.ksearch_query_results = function(results, search, reqid)
  {
    // ignore this outdated search response
    if (this.ksearch_value && search != this.ksearch_value)
    if (this.ksearch_input && this.ksearch_value && search != this.ksearch_value)
      return;
    this.env.contacts = results ? results : [];
    this.ksearch_display_results(this.env.contacts);
  };
  this.ksearch_display_results = function (a_results)
  {
    // display search results
    if (a_results.length && this.ksearch_input && this.ksearch_value) {
      var p, ul, li, text, s_val = this.ksearch_value;
    var p, ul, li, text, init, s_val = this.ksearch_value,
      maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15;
      // create results pane if not present
      if (!this.ksearch_pane) {
        ul = $('<ul>');
        this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane').css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
        this.ksearch_pane.__ul = ul[0];
      }
    // create results pane if not present
    if (!this.ksearch_pane) {
      ul = $('<ul>');
      this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane')
        .css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
      this.ksearch_pane.__ul = ul[0];
    }
      // remove all search results
      ul = this.ksearch_pane.__ul;
    ul = this.ksearch_pane.__ul;
    // remove all search results or add to existing list if parallel search
    if (reqid && this.ksearch_pane.data('reqid') == reqid) {
      maxlen -= ul.childNodes.length;
    }
    else {
      this.ksearch_pane.data('reqid', reqid);
      init = 1;
      // reset content
      ul.innerHTML = '';
      this.env.contacts = [];
      // move the results pane right under the input box
      var pos = $(this.ksearch_input).offset();
      this.ksearch_pane.css({ left:pos.left+'px', top:(pos.top + this.ksearch_input.offsetHeight)+'px', display: 'none'});
    }
      // add each result line to list
      for (i=0; i < a_results.length; i++) {
        text = typeof a_results[i] === 'object' ? a_results[i].name : a_results[i];
    // add each result line to list
    if (results && results.length) {
      for (i=0; i < results.length && maxlen > 0; i++) {
        text = typeof results[i] === 'object' ? results[i].name : results[i];
        li = document.createElement('LI');
        li.innerHTML = text.replace(new RegExp('('+RegExp.escape(s_val)+')', 'ig'), '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
        li.onmouseover = function(){ ref.ksearch_select(this); };
        li.onmouseup = function(){ ref.ksearch_click(this) };
        li._rcm_id = i;
        ul.appendChild(li);
        maxlen -= 1;
      }
      // select the first
      $(ul.firstChild).attr('id', 'rcmksearchSelected').addClass('selected');
      this.ksearch_selected = 0;
      // move the results pane right under the input box and make it visible
      var pos = $(this.ksearch_input).offset();
      this.ksearch_pane.css({ left:pos.left+'px', top:(pos.top + this.ksearch_input.offsetHeight)+'px' }).show();
    }
    // hide results pane
    else
      this.ksearch_hide();
    if (ul.childNodes.length) {
      this.ksearch_pane.show();
      // select the first
      if (!this.env.contacts.length) {
        $('li:first', ul).attr('id', 'rcmksearchSelected').addClass('selected');
        this.ksearch_selected = 0;
      }
    }
    if (results && results.length)
      this.env.contacts = this.env.contacts.concat(results);
    // run next parallel search
    if (maxlen > 0 && this.ksearch_data.id == reqid && this.ksearch_data.sources.length) {
      var lock, xhr, props = this.ksearch_data, source = props.sources.shift();
      if (source) {
        lock = this.display_message(this.get_label('searching'), 'loading');
        xhr = this.http_post(props.action, '_search='+urlencode(s_val)+'&_id='+reqid
          +'&_source='+urlencode(source), lock);
        this.ksearch_data.locks.push(lock);
        this.ksearch_data.requests.push(xhr);
      }
    }
  };
  this.ksearch_click = function(node)
@@ -3663,20 +3731,36 @@
    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;
    this.ksearch_value = '';
    if (this.ksearch_pane)
      this.ksearch_pane.hide();
   };
    this.ksearch_destroy();
  };
  // Aborts pending autocomplete requests
  this.ksearch_destroy = function()
  {
    var i, len, ac = this.ksearch_data;
    if (!ac)
      return;
    for (i=0, len=ac.locks.length; i<len; i++) {
      this.hide_message(ac.locks[i]); // hide loading message
      ac.requests[i].abort(); // abort ajax request
    }
    this.ksearch_data = null;
  }
  /*********************************************************/
  /*********         address book methods          *********/
@@ -3826,8 +3910,10 @@
      if (this.env.group)
        add_url += '&_gid='+urlencode(this.env.group);
      this.set_busy(true);
      this.location_href(this.env.comm_path+'&_action='+action+'&_source='+urlencode(this.env.source)+'&_cid='+urlencode(cid) + add_url, target);
      this.lock_frame();
      this.location_href(this.env.comm_path+'&_action='+action
        +'&_source='+urlencode(this.env.source)
        +'&_cid='+urlencode(cid) + add_url, target);
    }
    return true;
  };
@@ -4347,6 +4433,7 @@
      this.contact_list.clear_selection();
    }
    this.lock_frame();
    this.location_href(this.env.comm_path+'&_action=search'+add_url, target);
    return true;
@@ -4380,6 +4467,7 @@
        add_url = '&_framed=1';
        target = window.frames[this.env.contentframe];
      }
      this.lock_frame();
      this.location_href(this.env.comm_path+'&_action=edit-prefs&_section='+id+add_url, target);
    }
@@ -4449,6 +4537,10 @@
      row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
    };
    this.subscription_list.init();
    $('#mailboxroot')
      .mouseover(function(){ p.focus_subscription(this.id); })
      .mouseout(function(){ p.unfocus_subscription(this.id); })
      .mouseup(function(){ if (p.drag_active) p.subscription_move_folder(); });
  };
  this.focus_subscription = function(id)
@@ -4467,6 +4559,10 @@
          this.set_env('dstfolder', folder);
          $(row).addClass('droptarget');
        }
      }
      else if (id == 'mailboxroot') {
        this.set_env('dstfolder', '');
        $(row).addClass('droptarget');
      }
      else if (this.env.mailbox.match(new RegExp(delim))) {
        this.set_env('dstfolder', this.env.delimiter);
@@ -4512,11 +4608,13 @@
        (this.env.dstfolder != this.env.mailbox.replace(reg, ''))
    ) {
      reg = new RegExp('[^'+delim+']*['+delim+']', 'g');
      var lock = this.set_busy(true, 'foldermoving'),
        basename = this.env.mailbox.replace(reg, ''),
      var basename = this.env.mailbox.replace(reg, ''),
        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.mailbox)+'&_folder_newname='+urlencode(newname), lock);
      if (newname != this.env.mailbox) {
        this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.mailbox)+'&_folder_newname='+urlencode(newname), this.set_busy(true, 'foldermoving'));
        this.subscription_list.draglayer.hide();
      }
    }
    this.drag_active = false;
    this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
@@ -4546,9 +4644,9 @@
    if (!this.gui_objects.subscriptionlist)
      return false;
    var row, n, i, tmp, folders, len, list = [], slist = [],
    var row, n, i, tmp, folders, rowid, list = [], slist = [],
      tbody = this.gui_objects.subscriptionlist.tBodies[0],
      refrow = $('tr', tbody).get(0),
      refrow = $('tr', tbody).get(1),
      id = 'rcmrow'+((new Date).getTime());
    if (!refrow) {
@@ -4575,7 +4673,10 @@
    this.env.subscriptionrows[id] = [name, display_name, 0];
    // sort folders, to find a place where to insert the row
    folders = this.env.subscriptionrows;
    folders = [];
    $.each(this.env.subscriptionrows, function(k,v){ folders.push(v) });
    folders.sort(function(a,b){ return a[0] < b[0] ? -1 : (a[0] > b[0] ? 1 : 0) });
    for (n in folders) {
      // protected folder
      if (folders[n][2]) {
@@ -4591,19 +4692,22 @@
        tmp = null;
      }
    }
    list.sort();
    // make sure protected folders (and their subs) are on top
    list = slist.concat(list);
    // check if subfolder of a protected folder
    for (n=0; n<slist.length; n++) {
      if (name.indexOf(slist[n]+this.env.delimiter) == 0)
        rowid = this.get_folder_row_id(slist[n]);
    }
    // find folder position after sorting
    for (n=0, len=list.length; n<len; n++) {
      if (list[n] == name)
        break;
    for (n=0; !rowid && n<list.length; n++) {
      if (n && list[n] == name)
        rowid = this.get_folder_row_id(list[n-1]);
    }
    // add row to the table
    if (n && n < len)
      $('#'+this.get_folder_row_id(list[n-1])).after(row);
    if (rowid)
      $('#'+rowid).after(row);
    else
      row.appendTo(tbody);
@@ -4765,9 +4869,7 @@
      this.show_contentframe(true);
    }
    else {
      if (!this.env.frame_lock) {
        (parent.rcmail ? parent.rcmail : this).env.frame_lock = this.set_busy(true, 'loading');
      }
      this.lock_frame();
      this.location_href(this.env.comm_path+url, target);
    }
  };
@@ -5051,7 +5153,8 @@
      obj.click(function() { return ref.hide_message(obj); });
    }
    window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
    if (timeout > 0)
      window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
    return id;
  };
@@ -5409,11 +5512,11 @@
      url = '?_task=utils&_action=html2text',
      lock = this.set_busy(true, 'converting');
    console.log('HTTP POST: ' + url);
    this.log('HTTP POST: ' + url);
    $.ajax({ type: 'POST', url: url, data: htmlText, contentType: 'application/octet-stream',
      error: function(o, status, err) { rcmail.http_error(o, status, err, lock); },
      success: function(data) { rcmail.set_busy(false, null, lock); $(document.getElementById(id)).val(data); console.log(data); }
      success: function(data) { rcmail.set_busy(false, null, lock); $(document.getElementById(id)).val(data); rcmail.log(data); }
    });
  };
@@ -5428,7 +5531,7 @@
  /********************************************************/
  /*********        remote request methods        *********/
  /********************************************************/
  // compose a valid url with the given parameters
  this.url = function(action, query)
  {
@@ -5506,8 +5609,9 @@
    url += '&_remote=1';
    // send request
    console.log('HTTP GET: ' + url);
    $.ajax({
    this.log('HTTP GET: ' + url);
    return $.ajax({
      type: 'GET', url: url, data: { _unlock:(lock?lock:0) }, dataType: 'json',
      success: function(data){ ref.http_response(data); },
      error: function(o, status, err) { rcmail.http_error(o, status, err, lock); }
@@ -5537,8 +5641,9 @@
    }
    // send request
    console.log('HTTP POST: ' + url);
    $.ajax({
    this.log('HTTP POST: ' + url);
    return $.ajax({
      type: 'POST', url: url, data: postdata, dataType: 'json',
      success: function(data){ ref.http_response(data); },
      error: function(o, status, err) { rcmail.http_error(o, status, err, lock); }
@@ -5570,7 +5675,7 @@
    // if we get javascript code from server -> execute it
    if (response.exec) {
      console.log(response.exec);
      this.log(response.exec);
      eval(response.exec);
    }
@@ -5685,7 +5790,7 @@
      if (!field.length) {
        field = $('<input>').attr({type: 'hidden', name: fname});
        field.appendTo(form);
        field.prependTo(form);
      }
      field.val(ts);
@@ -5836,11 +5941,12 @@
      if (elm.type == 'hidden')
        continue;
      // remember which elem was disabled before lock
      if (lock && elm.disabled)
        this.disabled_form_elements.push(elm);
      else if (lock || $.inArray(elm, this.disabled_form_elements)<0)
      // check this.disabled_form_elements before inArray() as a workaround for FF5 bug
      // http://bugs.jquery.com/ticket/9873
      else if (lock || (this.disabled_form_elements && $.inArray(elm, this.disabled_form_elements)<0))
        elm.disabled = lock;
    }
  };