From f0ecee1e5851ecd027b3ffb959cc6b9de3a056f0 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 14 Apr 2014 18:25:23 +0200 Subject: [PATCH] Implement full notes display and editing from mail view --- plugins/kolab_notes/kolab_notes.php | 170 ++++++++++++++++-- plugins/kolab_notes/localization/en_US.inc | 3 +- plugins/kolab_notes/notes.js | 85 +++++---- plugins/kolab_notes/skins/larry/notes.css | 56 +++++- plugins/kolab_notes/skins/larry/sprites.png | Bin 2411 -> 4754 bytes .../skins/larry/templates/notes.html | 10 ++ 6 files changed, 272 insertions(+), 52 deletions(-) diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php index 3be3ab90..2c478558 100644 --- a/plugins/kolab_notes/kolab_notes.php +++ b/plugins/kolab_notes/kolab_notes.php @@ -34,6 +34,7 @@ class kolab_notes extends rcube_plugin private $lists; private $folders; private $cache = array(); + private $message_notes = array(); /** * Required startup method of a Roundcube plugin @@ -68,6 +69,8 @@ class kolab_notes extends rcube_plugin $this->rc->load_language($_SESSION['language'], array('notes.notes' => $this->gettext('navtitle'))); // add label for task title if ($args['task'] == 'notes') { + $this->add_hook('storage_init', array($this, 'storage_init')); + // register task actions $this->register_action('index', array($this, 'notes_view')); $this->register_action('fetch', array($this, 'notes_fetch')); @@ -77,6 +80,8 @@ class kolab_notes extends rcube_plugin $this->register_action('dialog-ui', array($this, 'dialog_view')); } else if ($args['task'] == 'mail') { + $this->add_hook('storage_init', array($this, 'storage_init')); + $this->add_hook('message_load', array($this, 'mail_message_load')); $this->add_hook('message_compose', array($this, 'mail_message_compose')); $this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html')); @@ -93,18 +98,35 @@ class kolab_notes extends rcube_plugin ))), 'messagemenu'); - $this->api->output->add_label('kolab_notes.appendnote', 'save', 'cancel'); + $this->api->output->add_label('kolab_notes.appendnote', 'kolab_notes.editnote', 'kolab_notes.deletenotesconfirm', 'kolab_notes.entertitle', 'save', 'delete', 'cancel', 'close'); $this->include_script('notes_mail.js'); } } if (!$this->rc->output->ajax_call && (!$this->rc->output->env['framed'] || in_array($args['action'], array('folder-acl','dialog-ui')))) { - require_once($this->home . '/kolab_notes_ui.php'); - $this->ui = new kolab_notes_ui($this); - $this->ui->init(); + $this->load_ui(); } } + /** + * Hook into IMAP FETCH HEADER.FIELDS command and request MESSAGE-ID + */ + public function storage_init($p) + { + $p['fetch_headers'] = trim($p['fetch_headers'] . ' MESSAGE-ID'); + return $p; + } + + /** + * Load and initialize UI class + */ + private function load_ui() + { + require_once($this->home . '/kolab_notes_ui.php'); + $this->ui = new kolab_notes_ui($this); + $this->ui->init(); + } + /** * Read available calendars for the current user and store them internally */ @@ -205,8 +227,8 @@ class kolab_notes extends rcube_plugin // attempt to create a default folder for this user if (empty($this->lists)) { - #if ($this->create_list(array('name' => 'Tasks', 'color' => '0000CC', 'default' => true))) - # $this->_read_lists(true); + $folder = kolab_storage::folder_update(array('name' => 'Notes', 'type' => 'note', 'default' => true, 'subscribed' => true)); + $this->_read_lists(true); } return $this->lists; @@ -234,10 +256,10 @@ class kolab_notes extends rcube_plugin // resolve message reference if ($msgref = rcube_utils::get_input_value('_msg', RCUBE_INPUT_GPC, true)) { $storage = $this->rc->get_storage(); - $storage->set_options(array('all_headers' => true)); list($uid, $folder) = explode('-', $msgref, 2); if ($message = $storage->get_message_headers($msgref)) { $this->rc->output->set_env('kolab_notes_template', array( + '_from_mail' => true, 'title' => $message->get('subject'), 'links' => array(array( 'uri' => $this->get_message_uri($message, $folder), @@ -346,7 +368,7 @@ class kolab_notes extends rcube_plugin // encode for client use if (is_array($data)) { - $this->_client_encode($data); + $this->_client_encode($data, true); } $this->rc->output->command('plugin.render_note', $data); @@ -393,7 +415,7 @@ class kolab_notes extends rcube_plugin /** * Helper method to encode the given note record for use in the client */ - private function _client_encode(&$note) + private function _client_encode(&$note, $resolve = false) { foreach ($note as $key => $prop) { if ($key[0] == '_' || $key == 'x-custom') { @@ -413,6 +435,14 @@ class kolab_notes extends rcube_plugin $note['html'] = $this->_wash_html($note['description']); } + // resolve message links + if (is_array($note['links'])) { + $me = $this; + $note['links'] = array_map(function($link) use ($me, $resolve){ + return $me->get_message_reference($link, $resolve) ?: array('uri' => $link); + }, $note['links']); + } + return $note; } @@ -680,11 +710,47 @@ class kolab_notes extends rcube_plugin } /** - * Handler for 'messagebody_html' hooks + * Lookup backend storage and find notes tagged with the given message-ID + */ + public function mail_message_load($p) + { + $this->message = $p['object']; + $this->message_notes = array(); + + // TODO: only query for notes if message was flagged with $KolabNotes ? + + $message_id = trim($p['object']->headers->messageID, '<> '); + if ($message_id && $p['object']->uid) { + $query = array(array('tags','=','ref:' . $message_id)); + foreach (kolab_storage::select($query, 'note') as $record) { + $record['list'] = kolab_storage::folder_id($record['_mailbox']); + $this->message_notes[] = $record; + } + } + } + + /** + * Handler for 'messagebody_html' hook */ public function mail_messagebody_html($args) { - + $html = ''; + foreach ($this->message_notes as $note) { + $html .= html::a(array( + 'href' => $this->rc->url(array('task' => 'notes', '_list' => $note['list'], '_id' => $note['uid'])), + 'class' => 'kolabnotesref', + 'rel' => $note['uid'] . '@' . $note['list'], + 'target' => '_blank', + ), Q($note['title'])); + } + + // prepend note links to message body + if ($html) { + $this->load_ui(); + $args['content'] = html::div('kolabmessagenotes', $html) . $args['content']; + } + + return $args; } /** @@ -714,7 +780,7 @@ class kolab_notes extends rcube_plugin 'Subject' => $note['title'], 'Date' => $note['changed']->format('r'), )); - console($note); + if ($this->is_html($note)) { $message->setHTMLBody($note['description']); @@ -735,9 +801,79 @@ class kolab_notes extends rcube_plugin return $message->getMessage(); } + /** + * Build a URI representing the given message reference + */ private function get_message_uri($headers, $folder) { - return sprintf('imap://%s/%s#%s', $headers->folder ?: $folder, $headers->uid, urlencode($headers->messageID)); + return sprintf('imap://%s/%s?message-id=%s&subject=%s', + $headers->folder ?: $folder, + $headers->uid, + urlencode($headers->messageID), + urlencode($headers->get('subject')) + ); + } + + /** + * Extract message reference components from the given URI + */ + private function parse_message_uri($uri) + { + $url = parse_url($uri); + if ($url['scheme'] == 'imap') { + parse_str($url['query'], $param); + $linkref = array( + 'uri' => $uri, + 'message_id' => $param['message-id'] ?: urldecode($url['fragment']), + 'subject' => $param['subject'], + ); + + $path = explode('/', $url['host'] . $url['path']); + $linkref['msguid'] = array_pop($path); + $linkref['folder'] = join('/', $path); + + return $linkref; + } + + return false; + } + + /** + * Resolve the email message reference from the given URI + */ + public function get_message_reference($uri, $resolve = false) + { + if ($linkref = $this->parse_message_uri($uri)) { + // fetch message subject + if ($resolve || empty($linkref['subject'])) { + $imap = $this->rc->get_storage(); + if (!($message = $imap->get_message_headers($linrkef['msguid'], $linkref['folder']))) { + // try to find message using the message_id fragment + $index = $imap->search_once($imap->list_folders_subscribed('', '*', 'mail', null, true), 'HEADER MESSAGE-ID ' . $linkref['message_id']); + if ($index->count()) { + $uid = reset($index->get()); + $folder = $index->get_parameters('MAILBOX'); + if ($message = $imap->get_message_headers($uid, $folder)) { + // replace metadata + $linkref['subject'] = $message->get('subject'); + $linkref['msguid'] = $message->uid; + $linkref['folder'] = $message->folder; + $linkref['uri'] = $this->get_message_uri($message, $folder); + } + } + } + } + + $linkref['mailurl'] = $this->rc->url(array( + 'task' => 'mail', + 'action' => 'show', + 'mbox' => $linkref['folder'], + 'uid' => $linkref['msguid'], + 'rel' => 'note', + )); + } + + return $linkref; } /** @@ -749,6 +885,14 @@ class kolab_notes extends rcube_plugin // TODO: handle attachments + // convert link references into simple URIs + if (array_key_exists('links', $note)) { + $object['links'] = array_map(function($link){ return is_array($link) ? $link['uri'] : strval($link); }, $note['links']); + } + else { + $object['links'] = $old['links']; + } + // clean up HTML content $object['description'] = $this->_wash_html($note['description']); $is_html = true; diff --git a/plugins/kolab_notes/localization/en_US.inc b/plugins/kolab_notes/localization/en_US.inc index ccb737d1..5e3d1bbe 100644 --- a/plugins/kolab_notes/localization/en_US.inc +++ b/plugins/kolab_notes/localization/en_US.inc @@ -24,7 +24,8 @@ $labels['tabsharing'] = 'Sharing'; $labels['discard'] = 'Discard'; $labels['abort'] = 'Abort'; $labels['unsavedchanges'] = 'Unsaved Changes!'; -$labels['appendnote'] = 'Add a note'; +$labels['appendnote'] = 'Add a Note'; +$labels['editnote'] = 'Edit Note'; $labels['savein'] = 'Save in'; $labels['savingdata'] = 'Saving data...'; diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js index c05be5a0..41dcac15 100644 --- a/plugins/kolab_notes/notes.js +++ b/plugins/kolab_notes/notes.js @@ -236,6 +236,7 @@ function rcube_kolab_notes_ui(settings) function init_dialog() { rcmail.register_command('save', save_note, true); + rcmail.addEventListener('plugin.render_note', render_note); rcmail.addEventListener('plugin.update_note', function(data){ data.id = rcmail.html_identifier_encode(data.uid); notesdata[data.id] = data; @@ -259,19 +260,24 @@ function rcube_kolab_notes_ui(settings) } init_editor(); - setTimeout(function(){ - me.selected_note = $.extend({ - list: me.selected_list, - uid: null, - title: rcmail.gettext('newnote','kolab_notes'), - description: '', - categories: [], - created: rcmail.gettext('now', 'kolab_notes'), - changed: rcmail.gettext('now', 'kolab_notes') - }, rcmail.env.kolab_notes_template || {}); - render_note(me.selected_note); - - }, 100); + if (settings.selected_uid) { + me.selected_list = settings.selected_list; + edit_note(settings.selected_uid); + } + else { + setTimeout(function(){ + me.selected_note = $.extend({ + list: me.selected_list, + uid: null, + title: rcmail.gettext('newnote','kolab_notes'), + description: '', + categories: [], + created: rcmail.gettext('now', 'kolab_notes'), + changed: rcmail.gettext('now', 'kolab_notes') + }, rcmail.env.kolab_notes_template || {}); + render_note(me.selected_note); + }, 100); + } } this.init_dialog = init_dialog; @@ -319,16 +325,7 @@ function rcube_kolab_notes_ui(settings) // register click handler for message links $(rcmail.gui_objects.notesattachmentslist).on('click', 'li a.messagelink', function(){ - var uri = $(this).attr('rel'); - if (uri && uri.match(/imap:\/\/.+/)) { - var path = uri.split('#')[0].substr(7).split('/'), - uid = path.pop(), - folder = path.join('/'); - if (folder && uid) { - rcmail.open_window(rcmail.url('mail/show', { _mbox: folder, _uid: uid, _rel: 'note' })); - } - } - + rcmail.open_window(this.href); return false; }); } @@ -715,13 +712,14 @@ function rcube_kolab_notes_ui(settings) return; } - var list = me.notebooks[data.list] || me.notebooks[me.selected_list]; + var list = me.notebooks[data.list] || me.notebooks[me.selected_list] || {}; content = $('#notecontent').val(data.description), readonly = data.readonly || !list.editable, attachmentslist = $(rcmail.gui_objects.notesattachmentslist).html(''); $('.notetitle', rcmail.gui_objects.noteviewtitle).val(data.title).prop('disabled', readonly); $('.dates .notecreated', rcmail.gui_objects.noteviewtitle).html(Q(data.created || '')); $('.dates .notechanged', rcmail.gui_objects.noteviewtitle).html(Q(data.changed || '')); + $(rcmail.gui_objects.notebooks).filter('select').val(list.id); if (data.created || data.changed) { $('.dates', rcmail.gui_objects.noteviewtitle).show(); } @@ -753,15 +751,24 @@ function rcube_kolab_notes_ui(settings) if (data.links) { $.each(data.links, function(i, link){ - $('
  • ').addClass('link') + var li = $('
  • ').addClass('link') .addClass('message eml') .append($('') - .attr('href', '#show') - .attr('rel', link.uri) + .attr('href', link.mailurl) .addClass('messagelink') .html(Q(link.subject || link.message_id || link.uri)) ) .appendTo(attachmentslist); +/* + if (!readonly && !data._from_mail) { + $('') + .attr('href', '#delete') + .attr('title', rcmail.gettext('delete')) + .addClass('delete') + .html(rcmail.gettext('delete')) + .appendTo(li); + } +*/ }); } @@ -770,6 +777,9 @@ function rcube_kolab_notes_ui(settings) .on('click', function(){ $('.tagline .placeholder').hide(); }); } + if (!data.list) + data.list = list.id; + me.selected_note = data; me.selected_note.id = rcmail.html_identifier_encode(data.uid); rcmail.enable_command('save', list.editable && !data.readonly); @@ -781,7 +791,7 @@ function rcube_kolab_notes_ui(settings) html = text2html(html); } - var node, editor = tinyMCE.get('notecontent'); + var node, editor = tinyMCE.get('notecontent'), is_html = false; if (!readonly && editor) { $(rcmail.gui_objects.notesdetailview).hide(); $(rcmail.gui_objects.noteseditform).show(); @@ -795,13 +805,17 @@ function rcube_kolab_notes_ui(settings) $('.notetitle', rcmail.gui_objects.noteviewtitle).focus().select(); // read possibly re-formatted content back from editor for later comparison - me.selected_note.description = editor.getContent({ format:'html' }) + me.selected_note.description = editor.getContent({ format:'html' }); + is_html = true; } else { $(rcmail.gui_objects.noteseditform).hide(); $(rcmail.gui_objects.notesdetailview).html(html).show(); } + // notify subscribers + rcmail.triggerEvent('kolab_notes_render', { data:data, readonly:readonly, html:is_html }); + // Trigger resize (needed for proper editor resizing) $(window).resize(); } @@ -1010,6 +1024,11 @@ function rcube_kolab_notes_ui(settings) if (me.selected_note.links) savedata.links = me.selected_note.links; + // add reference to old list if changed + if (me.selected_note.list && savedata.list != me.selected_note.list) { + savedata._fromlist = me.selected_note.list; + } + // do some input validation if (savedata.title == '') { alert(rcmail.gettext('entertitle', 'kolab_notes')); @@ -1032,11 +1051,12 @@ function rcube_kolab_notes_ui(settings) */ function get_save_data() { - var editor = tinyMCE.get('notecontent'); + var editor = tinyMCE.get('notecontent'), + listselect = $('option:selected', rcmail.gui_objects.notebooks); var savedata = { title: trim($('.notetitle', rcmail.gui_objects.noteviewtitle).val()), description: editor ? editor.getContent({ format:'html' }) : $('#notecontent').val(), - list: me.selected_note.list || me.selected_list, + list: listselect.length ? listselect.val() : me.selected_note.list || me.selected_list, uid: me.selected_note.uid, categories: [] }; @@ -1068,7 +1088,8 @@ function rcube_kolab_notes_ui(settings) return savedata.title != me.selected_note.title || savedata.description != me.selected_note.description - || savedata.categories.join(',') != (me.selected_note.categories || []).join(','); + || savedata.categories.join(',') != (me.selected_note.categories || []).join(',') + || savedata.list != me.selected_note.list; } /** diff --git a/plugins/kolab_notes/skins/larry/notes.css b/plugins/kolab_notes/skins/larry/notes.css index 96d283b4..96e962f8 100644 --- a/plugins/kolab_notes/skins/larry/notes.css +++ b/plugins/kolab_notes/skins/larry/notes.css @@ -103,7 +103,7 @@ } .notesview .toolbarmenu.iconized .selected span.icon { - background: url(sprites.png) -5px -109px no-repeat; + background: url(sprites.png) -4px -110px no-repeat; } .notesview #notedetailsbox { @@ -137,16 +137,18 @@ width: 100%; } -.notesdialog #noteform { - bottom: 30px; -} - .notesview #notedetails { padding: 8px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; + border-bottom: 1px solid #dfdfdf; +} + +.notesdialog #noteform, +.notesdialog #notedetails { + bottom: 30px; } .notesview #notedetails pre { @@ -266,12 +268,16 @@ position: absolute; left: 0; right: 0; - bottom: 0; + bottom: 41px; height: 27px; background: #fff; padding-left: 10px; } +.notesdialog #notereferences { + bottom: 0; +} + .notesview #notebooks li { margin: 0; height: 20px; @@ -369,4 +375,42 @@ -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; +} + +.notesview .attachmentslist li.message.eml { + display: inline-block; + background-image: url(sprites.png); + background-position: -6px -128px; + margin-right: 1em; +} + +.notesview .attachmentslist li.message a.messagelink { + padding-left: 24px; +} + +ul.toolbarmenu li .appendnote span.icon { + background-image: url(sprites.png); + background-position: -4px -170px; +} + +div.kolabmessagenotes { + margin: 8px 8px; + padding: 4px 8px; + border: 1px solid #dfdfdf; + background: #fafafa; + border-radius: 4px; +} + +div.kolabmessagenotes a.kolabnotesref { + display: inline-block; + color: #333; + font-weight: bold; + padding: 3px 15px 2px 22px; + text-shadow: 0px 1px 1px #fff; + text-decoration: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 12em; + background: url(sprites.png) -6px -151px no-repeat; } \ No newline at end of file diff --git a/plugins/kolab_notes/skins/larry/sprites.png b/plugins/kolab_notes/skins/larry/sprites.png index 184cef4151e861dbafba883f1e2d60b0d7caa77f..62aff656470b3ee9b1c45f5edc5ae1240afd56e2 100644 GIT binary patch literal 4754 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 W$Rjq%&U^Hi$Ux5o^Hj(2`hNhwD_JW5 literal 2411 zcmV-x36%DUP)w-V4mXaTfLNu)6`0ePSl(hAW+3lb8DF%dKbEGeSU zL88JQ5rIWTEZwAyLTHmpr948rELGMGx@5GpH6z(TSrW&N6F=hD-Txf(F}0 z>r+!xedo@dtKGkU|9{ABkPt8v&59K(_A+QV_51x)QJ=A7*D95&WZAN1dja?v)Cmm> zHdri{`K&TAF(I_HwAd92g%{P39s=-aoli?kQ&(44YjrvukNY^Y*-V~ihz8a;olYIA zR905r`ZEL-i-a+gef|3NvbMIiV$ztv^E45C3?ZYOXB9BtK=B@QQY4s%W?s2+Wk{)1 zzUuS&s1?ZTq2S0e03EbitryUstPp>Pn%gpW?%Zck{g~%NSdzeQXpYO}S~z?5?0f=w zRM9J$52(gzz?C9q3=T@ z%@F}=34A^`9$+tg+yql*G1Ef)4<9~kS-N!T?@{qU)pzdPslzh)Jgl<_)5-vU#|+Ll z7!0eJFFhU)=kPD%pf=H~6&x0m99R+v#i7kmgd06(-stycR8=#4iQe+1bR*k3u`4_n>YugUfa7 z*s-g{#l@w|moL``10f;bGFTnfpyi=MhfHVAoC$%70ozO_Q}4=^D}NJMFf^Qa8H0WR zy%GWy)8oJ%Y=LIe_r;4Nuy~9xs1&dOwNd?=<*|ODf5Ib)@9jBZEUbFz0;fSF5sME= z;hO9ly!_kRzrI!ZBN-x3l#6=PlR?B>kTj1RfNpO(S9(Q>ubA}*3}yPP_oK%LJG|Getf)Ig8qNW$>Z zyJb0zmx{f3j^L6QNu(+{4p3Q0sj=rhfU6#NYQ- zo%iy}#mv;IRE>Gs^pCGZ5~J8ytR4{1I<~7>&(?o3@~4y1A_)PyVAI~GKW}g9F#n}1 zY9uj@KDnSolxW^W>rYK%7k?!^lJJ0B@Z!tOPp#ix$_sw0-|thf4TMJ|p;D?Ulxj6a z4gc#4qk{{9N{b|XcAJwto%F!Wr)c%r-+E3RZ4q3Q&$&GzP-&6GQ2Q^g<{67ig{MmN zWUx5kGzA}V9x=Z%sJuwR@xat;+V%ZpB$13HlHeqm+q7xZjV)WY2&7CXxTvToqp+}$ zmI(zTwaDQZiI$?lwY9a?8#iujtgEYws2etHXk5R3eGvhzTeq&Lrly9}8#iy>+=!Io z9Fh$pCF=Bb@;1Z5*ziRav#SB-^O)AYHBLUkC7xC z2O(u1p4Wih(_9!*R9axVL%_MYxj)Xz%Hs5TeN^z|f=^c9Gj_&UjfX4=PMpqkjn7-JUAuOY0P^zk-hrP##cTISU38Dx zH4%);U&2@43~m_H;5y7o02I7$-#+3Z9n>Kj%z&)iv17-6oHG={X9N^h(KtH;U|zq7 z(cMj~(ACxT?`UA!TL9QKElWyDE}_tJ(Z$0mm{tt{H(&~p_ zq9qAVf|KARI0;UI`Bl#f2?aAKD-!|^g~^92jlfoLj zOdrcPHK1SnsOABg3Cha?%Y`#&qEBH222J!@%_n!KBsd8cy;k!)ACI(CVtS0+$xo0t zeIa3{Mv^?!Znxh+y6Lr^o}Pk)fB{^B)Y5&Vn95I`I(50cy! z)zQ&$6GsA#0NjpCkKwn>a8L}M*U?k1udnYmIrd}Fn88RVQZwRI?G{X$`Djn!Q*Ly0 zba>~^olO9A$L*`n;NV~x{sWPLk-7xAP11mB0E!5dOgFslI&k1XTYrE5n{i!07A#nB zlMEP=>W4`)VA>l53IM5d7cXAC+TPy&YgAqF{<3$Q4j9Pp-Mgy-C!#b(*BQ z7?!niNrl7Yw718PA16KkQ1+536~1TBo~qv7-rM*W=9!q*YB}(sp`n}1#InIM;ls(_ d`utyj{|A0PY5KSbwBi5&002ovPDHLkV1hN3phf@y diff --git a/plugins/kolab_notes/skins/larry/templates/notes.html b/plugins/kolab_notes/skins/larry/templates/notes.html index 75fa1889..8f35b61f 100644 --- a/plugins/kolab_notes/skins/larry/templates/notes.html +++ b/plugins/kolab_notes/skins/larry/templates/notes.html @@ -60,6 +60,9 @@ +
    + +
    @@ -115,9 +118,16 @@ $(document).ready(function(e){ var form = $('#noteform, #notedetails'), content = $('#notecontent'), header = $('#notedetailstitle'), + footer = $('#notereferences'), w, h; + if ($('#attachment-list li').length) + footer.show(); + else + footer.hide(); + form.css('top', header.outerHeight()+'px'); + form.css('bottom', ((footer.is(':visible') ? footer.outerHeight()+4 : 0) + 41) + 'px'); w = form.outerWidth(); h = form.outerHeight();