root/branches/release-28/mt-static/mt.js @ 1316

Revision 1316, 94.7 kB (checked in by bsmith, 23 months ago)

bugzid:66844 - IE Display Bug: Page Title Overlaps content when system msg is closed

  • Property svn:mime-type set to text/javascript
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
3# This program is distributed under the terms of the
4# GNU General Public License, version 2.
5#
6# $Id$
7*/
8
9var pager;
10var CMSScriptURI;
11var ScriptURI;
12var ScriptBaseURI;
13var StaticURI;
14var HelpBaseURI;
15var Lexicon = {};
16var itemset_options = {};
17
18if ((!(navigator.appVersion.indexOf('MSIE') != -1) &&
19      (parseInt(navigator.appVersion)==4))) {
20    document.write("<style type=\"text/css\">");
21    document.write("body { margin-top: -8px; margin-left: -8px; }"); 
22    document.write("</style>");
23}
24
25var origWidth, origHeight;
26if ((navigator.appName == 'Netscape') &&
27    (parseInt(navigator.appVersion) == 4)) {
28    origWidth = innerWidth;
29    origHeight = innerHeight;
30    window.onresize = restore;
31}
32
33function restore () {
34    if (innerWidth != origWidth || innerHeight != origHeight)
35        location.reload();
36}
37
38function doRebuild (blogID, otherParams) {
39    window.open(CMSScriptURI + '?__mode=rebuild_confirm&blog_id=' + blogID + '&' + otherParams, 'rebuild', 'width=400,height=400,resizable=yes');
40}
41
42function openManual (section, page) {
43    var url;
44    if (page)
45        url = HelpBaseURI + 'help/' + section + '/' + page + '/';
46    else if (section)
47        url = HelpBaseURI + 'help/' + section + '/';
48    else
49        url = HelpBaseURI + 'help/';
50    window.open(url, 'mt_help', 
51'scrollbars=yes,status=yes,resizable=yes,toolbar=yes,location=yes,menubar=yes');
52    return false;
53}
54
55function countMarked (f, nameRestrict) {
56    var count = 0;
57    var e = f.id;
58    if (!e) return 0;
59    if (e.type && e.type == 'hidden') return 1;
60    if (e.value && e.checked)
61        count++;
62    else
63    if (nameRestrict) {
64        for (i=0; i<e.length; i++)
65            if (e[i].checked && (e[i].name == nameRestrict))
66                count++;
67    } else {
68        for (i=0; i<e.length; i++)
69            if (e[i].checked)
70                count++;
71    }
72   return count;
73}
74
75//For make-js script
76//trans('delete');
77//trans('remove');
78//trans('enable');
79//trans('disable');
80function doRemoveItems (f, singular, plural, nameRestrict, args, params) {
81    if (params && (typeof(params) == 'string')) {
82        params = { 'mode': params };
83    } else if (!params) {
84        params = {}
85    }
86    var verb = params['verb'] || trans('delete');
87    var mode = params['mode'] || 'delete';
88    var object_type;
89    if (params['type']) {
90        object_type = params['type'];
91    } else {
92        for (var i = 0; i < f.childNodes.length; i++) {
93            if (f.childNodes[i].name == '_type') {
94                object_type = f.childNodes[i].value;
95                break;
96            }
97        }
98    }
99    var count = countMarked(f, nameRestrict);
100    if (!count) {
101        alert(params['none_prompt'] || trans('You did not select any [_1] to [_2].', plural, verb));
102        return false;
103    }
104    var singularMessage = params['singular_prompt'] || trans('Are you sure you want to [_2] this [_1]?');
105    var pluralMessage = params['plural_prompt'] || trans('Are you sure you want to [_3] the [_1] selected [_2]?');
106    if (object_type == 'role') {
107        singularMessage = trans('Are you certain you want to remove this role? By doing so you will be taking away the permissions currently assigned to any users and groups associated with this role.');
108        pluralMessage = trans('Are you certain you want to remove these [_1] roles? By doing so you will be taking away the permissions currently assigned to any users and groups associated with these roles.');
109    }
110    if (confirm(count == 1 ? trans(singularMessage, singular, verb) : trans(pluralMessage, count, plural, verb))) {
111        return doForMarkedInThisWindow(f, singular, plural, nameRestrict, mode, args);
112    }
113}
114
115/* Widget support functions */
116// function addWidget(el, f) {
117//     if (!TC.Client) return;
118//     // Add a new widget to the top of the 'el' element
119//     var args = DOM.getFormData( f );
120//     args['json'] = '1';
121//     TC.Client.call({
122//         'load': function(c) { addWidgetToPage(el, c); },
123//         'error': function() { showMsg("Error adding widget.", "widget-updated", "alert"); },
124//         'method': 'POST',
125//         'uri': ScriptURI,
126//         'arguments': args
127//     });
128//     return false;
129// }
130//
131// function addWidgetToPage(el, c) {
132//     el = TC.elementOrId(el);
133//     var result;
134//     try {
135//         result = eval('(' + c.responseText + ')');
136//     } catch(e) {
137//         showMsg("Error adding widget.", "widget-updated", "alert");
138//         return;
139//     }
140//     var new_node = document.createElement('div');
141//     new_node.innerHTML = result.result.html;
142//     var next;
143//     if (el.hasChildNodes()) {
144//         if (el.firstChild.nodeType != Node.ELEMENT_NODE)
145//             next = DOM.getNextElement(el.firstChild);
146//         else
147//             next = el.firstChild;
148//     }
149//     if (next) {
150//         el.insertBefore(new_node, next);
151//     } else {
152//         el.appendChild(new_node);
153//     }
154// }
155
156function removeWidget(id) {
157    var f = getByID(id + '-form');
158    f['widget_action'].value = 'remove';
159    if ((f['widget_singular'].value == '1') || (!TC.Client)) {
160        f.submit();
161        return;
162    }
163    var args = DOM.getFormData(f);
164    args['json'] = '1';
165    TC.Client.call({
166        'load': function(c) { removedWidget(id, c); },
167        'error': function() { showMsg("Error removing widget.", "widget-updated", "alert"); },
168        'method': 'POST',
169        'uri': ScriptURI,
170        'arguments': args
171    });
172}
173
174function removedWidget(id, c) {
175    var el = getByID(id);
176    var parent = el.parentNode;
177    parent.removeChild(el);
178}
179
180function updateWidget(id) {
181    var f = getByID(id + "-form");
182    if (!f) return false;
183    f['widget_action'].value = 'save';
184    if (!TC.Client) return true;
185    // if (f['widget_refresh'] && f['widget_refresh'].value) {
186    //     return true;
187    // }
188
189    var args = DOM.getFormData( f );
190    args['json'] = '1';
191    TC.Client.call({
192        'load': function(c, responseText) { updatedWidget(id, responseText); },
193        'error': function() { showMsg("Error updating widget.", "widget-updated", "alert"); },
194        'method': 'POST',
195        'uri': ScriptURI,
196        'arguments': args
197    });
198    return false;
199}
200
201function updatedWidget(id, responseText) {
202    var el = TC.elementOrId(id);
203    var result;
204    try {
205        result = eval('(' + responseText + ')');
206    } catch(e) {
207        showMsg("Error updating widget.", "widget-updated", "alert");
208        return;
209    }
210    if (result.result.html) {
211        // updatePrefs has returned a new widget
212        el.innerHTML = result.result.html;
213    }
214    if (result.result.message) {
215        showMsg(result.result.message, "widget-updated", "info");
216    }
217}
218
219function setObjectStatus (f, singular, plural, new_status, nameRestrict, args) {
220    var count = countMarked(f, nameRestrict);
221    var status_mode = 'enable';
222    var named_status = trans('enable');
223    if (new_status == 0) {
224        status_mode = 'disable';
225        named_status = trans('disable');
226    }
227    if (!count) {
228        alert(trans('You did not select any [_1] to [_2].', plural, named_status));
229        return false;
230    }
231    var toSet = "";
232    for (var i = 0; i < f.childNodes.length; i++) {
233        if (f.childNodes[i].name == '_type') {
234            toSet = f.childNodes[i].value;
235            break;
236        }
237    }
238    if (toSet) {
239        singularMessage = trans('Are you sure you want to [_2] this [_1]?');
240        pluralMessage = trans('Are you sure you want to [_3] the [_1] selected [_2]?');
241        if (confirm(count == 1 ? trans(singularMessage, singular, named_status) : trans(pluralMessage, count, plural, named_status))) {
242            return doForMarkedInThisWindow(f, singular, plural, nameRestrict, status_mode + '_object', args);
243        }
244    } 
245}
246
247function doForMarkedInThisWindow (f, singular, plural, nameRestrict, 
248                                  mode, args, phrase) {
249    var count = countMarked(f, nameRestrict);
250    if (!count) {
251        alert(trans('You did not select any [_1] [_2].', plural, phrase));
252        return false;
253    }
254    f.target = "_top";
255    if (f.elements['itemset_action_input'])
256        f.elements['itemset_action_input'].value = '';
257    if (args) {
258        var opt;
259        var input;
260        if (opt = itemset_options[args['action_name']]) {
261            if (opt['min'] && (count < opt['min'])) {
262                alert(trans('You can only act upon a minimum of [_1] [_2].', opt['min'], plural));
263                return false;
264            } else if (opt['max'] && (count > opt['max'])) {
265                alert(trans('You can only act upon a maximum of [_1] [_2].', opt['max'], plural));
266                return false;
267            } else if (opt['input']) {
268                if (input = prompt(opt['input'])) {
269                    f.elements['itemset_action_input'].value = input;
270                } else {
271                    return false;
272                }
273            } else if (opt['continue_prompt']) {
274                if (!confirm(opt['continue_prompt'])) {
275                    return false;
276                }
277            }
278            if (opt['dialog']) {
279                f.target = "dialog_iframe";
280                show("dialog-container");
281                DOM.addEventListener( document.body, "keypress", dialogKeyPress, true );
282            }
283        }
284        for (var arg in args) {
285            if (f.elements[arg]) f.elements[arg].value = args[arg];
286        }
287    }
288    f.elements["__mode"].value = mode;
289    f.submit();
290}
291
292function submitFormConfirm(f, mode, message) {
293    log.warn('submitFormConfirm() deprecated');
294    if (confirm(message)) {
295        if (f.elements["__mode"] && mode)
296            f.elements["__mode"].value = mode;
297        f.submit();
298    }
299}
300
301function submitForm(f, mode) {
302    log.warn('submitForm() deprecated');
303    if (f.elements["__mode"] && mode)
304        f.elements["__mode"].value = mode;
305    f.submit();
306}
307
308function doPluginAction(f, plural, phrase) {
309    if (!f) {
310        var forms = document.getElementsByTagName( "form" ); 
311        for ( var i = 0; i < forms.length; i++ ) { 
312            var pas = truth( forms[ i ][ 'plugin_action_selector' ] );
313            if (pas) {
314                f = forms[ i ];
315                break;
316            }
317        }
318    }
319    if (!f)
320        return;
321    var sel = f['plugin_action_selector'];
322    if (sel.length && sel[0].options) sel = sel[0];
323    var action = sel.options[sel.selectedIndex].value;
324    if (action == '0' || action == '') {
325        alert(trans('You must select an action.'));
326        return;
327    }
328    if (itemset_options[action]) {
329        if (itemset_options[action]['js']) {
330            return eval(itemset_options[action]['js'] + '(f,action);');
331        }
332    }
333    return doForMarkedInThisWindow(f, '', plural, 'id', 'itemset_action', {'action_name': action}, phrase);
334}
335
336function updatePluginAction(s) {
337    var frm = s.form;
338    frm.elements['plugin_action_selector'].value = s[s.selectedIndex].value;
339    // synchronize top and bottom plugin action selection
340    var el = frm[s.name];
341    for (var i = 0; i < el.length; i++)
342        if (el[i].selectedIndex != s.selectedIndex)
343            el[i].selectedIndex = s.selectedIndex;
344}
345
346function doItemsAreJunk (f, type, plural, nameRestrict) {
347    doForMarkedInThisWindow(f, type, plural, nameRestrict,
348        'handle_junk', {}, trans('to mark as spam'));
349}
350
351function doItemsAreNotJunk (f, type, plural, nameRestrict) {
352    doForMarkedInThisWindow(f, type, plural, nameRestrict,
353        'not_junk', {}, trans('to remove spam status'));
354}
355
356function dialogKeyPress(e) {
357    if (e.keyCode == 27) {
358        // escape key...
359        DOM.removeEventListener( document.body, "keypress", dialogKeyPress, true );
360        closeDialog();
361    }
362}
363
364function openDialog(f, mode, params) {
365    var url = ScriptURI;
366    url += '?__mode=' + mode;
367    if (params) url += '&' + params;
368    url += '&__type=dialog';
369    if (app) app.closeFlyouts();
370    show("dialog-container");
371    // handle escape key for closing modal dialog
372    DOM.addEventListener( document.body, "keypress", dialogKeyPress, true );
373    openDialogUrl(url);
374    if ( document.all && DOM.getElement( "dialog-container" ) ) {
375        DOM.addClassName( "dialog-container", "hidden" );
376        new Timer(function() {
377            DOM.removeClassName( "dialog-container", "hidden" );
378        }, 500, 1 );
379    }
380    return false;
381}
382
383function openDialogUrl(url) {
384    var iframe = getByID("dialog-iframe");
385    var frame_d = iframe.contentDocument;
386    if (!frame_d) {
387        // Sometimes the contentWindow is unavailable because we've just
388        // unhidden the container div that holds the iframe. If this happens
389        // we have to wait for the contentWindow object to be created
390        // before we can access the document within. This may take an extra
391        // try using a setTimeout on this window.
392        if (iframe.contentWindow)
393            frame_d = iframe.contentWindow.document || iframe.document;
394    }
395    if (frame_d) {
396        frame_d.open();
397        frame_d.write("<html><head><style type=\"text/css\">\n"
398            + "#dialog-indicator {\nposition: relative;\ntop: 200px;\n"
399            + "background: url(" + StaticURI + "images/indicator.gif) "
400            + "no-repeat;\nwidth: 66px;\nheight: 66px;\nmargin: 0 auto;"
401            + "\n}\n</style><script type=\"text/javascript\">\n"
402            + "function init() {\ndocument.location = \"" + url + "\";\n}\n"
403            + "if (window.navigator.userAgent.match(/ AppleWebKit\\//))\n"
404            + "window.setTimeout(\"init()\", 1500);\n"
405            + "else window.onload = init;\n</scr"+"ipt></head><body>"
406            + "<div align=\"center\"><div id=\"dialog-indicator\"></div>"
407            + "</div></body></html>");
408        frame_d.close();
409    } else {
410        window.setTimeout("openDialogUrl('" + url + "')", 100);
411    }
412}
413
414function closeDialog(url) {
415    var w = window;
416    while (w.parent && (w.parent != w))
417        w = w.parent;
418    if (url)
419        w.location = url;
420    else
421        hide("dialog-container", w.document);
422    return false;
423}
424
425function getByID(n, d) {
426    if (!d) d = document;
427    if (d.getElementById)
428        return d.getElementById(n);
429    else if (d.all)
430        return d.all[n];
431}
432
433var canFormat = 0;
434if (document.selection ||
435    (typeof(document.createElement("textarea")["setSelectionRange"]) != "undefined"))
436    canFormat = 1;
437
438function getSelected(e) {
439    if (document.selection) {
440        e.focus();
441        var range = document.selection.createRange();
442        return range.text;
443    } else {
444        var length = e.textLength;
445        var start = e.selectionStart;
446        var end = e.selectionEnd;
447        if (end == 1 || end == 2 && length != undefined) end = length;
448        return e.value.substring(start, end);
449    }
450}
451
452function setSelection(e, v) {
453    if (document.selection) {
454        e.focus();
455        var range = document.selection.createRange();
456        range.text = v;
457    } else {
458        var scrollTop = e.scrollTop;
459        var length = e.textLength;
460        var start = e.selectionStart;
461        var end = e.selectionEnd;
462        if (end == 1 || end == 2 && length != undefined) end = length;
463        e.value = e.value.substring(0, start) + v + e.value.substr(end, length);
464        e.selectionStart = start + v.length;
465        e.selectionEnd = start + v.length;
466        e.scrollTop = scrollTop;
467    }
468    e.focus();
469}
470
471function formatStr(e, v) {
472    if (!canFormat) return;
473    var str = getSelected(e);
474    if (str) setSelection(e, '<' + v + '>' + str + '</' + v + '>');
475    return false;
476}
477
478function mtShortCuts(e) {
479    e = e || event;
480    var code;
481    if (e.keyCode) code = e.keyCode;
482    else if (e.which) code = e.which;
483    el = e.target || e.srcElement;
484    if (el.nodeType == 3) el = el.parentNode; // Safari bug
485    if (e.ctrlKey) {
486        switch(e.keyCode) {
487            case 66: // b
488            case 73: // i
489            case 85: // u
490            disableCtrlDefault(e);
491            if (code == '66') formatStr(el, 'strong');
492            if (code == '73') formatStr(el, 'em');
493            if (code == '85') formatStr(el, 'u');
494        }
495    }
496}
497
498function disableCtrlDefault(e) {
499    if(e.preventDefault) {
500        e.preventDefault();
501    } else {
502        e.returnValue = false;
503    }
504    return;
505}
506
507function insertLink(e, isMail) {
508    if (!canFormat) return;
509    var str = getSelected(e);
510    var link = '';
511    if (!isMail) {
512        if (str.match(/^https?:/)) {
513            link = str;
514        } else if (str.match(/^(\w+\.)+\w{2,5}\/?/)) {
515            link = 'http://' + str;
516        } else if (str.match(/ /)) {
517            link = 'http://';
518        } else {
519            link = 'http://' + str;
520        }
521    } else {
522        if (str.match(/@/)) {
523            link = str;
524        }
525    }
526    var my_link = prompt(isMail ? trans('Enter email address:') : trans('Enter URL:'), link);
527    if (my_link != null) {
528         if (str == '') str = my_link;
529         if (isMail) my_link = 'mailto:' + my_link;
530        setSelection(e, '<a href="' + my_link + '">' + str + '</a>');
531    }
532    return false;
533}
534
535function execFilter(f) {
536    var filter_col = f['filter-col'].options[f['filter-col'].selectedIndex].value;
537    var opts = f[filter_col+'-val'].options;
538    var filter_val = '';
539    if (opts) {
540        filter_val = opts[f[filter_col+'-val'].selectedIndex].value;
541    } else if (f[filter_col+'-val'].value) {
542        filter_val = f[filter_col+'-val'].value;
543    }
544    getByID('filter').value = filter_col;
545    getByID('filter_val').value = filter_val;
546    getByID('filter-form').submit();
547    return false;
548}
549
550function setFilterVal(value) {
551    var f = getByID('filter-select-form');
552    if (value == '') return;
553    var filter_col = f['filter-col'].options[f['filter-col'].selectedIndex].value;
554    var val_span = getByID("filter-text-val");
555    if (filter_col) {
556        var filter_fld = f[filter_col+'-val'];
557        if (filter_fld.options) {
558            for (var i = 0; i < filter_fld.options.length; i++) {
559                if (filter_fld.options[i].value == value) {
560                    value = filter_fld.options[i].text;
561                    // strip off any leading spacing found on category lists
562                    value = value.replace(/^(\xA0 )+/, '');
563                    filter_fld.selectedIndex = i;
564                    if (val_span)
565                        val_span.innerHTML = '<strong>' + value + '</strong>';
566                    break;
567                }
568            }
569        } else if (filter_fld.value) {
570            filter_fld.value = value;
571            if (val_span)
572                val_span.innerHTML = '<strong>' + value + '</strong>';
573        }
574    }
575}
576
577function toggleDisplayOptions() {
578    return toggleActive('display-options');
579}
580
581function toggleEntryDisplayOptions() {
582    return toggleActive('display-options-widget');
583}
584
585function toggleActive( id ) {
586    var div = DOM.getElement( id );
587    if ( !div )
588        return false;
589    if ( DOM.hasClassName( div, 'active' ) )
590        DOM.removeClassName( div, 'active' );
591    else
592        DOM.addClassName( div, 'active' );
593    return false;
594}
595
596function toggleHidden( id ) {
597    var div = DOM.getElement( id );
598    if ( !div )
599        return false;
600    if ( DOM.hasClassName( div, 'hidden' ) )
601        DOM.removeClassName( div, 'hidden' );
602    else
603        DOM.addClassName( div, 'hidden' );
604    return false;
605}
606
607function tabToggle(selectedTab, tabs) {
608    log.warn('tabToggle is deprecated. use mt tab delegate');
609    for (var i = 0; i < tabs.length; i++) {
610        var tabObject = getByID(tabs[i] + '-tab');
611        var contentObject = getByID(tabs[i] + '-panel');
612           
613        if (tabObject && contentObject) {
614            if (tabs[i] == selectedTab) {
615                DOM.addClassName( tabObject, 'selected-tab' );
616                DOM.addClassName( contentObject, 'selected-tab-panel' );
617            } else {
618                DOM.removeClassName( tabObject, 'selected-tab' );
619                DOM.removeClassName( contentObject, 'selected-tab-panel' );
620            }
621        }
622    }
623    return false;
624}
625
626function show(id, d, style) {
627    var el = getByID(id, d);
628    if (!el) return;
629    if ( DOM.hasClassName( el, "hidden" ) ) {
630        DOM.removeClassName ( el, "hidden");
631    } else {
632        el.style.display = style ? style : 'block';
633    }
634    /* hack */
635    if ( DOM.hasClassName( el, "autolayout-height-parent" ) )
636        DOM.setHeight( el, finiteInt( el.parentNode.clientHeight ) );
637}
638
639function hide(id, d) {
640    var el = getByID(id, d);
641    if (!el) return;
642    if ( !DOM.hasClassName( el, "hidden" ) ) {
643        DOM.addClassName ( el, "hidden");
644    } else {
645        el.style.display = 'none';
646    }
647}
648
649function showReply(id, d, style) {
650    var el = getByID(id, d);
651    if (!el) return;
652    el.style.visibility = style ? style : 'visible';
653}
654
655function hideReply(id, d) {
656    var el = getByID(id, d);
657    if (!el) return;
658    el.style.visibility = 'hidden';
659}
660
661function toggleSubPrefs(c) {
662    var div = TC.elementOrId((c.name || c.id)+"-prefs") || TC.elementOrId((c.name || c.id)+'_prefs');
663    if (div) {
664        if (c.type) {
665            var on = c.type == 'checkbox' ? c.checked : c.value != 0;
666            if (on) {
667                TC.removeClassName(div, "hidden");
668            } else {
669                TC.addClassName(div, "hidden");
670            }
671            // div.style.display = on ? "block" : "none";
672        } else {
673            var on = div.style.display && div.style.display != "none";
674            if (on) {
675                TC.addClassName(div, "hidden");
676            } else {
677                TC.removeClassName(div, "hidden");
678            }
679            // div.style.display = on ? "none" : "block";
680        }
681    }
682    return false;
683}
684
685function toggleAdvancedPrefs(evt, c) {
686    evt = evt || window.event;
687    var id;
688    var obj;
689    if (!c || (typeof c != 'string')) {
690        c = c || evt.target || evt.srcElement;
691        id = c.id || c.name;
692        obj = c;
693    } else {
694        id = c;
695    }
696    var div = getByID( id + '-advanced');
697    if (div) {
698        if (obj) {
699            var shiftKey = evt ? evt.shiftKey : undefined;
700                if (evt && shiftKey && obj.type == 'checkbox')
701                obj.checked = true;
702            var on = obj.type == 'checkbox' ? obj.checked : obj.value != 0;
703            if (on && shiftKey) {
704                if (div.style.display == "block")
705                    div.style.display = "none";
706                else
707                    div.style.display = "block";
708            } else {
709                div.style.display = "none";
710            }
711        } else {
712            if (div.style.display == "block")
713                div.style.display = "none";
714            else
715                div.style.display = "block";
716        }
717    }
718    return false;
719}
720
721function trans(str) {
722    if (Lexicon && Lexicon[str])
723        str = Lexicon[str];
724    if (arguments.length > 1)
725        for (var i = 1; i <= arguments.length; i++) {
726            /* This matches [_#] or [_#:comment] */
727            str = str.replace(new RegExp('\\[_' + i + '(?:\:[^\\]]+)?\\]', 'g'), arguments[i]);
728            var re = new RegExp('\\[quant,_' + i + ',(.+?)(?:,(.+?))?(?:\:[^\\]]+)?\\]');
729            var matches;
730            while (matches = str.match(re)) {
731                if (arguments[i] != 1)
732                    str = str.replace(re, arguments[i] + ' ' +
733                        ((typeof(matches[2]) != 'undefined') ? matches[2]
734                                                               : matches[1]
735                                                                 + 's'));
736                else
737                    str = str.replace(re, arguments[i] + ' ' + matches[1]);
738            }
739        }
740    return str;
741}
742
743function junkScoreNudge(amount, id, max) {
744    if (max == undefined) max = 10;
745    var fld = getByID(id);
746    score = fld.value;
747    score.replace(/\+/, '');
748    score = parseFloat(score) + amount;
749    if (isNaN(score)) score = amount;
750    if (score > max) score = max;
751    if (score < 0) score = 0;
752    fld.value = score;
753    return false;
754}
755
756var dirify_table = {
757    "\u00C0": 'A',    // A`
758    "\u00E0": 'a',    // a`
759    "\u00C1": 'A',    // A'
760    "\u00E1": 'a',    // a'
761    "\u00C2": 'A',    // A^
762    "\u00E2": 'a',    // a^
763    "\u0102": 'A',    // latin capital letter a with breve
764    "\u0103": 'a',    // latin small letter a with breve
765    "\u00C6": 'AE',   // latin capital letter AE
766    "\u00E6": 'ae',   // latin small letter ae
767    "\u00C5": 'A',    // latin capital letter a with ring above
768    "\u00E5": 'a',    // latin small letter a with ring above
769    "\u0100": 'A',    // latin capital letter a with macron
770    "\u0101": 'a',    // latin small letter a with macron
771    "\u0104": 'A',    // latin capital letter a with ogonek
772    "\u0105": 'a',    // latin small letter a with ogonek
773    "\u00C4": 'A',    // A:
774    "\u00E4": 'a',    // a:
775    "\u00C3": 'A',    // A~
776    "\u00E3": 'a',    // a~
777    "\u00C8": 'E',    // E`
778    "\u00E8": 'e',    // e`
779    "\u00C9": 'E',    // E'
780    "\u00E9": 'e',    // e'
781    "\u00CA": 'E',    // E^
782    "\u00EA": 'e',    // e^
783    "\u00CB": 'E',    // E:
784    "\u00EB": 'e',    // e:
785    "\u0112": 'E',    // latin capital letter e with macron
786    "\u0113": 'e',    // latin small letter e with macron
787    "\u0118": 'E',    // latin capital letter e with ogonek
788    "\u0119": 'e',    // latin small letter e with ogonek
789    "\u011A": 'E',    // latin capital letter e with caron
790    "\u011B": 'e',    // latin small letter e with caron
791    "\u0114": 'E',    // latin capital letter e with breve
792    "\u0115": 'e',    // latin small letter e with breve
793    "\u0116": 'E',    // latin capital letter e with dot above
794    "\u0117": 'e',    // latin small letter e with dot above
795    "\u00CC": 'I',    // I`
796    "\u00EC": 'i',    // i`
797    "\u00CD": 'I',    // I'
798    "\u00ED": 'i',    // i'
799    "\u00CE": 'I',    // I^
800    "\u00EE": 'i',    // i^
801    "\u00CF": 'I',    // I:
802    "\u00EF": 'i',    // i:
803    "\u012A": 'I',    // latin capital letter i with macron
804    "\u012B": 'i',    // latin small letter i with macron
805    "\u0128": 'I',    // latin capital letter i with tilde
806    "\u0129": 'i',    // latin small letter i with tilde
807    "\u012C": 'I',    // latin capital letter i with breve
808    "\u012D": 'i',    // latin small letter i with breve
809    "\u012E": 'I',    // latin capital letter i with ogonek
810    "\u012F": 'i',    // latin small letter i with ogonek
811    "\u0130": 'I',    // latin capital letter with dot above
812    "\u0131": 'i',    // latin small letter dotless i
813    "\u0132": 'IJ',   // latin capital ligature ij
814    "\u0133": 'ij',   // latin small ligature ij
815    "\u0134": 'J',    // latin capital letter j with circumflex
816    "\u0135": 'j',    // latin small letter j with circumflex
817    "\u0136": 'K',    // latin capital letter k with cedilla
818    "\u0137": 'k',    // latin small letter k with cedilla
819    "\u0138": 'k',    // latin small letter kra
820    "\u0141": 'L',    // latin capital letter l with stroke
821    "\u0142": 'l',    // latin small letter l with stroke
822    "\u013D": 'L',    // latin capital letter l with caron
823    "\u013E": 'l',    // latin small letter l with caron
824    "\u0139": 'L',    // latin capital letter l with acute
825    "\u013A": 'l',    // latin small letter l with acute
826    "\u013B": 'L',    // latin capital letter l with cedilla
827    "\u013C": 'l',    // latin small letter l with cedilla
828    "\u013F": 'l',    // latin capital letter l with middle dot
829    "\u0140": 'l',    // latin small letter l with middle dot
830    "\u00D2": 'O',    // O`
831    "\u00F2": 'o',    // o`
832    "\u00D3": 'O',    // O'
833    "\u00F3": 'o',    // o'
834    "\u00D4": 'O',    // O^
835    "\u00F4": 'o',    // o^
836    "\u00D6": 'O',    // O:
837    "\u00F6": 'o',    // o:
838    "\u00D5": 'O',    // O~
839    "\u00F5": 'o',    // o~
840    "\u00D8": 'O',    // O/
841    "\u00F8": 'o',    // o/
842    "\u014C": 'O',    // latin capital letter o with macron
843    "\u014D": 'o',    // latin small letter o with macron
844    "\u0150": 'O',    // latin capital letter o with double acute
845    "\u0151": 'o',    // latin small letter o with double acute
846    "\u014E": 'O',    // latin capital letter o with breve
847    "\u014F": 'o',    // latin small letter o with breve
848    "\u0152": 'OE',   // latin capital ligature oe
849    "\u0153": 'oe',   // latin small ligature oe
850    "\u0154": 'R',    // latin capital letter r with acute
851    "\u0155": 'r',    // latin small letter r with acute
852    "\u0158": 'R',    // latin capital letter r with caron
853    "\u0159": 'r',    // latin small letter r with caron
854    "\u0156": 'R',    // latin capital letter r with cedilla
855    "\u0157": 'r',    // latin small letter r with cedilla
856    "\u00D9": 'U',    // U`
857    "\u00F9": 'u',    // u`
858    "\u00DA": 'U',    // U'
859    "\u00FA": 'u',    // u'
860    "\u00DB": 'U',    // U^
861    "\u00FB": 'u',    // u^
862    "\u00DC": 'U',    // U:
863    "\u00FC": 'u',    // u:
864    "\u016A": 'U',    // latin capital letter u with macron
865    "\u016B": 'u',    // latin small letter u with macron
866    "\u016E": 'U',    // latin capital letter u with ring above
867    "\u016F": 'u',    // latin small letter u with ring above
868    "\u0170": 'U',    // latin capital letter u with double acute
869    "\u0171": 'u',    // latin small letter u with double acute
870    "\u016C": 'U',    // latin capital letter u with breve
871    "\u016D": 'u',    // latin small letter u with breve
872    "\u0168": 'U',    // latin capital letter u with tilde
873    "\u0169": 'u',    // latin small letter u with tilde
874    "\u0172": 'U',    // latin capital letter u with ogonek
875    "\u0173": 'u',    // latin small letter u with ogonek
876    "\u00C7": 'C',    // ,C
877    "\u00E7": 'c',    // ,c
878    "\u0106": 'C',    // latin capital letter c with acute
879    "\u0107": 'c',    // latin small letter c with acute
880    "\u010C": 'C',    // latin capital letter c with caron
881    "\u010D": 'c',    // latin small letter c with caron
882    "\u0108": 'C',    // latin capital letter c with circumflex
883    "\u0109": 'c',    // latin small letter c with circumflex
884    "\u010A": 'C',    // latin capital letter c with dot above
885    "\u010B": 'c',    // latin small letter c with dot above
886    "\u010E": 'D',    // latin capital letter d with caron
887    "\u010F": 'd',    // latin small letter d with caron
888    "\u0110": 'D',    // latin capital letter d with stroke
889    "\u0111": 'd',    // latin small letter d with stroke
890    "\u00D1": 'N',    // N~
891    "\u00F1": 'n',    // n~
892    "\u0143": 'N',    // latin capital letter n with acute
893    "\u0144": 'n',    // latin small letter n with acute
894    "\u0147": 'N',    // latin capital letter n with caron
895    "\u0148": 'n',    // latin small letter n with caron
896    "\u0145": 'N',    // latin capital letter n with cedilla
897    "\u0146": 'n',    // latin small letter n with cedilla
898    "\u0149": 'n',    // latin small letter n preceded by apostrophe
899    "\u014A": 'N',    // latin capital letter eng
900    "\u014B": 'n',    // latin small letter eng
901    "\u00DF": 'ss',   // double-s
902    "\u015A": 'S',    // latin capital letter s with acute
903    "\u015B": 's',    // latin small letter s with acute
904    "\u0160": 'S',    // latin capital letter s with caron
905    "\u0161": 's',    // latin small letter s with caron
906    "\u015E": 'S',    // latin capital letter s with cedilla
907    "\u015F": 's',    // latin small letter s with cedilla
908    "\u015C": 'S',    // latin capital letter s with circumflex
909    "\u015D": 's',    // latin small letter s with circumflex
910    "\u0218": 'S',    // latin capital letter s with comma below
911    "\u0219": 's',    // latin small letter s with comma below
912    "\u0164": 'T',    // latin capital letter t with caron
913    "\u0165": 't',    // latin small letter t with caron
914    "\u0162": 'T',    // latin capital letter t with cedilla
915    "\u0163": 't',    // latin small letter t with cedilla
916    "\u0166": 'T',    // latin capital letter t with stroke
917    "\u0167": 't',    // latin small letter t with stroke
918    "\u021A": 'T',    // latin capital letter t with comma below
919    "\u021B": 't',    // latin small letter t with comma below
920    "\u0192": 'f',    // latin small letter f with hook
921    "\u011C": 'G',    // latin capital letter g with circumflex
922    "\u011D": 'g',    // latin small letter g with circumflex
923    "\u011E": 'G',    // latin capital letter g with breve
924    "\u011F": 'g',    // latin small letter g with breve
925    "\u0120": 'G',    // latin capital letter g with dot above
926    "\u0121": 'g',    // latin small letter g with dot above
927    "\u0122": 'G',    // latin capital letter g with cedilla
928    "\u0123": 'g',    // latin small letter g with cedilla
929    "\u0124": 'H',    // latin capital letter h with circumflex
930    "\u0125": 'h',    // latin small letter h with circumflex
931    "\u0126": 'H',    // latin capital letter h with stroke
932    "\u0127": 'h',    // latin small letter h with stroke
933    "\u0174": 'W',    // latin capital letter w with circumflex
934    "\u0175": 'w',    // latin small letter w with circumflex
935    "\u00DD": 'Y',    // latin capital letter y with acute
936    "\u00FD": 'y',    // latin small letter y with acute
937    "\u0178": 'Y',    // latin capital letter y with diaeresis
938    "\u00FF": 'y',    // latin small letter y with diaeresis
939    "\u0176": 'Y',    // latin capital letter y with circumflex
940    "\u0177": 'y',    // latin small letter y with circumflex
941    "\u017D": 'Z',    // latin capital letter z with caron
942    "\u017E": 'z',    // latin small letter z with caron
943    "\u017B": 'Z',    // latin capital letter z with dot above
944    "\u017C": 'z',    // latin small letter z with dot above
945    "\u0179": 'Z',    // latin capital letter z with acute
946    "\u017A": 'z'     // latin small letter z with acute
947};
948
949function dirify (s) {
950    s = s.replace(/<[^>]+>/g, '');
951    for (var p in dirify_table)
952        if (s.indexOf(p) != -1)
953            s = s.replace(new RegExp(p, "g"), dirify_table[p]);
954    s = s.toLowerCase();
955    s = s.replace(/&[^;\s]+;/g, '');
956    s = s.replace(/[^-a-z0-9_ ]/g, '');
957    s = s.replace(/\s+/g, '_');
958    s = s.replace(/_+$/, '');
959    s = s.replace(/_+/g, '_');
960    return s;
961}
962
963function setElementValue(domID, newVal) {
964    getByID(domID).value = newVal;
965}
966
967/* pager and datasource */
968
969/***
970 * Datasource class
971 * A class for navigating and displaying data from an AJAX datasource.
972 * Methods:
973 *   constructor(el, datatype): Creates a new datasource using DOM
974 *     element 'el' as a container for the data (table rows typically)
975 *     and datatype is used to communicate the '_type' parameter to
976 *     the server.
977 *   setPager(pager): Sets a Pager class object which is refreshed
978 *     upon receiving new data.
979 *   search(string): Invokes a search on the datasource.
980 *   navigate(offset): Used to navigate to a particular offset within
981 *     the dataset.
982 */
983Datasource = new Class(Object, {
984    init: function(el, datatype) {
985        // this.id = id;
986        // this.document = doc || document;
987        this.element = TC.elementOrId(el);
988        this.searching = false;
989        this.navigating = false;
990        this.type = datatype;
991        this.onUpdate = null;
992    },
993    setPager: function(pager, pager2) {
994        this.pager = pager;
995        if (pager2) this.pager2 = pager2
996        if (pager) pager.datasource = this;
997        if (pager2) pager2.datasource = this;
998        if (pager) pager.render();
999        if (pager2) pager2.render();
1000    },
1001    search: function(str) {
1002        if (this.searching) return;
1003
1004        var doc = TC.getOwnerDocument(this.element);
1005        var args = doc.location.search;
1006        args = args.replace(/^\?/, '');
1007        args = args.replace(/&?offset=\d+/, '');
1008        args = 'search=' + escape(str) + (args ? '&' + args : '') + '&json=1';
1009        if (this.type) {
1010            args = args.replace(/&?_type=\w+/, '');
1011            args += '&_type=' + this.type;
1012        }
1013
1014        this.searching = true;
1015        if (this.pager)
1016            this.pager.render();
1017        if (this.pager2)
1018            this.pager2.render();
1019        var self = this;
1020        TC.Client.call({
1021            'load': function(c,r) { self.searched(r); },
1022            'error': function() { alert("Error during search."); self.searched(null); },
1023            'method': 'POST',
1024            'uri': ScriptURI,
1025            'arguments': args
1026        });
1027    },
1028    searched: function(c) {
1029        this.searching = false;
1030        if (c) {
1031            try {
1032                data = eval('(' + c + ')');
1033                this.update(data['html']);
1034                if (this.pager)
1035                    this.pager.setState({});
1036                if (this.pager2)
1037                    this.pager2.setState({});
1038            } catch (e) {
1039                alert("Error in response: " + e);
1040                if (this.pager)
1041                    this.pager.render();
1042                if (this.pager2)
1043                    this.pager2.render();
1044            }
1045        } else {
1046            if (this.pager)
1047                this.pager.render();
1048            if (this.pager2)
1049                this.pager2.render();
1050        }
1051    },
1052    update: function(html) {
1053        if (!this.element) return;
1054        this.element.innerHTML = html;
1055        this.updated();
1056    },
1057    updated: function() {
1058        if (this.onUpdate) this.onUpdate(this);
1059    },
1060    navigate: function(offset) {
1061        if (offset == null) return;
1062        if (this.navigating) return;
1063
1064        var doc = TC.getOwnerDocument(this.element);
1065        var args = doc.location.search;
1066        args = args.replace(/^\?/, '');
1067        //args = args.replace(/&?search=[^&]+/, '');
1068        //args = args.replace(/&?do_search=1/, '');
1069        args = args.replace(/&?offset=\d+/, '');
1070        args = 'offset=' + offset + (args ? '&' + args : '') + '&json=1';
1071        if (this.type) {
1072            args = args.replace(/&?_type=\w+/, '');
1073            args = args + '&_type=' + this.type;
1074        }
1075
1076        this.navigating = true;
1077        if (this.pager)
1078            this.pager.render();
1079        if (this.pager2)
1080            this.pager2.render();
1081        var self = this;
1082        TC.Client.call({
1083            'load': function(c,r) { self.navigated(r); },
1084            'error': function() { alert("Error in request."); self.navigated(null); },
1085            'method': 'POST',
1086            'uri': ScriptURI,
1087            'arguments': args
1088        });
1089        return false;
1090    },
1091    navigated: function(c) {
1092        var data;
1093        this.navigating = false;
1094        if (c) {
1095            try {
1096                data = eval('(' + c + ')');
1097                this.update(data['html']);
1098                if (this.pager)
1099                    this.pager.setState(data['pager']);
1100                if (this.pager2)
1101                    this.pager2.setState(data['pager']);
1102            } catch (e) {
1103                alert("Error in response: " + e);
1104                if (this.pager)
1105                    this.pager.render();
1106                if (this.pager2)
1107                    this.pager2.render();
1108            }
1109        } else {
1110            if (this.pager) this.pager.render();
1111            if (this.pager2) this.pager2.render();
1112        }
1113    }
1114});
1115//These two lines are to translate phrases in list_tags.tmpl
1116//trans("The tag '[_2]' already exists. Are you sure you want to merge '[_1]' with '[_2]'?");
1117//trans("The tag '[_2]' already exists. Are you sure you want to merge '[_1]' with '[_2]' across all weblogs?");
1118
1119/***
1120 * Pager class
1121 * Expects a 'state' object containing:
1122 *   offset: offset into listing (10, means first row displayed is 11)
1123 *   listTotal: total number of rows in dataset
1124 *   rows: number of rows being displayed
1125 *   chronological: boolean, whether listing is reverse-chronological
1126 *     or not.
1127 *  Methods:
1128 *    constructor(el): constructs using DOM element el as a container
1129 *    setDatasource(ds): used to assign a datasource object
1130 *    setState(state): used to update state settings
1131 *    previous: navigates datasource to previous page
1132 *    next: navigates datasource to next page
1133 *    first: navigates datasource to first page
1134 *    last: navigates datasource to last page
1135 *    previousOffset: calculates and returns offset for previous page
1136 *    nextOffset: calculates and returns offset for next page
1137 *    lastOffset: calculates and returns offset for 'last' page
1138 *    render: refreshes the pagination controls
1139 */
1140Pager = new Class(Object, {
1141    init: function(el) {
1142        this.element = TC.elementOrId(el);
1143        this.state = {};
1144    },
1145    setDatasource: function(ds) {
1146        this.datasource = ds;
1147        this.render();
1148    },
1149    setState: function(state) {
1150        this.state = state;
1151        this.render();
1152    },
1153    previous: function(e) {
1154        this.navigate(this.previousOffset());
1155        return TC.stopEvent(e || window.event);
1156    },
1157    navigate: function(offset) {
1158        if (offset == null) return;
1159        if (this.datasource)
1160            return this.datasource.navigate(offset);
1161        // traditional navigation...
1162        var doc = TC.getOwnerDocument(this.element);
1163        new_loc = doc.location.href;
1164        if (this.state.method == 'POST') {
1165            new_loc += '?' + this.state.return_args;
1166        }
1167        new_loc = new_loc.replace(/&?offset=\d+/, '');
1168        new_loc += '&offset=' + offset;
1169        window.location = new_loc;
1170        return false;
1171    },
1172    previousOffset: function() {
1173        if (this.state.offset > 0) {
1174            var offset = this.state.offset - this.state.limit;
1175            if (offset < 0)
1176                offset = 0;
1177            return offset;
1178        }
1179        return null;
1180    },
1181    nextOffset: function() {
1182        if (this.state.listTotal) {
1183            var listStart = (this.state.offset ? this.state.offset : 0) + 1;
1184            var offset = (this.state.offset ? this.state.offset : 0) + this.state.rows;
1185            if (offset >= this.state.listTotal) {
1186                offset = null;
1187            }
1188            return offset;
1189        }
1190        return null;
1191    },
1192    lastOffset: function() {
1193        var offset = 0;
1194        if (this.state.listTotal) {
1195            var listStart = (this.state.offset ? this.state.offset : 0) + 1;
1196            var listEnd = (this.state.offset ? this.state.offset : 0) + this.state.rows;
1197            if (listEnd >= this.state.listTotal) {
1198                offset = null;
1199            } else {
1200                offset = this.state.listTotal - this.state.rows;
1201                if (offset < listStart)
1202                    offset = null;
1203            }
1204            return offset;
1205        }
1206        return null;
1207    },
1208    next: function(e) {
1209        this.navigate(this.nextOffset());
1210        return TC.stopEvent(e || window.event);
1211    },
1212    first: function(e) {
1213        this.navigate(0);
1214        return TC.stopEvent(e || window.event);
1215    },
1216    last: function(e) {
1217        this.navigate(this.lastOffset());
1218        return TC.stopEvent(e || window.event);
1219    },
1220    render: function() {
1221        if (!this.element) return;
1222
1223        /*
1224        This long method is concerned with creating the elements of
1225        the pagination control. It refreshes the controls based on
1226        the 'state' member of the Pager object. This control is
1227        typically tied to a Datasource object. So the navigation
1228        links of the control will influence the Datasource.
1229        Likewise, upon navigating the Datasource, it will invoke
1230        the pager to refresh when the data has been updated.
1231
1232        pager.rows (number of rows shown)
1233        pager.listTotal (total number of rows in datasource)
1234        pager.offset (offset currently used)
1235        pager.chronological (boolean, whether the listing is chronological or not)
1236        */
1237        var html = '';
1238        /* TODO - this can all be replaced with a js template */
1239        if (this.datasource && this.datasource.navigating) {
1240            // TODO: change this to use a CSS class instead.
1241            html = "<div>" + trans('Loading...') + " <img src=\"" + StaticURI + "images/indicator.white.gif\" height=\"10\" width=\"10\" alt=\"...\" /></div>";
1242            this.element.innerHTML = html;
1243        } else if ((this.state.rows != null) && (this.state.rows > 0)) {
1244            this.element.innerHTML = '';
1245            var listStart = (this.state.offset ? this.state.offset : 0) + 1;
1246            var listEnd = (this.state.offset ? this.state.offset : 0) + this.state.rows;
1247
1248            var doc = TC.getOwnerDocument(this.element);
1249            var self = this;
1250            // pagination control structure
1251            if (this.state.offset > 0) {
1252                var link = doc.createElement('a');
1253                link.href = 'javascript:void(0)';
1254                link.onclick = function(e) { return self.first(e) };
1255                link.className = 'start';
1256                link.innerHTML = '<em>&lt;&lt;</em>&nbsp;';
1257                this.element.appendChild(link);
1258            } else {
1259                var txt = doc.createElement('span');
1260                txt.className = 'start-disabled';
1261                txt.innerHTML = '<em>&lt;&lt;</em>&nbsp;';
1262                this.element.appendChild(txt);
1263            }
1264            if (this.previousOffset() != null) {
1265                var link = doc.createElement('a');
1266                link.href = 'javascript:void(0)';
1267                link.onclick = function(e) { return self.previous(e) };
1268                link.className = 'to-start';
1269                link.innerHTML = '<em>&lt;</em>&nbsp;';
1270                this.element.appendChild(link);
1271            } else {
1272                var txt = doc.createElement('span');
1273                txt.className = 'to-start-disabled';
1274                txt.innerHTML = '<em>&lt;</em>&nbsp;';
1275                this.element.appendChild(txt);
1276            }
1277            var showing = doc.createElement('span');
1278            showing.className = 'current-rows';
1279            if (this.state.listTotal)
1280                showing.innerHTML = trans('[_1] &ndash; [_2] of [_3]', listStart, listEnd, this.state.listTotal);
1281            else
1282                showing.innerHTML = trans('[_1] &ndash; [_2]', listStart, listEnd);
1283            this.element.appendChild(showing);
1284            if (this.nextOffset() != null) {
1285                var link = doc.createElement('a');
1286                link.href = 'javascript:void(0)';
1287                link.onclick = function(e) { return self.next(e) };
1288                link.className = 'to-end';
1289                link.innerHTML = '&nbsp;<em>&gt;</em>';
1290                this.element.appendChild(link);
1291            } else {
1292                var txt = doc.createElement('span');
1293                txt.className = 'to-end-disabled';
1294                txt.innerHTML = '&nbsp;<em>&gt;</em>';
1295                this.element.appendChild(txt);
1296            }
1297            if (this.lastOffset() != null) {
1298                var link = doc.createElement('a');
1299                link.href = 'javascript:void(0)';
1300                link.onclick = function(e) { return self.last(e) };
1301                link.className = 'end';
1302                link.innerHTML = '&nbsp;<em>&gt;&gt;</em>';
1303                this.element.appendChild(link);
1304            } else {
1305                var txt = doc.createElement('span');
1306                txt.className = 'end-disabled';
1307                txt.innerHTML = '&nbsp;<em>&gt;&gt;</em>';
1308                this.element.appendChild(txt);
1309            }
1310        } else {
1311            this.element.innerHTML = '';
1312        }
1313    }
1314});
1315
1316
1317MT = {};
1318
1319
1320if ( window.App ) {
1321
1322App.singletonConstructor =
1323MT.App = new Class( App, {
1324 
1325
1326    NAMESPACE: "mt",
1327    changed: false,
1328    autoSaveDelay: 15000, /* ms */
1329
1330
1331    initComponents: function() {
1332        arguments.callee.applySuper( this, arguments );
1333        this.openFlyouts = [];
1334
1335        this.setDelegate( "navMenu", new this.constructor.NavMenu() );
1336
1337        this.initFormElements();
1338
1339        /* fix a display issue */
1340        var navEl = DOM.getElement( "content-nav" );
1341        var navConEl = DOM.getElement( "content-header-inner" );
1342        if ( navEl && navConEl ) {
1343            var d = DOM.getAbsoluteDimensions( navConEl );
1344            navEl.style.top = "-" + (d.clientHeight - 13) + "px";
1345        }
1346
1347        if ( this.constructor.Resizer ) {
1348            this.setDelegate( "resizer", new this.constructor.Resizer( this.getIndirectMethod( "resizeComplete" ) ) );
1349            this.setDelegateListener( "eventMouseUp", "resizer" );
1350            this.setDelegateListener( "eventMouseMove", "resizer" );
1351        }
1352
1353        if ( this.constructor.DefaultValue )
1354            this.setDelegate( "defaultValue", new this.constructor.DefaultValue() );
1355       
1356        if ( this.constructor.TabContainer )
1357            this.setDelegate( "tabContainer", new this.constructor.TabContainer() );
1358           
1359        var forms = DOM.getElementsByTagAndAttribute( this.document, "form", "mt:auto-save" );
1360        if ( forms.length )
1361            window.onbeforeunload = this.getIndirectEventListener( "eventBeforeUnload" );
1362
1363        for ( var i = 0; i < forms.length; i++ ) {
1364            var autosave = truth( forms[ i ].getAttribute( "mt:auto-save" ) );
1365            if ( !autosave )
1366                continue;
1367
1368            this.form = forms[ i ];
1369
1370            var ad = forms[ i ].getAttribute( "mt:auto-save-delay" );
1371            var autoSaveDelay;
1372            if ( ad !== null ) {
1373                autoSaveDelay = parseInt( ad ) || 0;
1374                this.autoSaveDelay = autoSaveDelay;
1375            } else
1376                log.warn("auto-save-delay not defined on this form. Defaulting to "+this.autoSaveDelay);
1377
1378            log('using auto save delay: '+this.autoSaveDelay);
1379
1380            var es = Array.fromPseudo(
1381                forms[ i ].getElementsByTagName( "input" ),
1382                forms[ i ].getElementsByTagName( "textarea" )
1383            );
1384            for ( var j = 0; j < es.length; j++ ) {
1385                if ( es[ j ].getAttribute && es[ j ].getAttribute( "mt:watch-change" ) ) {
1386                    log('adding watcher to '+es[ j ].name);
1387                    DOM.addEventListener( es[ j ], "change", this.getIndirectEventListener( "setDirty" ) );
1388                }
1389                if ( autosave && es[ j ].nodeName == "TEXTAREA" ) {
1390                    /* don't attach to the editor textarea in this form */
1391                    if ( this.editor && es[ j ].id == this.editor.textarea.element.id )
1392                        continue;
1393                    DOM.addEventListener( es[ j ], "keydown", this.getIndirectEventListener( "setDirtyKeyDown" ) );
1394                }
1395            }
1396        }
1397
1398        if ( MT.App.dirty )
1399            this.changed = true;   
1400    },
1401
1402
1403    destroyObject: function() {
1404        this.autoSaveReq = null;
1405        this.autoSaveTimer = null;
1406        this.form = null;
1407        this.cpeList = null;
1408        arguments.callee.applySuper( this, arguments );
1409    },
1410
1411
1412    initFormElements: function() {
1413        var forms = document.getElementsByTagName( "form" );
1414        for( var i = 0; i < forms.length; i++ ) {
1415            forms[ i ].submitted = false;
1416            DOM.addEventListener( forms[ i ], "submit", this.getIndirectEventListener( "eventSubmit" ) );
1417            var tareas = forms[ i ].getElementsByTagName( "textarea" );
1418            var tabs = 0;
1419            for ( var j = 0; j < tareas.length; j++ ) {
1420                if ( ( tabs = tareas[ j ].getAttribute( "mt:allow-tabs" ) ) )
1421                    if ( truth ( tabs ) )
1422                        this.attachTabsToTextarea( tareas[ j ] );
1423
1424                if ( tareas[ j ].getAttribute( "mt:editor" ) == "codepress" ) {
1425                    if ( this.constructor.CodePress.isSupported() ) {
1426                        var ed = new this.constructor.CodePress( tareas[ j ] );
1427                        if ( ed ) {
1428                            if ( !this.cpeList )
1429                                this.cpeList = [];
1430                            this.cpeList.push( ed );
1431                        }
1432                    }
1433                }
1434            }
1435        }
1436    },
1437
1438
1439    eventSubmit: function( event ) {
1440        var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1441        if ( !form )
1442            return;
1443           
1444        if ( form.getAttribute( "mt:once" ) ) {
1445       
1446            if ( form.submitted )
1447                return event.stop();
1448       
1449            this.toggleSubmit( form, true );
1450        }
1451
1452        if ( this.cpeList )
1453            this.cpeList.forEach( function( cpe ) { cpe.onSubmit() } );
1454
1455        form.submitted = true;
1456    },
1457
1458
1459    eventBeforeUnload: function( event ) {
1460        if ( this.changed ) {
1461            if ( this.constructor.Editor )
1462                return event.returnValue = Editor.strings.unsavedChanges;
1463            else if ( window.Editor )
1464                return event.returnValue = window.Editor.strings.unsavedChanges;
1465        }
1466       
1467        return undefined;
1468    },
1469
1470
1471    toggleSubmit: function( form, disable ) {
1472        /* sane default */
1473        if ( !disable )
1474            disable = false;
1475        var elements = form.getElementsByTagName( "*" );
1476        for( var i = 0; i < elements.length; i++ ) {
1477            var element = elements[ i ];
1478            var tagName = element.tagName.toLowerCase();
1479            var type = element.getAttribute( "type" );
1480            type = type ? type.toLowerCase() : "";
1481            if ( tagName == "button" || 
1482                (tagName == "input" && (type == "button" || type == "submit" || type == "image")) )
1483                element.disabled = disable;
1484        }
1485    },
1486
1487
1488    closeFlyouts: function( target ) {
1489        var flyout;
1490        var es = Array.fromPseudo( this.openFlyouts );
1491        for ( var i = 0, len = es.length; i < len; i++ ) {
1492            if ( ( flyout = DOM.getElement( es[ i ] ) ) ) {
1493                if ( target && DOM.hasAncestor( target, flyout ) )
1494                    continue;
1495                DOM.addClassName( flyout, "hidden" );
1496                this.openFlyouts.remove( es[ i ] );
1497                showAllDropDown();
1498            }
1499        }
1500    },
1501
1502
1503    eventClick: function( event ) {
1504        var command = this.getMouseEventCommand( event );
1505
1506        switch( command ) {
1507           
1508            case "openSelectBlog":
1509                app.openDialog( '__mode=dialog_select_weblog&amp;select_favorites=1&return_args='
1510                    + escape( event.commandElement.getAttribute( "mt:href" ) ) );
1511                break;
1512
1513            case "goToLocation":
1514                this.gotoLocation( event.commandElement.getAttribute( "href" ) );
1515                break;
1516           
1517            case "autoSave":
1518                this.autoSave();
1519                break;
1520
1521            case "setModeCodepressOn":
1522                this.cpeList.forEach( function( cpe ) { cpe.toggleOn( true ); } );
1523                break;
1524
1525            case "setModeCodepressOff":
1526                this.cpeList.forEach( function( cpe ) { cpe.toggleOff( true ); } );
1527                break;
1528
1529            case "openFlyout":
1530                var name = event.commandElement.getAttribute( "mt:flyout" );
1531                var el = DOM.getElement( name );
1532                if ( !defined( el ) )
1533                    return;
1534
1535                this.closeFlyouts( event.target );
1536
1537                DOM.removeClassName( el, "hidden" );
1538                this.targetElement = event.target;
1539                this.applyAutolayouts( el );
1540                this.targetElement = null;
1541
1542                hideAllDropDown();
1543                showDropDown( el );
1544                this.openFlyouts.add( name );
1545
1546                break;
1547               
1548            case "closeFlyout":
1549                this.closeFlyouts();
1550               
1551                break;
1552
1553            default:
1554                this.closeFlyouts( event.target );
1555
1556                var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1557                if ( !form )
1558                    return;
1559
1560                var mode = event.target.getAttribute( "mt:mode" );
1561                if ( !mode && event.commandElement )
1562                    mode = event.commandElement.getAttribute( "mt:mode" );
1563
1564                if ( mode ) {
1565                    log('setting __mode in this form: '+mode);
1566                    var elements = form.getElementsByTagName( "input" );
1567                    for( var i = 0; i < elements.length; i++ ) {
1568                        if ( elements[ i ].name == "__mode" ) {
1569                            log('found __mode element');
1570                            elements[ i ].value = mode;
1571                            break;
1572                        }
1573                    }
1574                }
1575
1576                if ( command == "submit" ) {
1577                    event.stop();
1578                    var msg;
1579                    if ( event.commandElement && ( msg = event.commandElement.getAttribute( "mt:confirm-msg" ) ) )
1580                        if ( !confirm( msg ) )
1581                            return;
1582                    form.submit();
1583                }
1584
1585                return;
1586
1587        }
1588        return event.stop();
1589    },
1590   
1591
1592    /* from blog selector transient */
1593    gotoUrl: function( url ) {
1594        if ( url )
1595            this.gotoLocation( url );
1596    },
1597
1598
1599    toggleActive: function( id ) {
1600        log('toggleactive:'+id);
1601        var div = DOM.getElement( id );
1602        if ( DOM.hasClassName( div, 'active' ) )
1603            DOM.removeClassName( div, 'active' );
1604        else
1605            DOM.addClassName( div, 'active' );
1606    },
1607
1608
1609    openDialog: function( params ) {
1610        this.closeFlyouts();
1611        show("dialog-container");
1612        /* TODO remove this cruft */
1613        /*  handle escape key for closing modal dialog */
1614        DOM.addEventListener( document.body, "keypress", dialogKeyPress, true );
1615        openDialogUrl( ScriptURI + "?" + params );
1616        /* IE hack to get the dialog modal to reflow */
1617        if ( document.all && DOM.getElement( "dialog-container" ) ) {
1618            DOM.addClassName( "dialog-container", "hidden" );
1619            new Timer(function() {
1620                DOM.removeClassName( "dialog-container", "hidden" );
1621            }, 500, 1 );
1622        }
1623    },
1624
1625
1626    attachTabsToTextarea: function( element ) {
1627        DOM.addEventListener( element, "keypress", this.getIndirectMethod( "eventKeyPressAllowTabs" ) );
1628        DOM.addEventListener( element, "keydown", this.getIndirectMethod( "eventKeyDownAllowTabs" ) );
1629    },
1630
1631
1632    eventKeyPressAllowTabs: function( event ) {
1633        if ( event.keyCode == 9 )
1634            return event.stop();
1635    },
1636   
1637   
1638    eventKeyDownAllowTabs: function( event ) {
1639        if ( event.keyCode == 9 ) {
1640                    TC.setSelectionValue( ( event.target || event.srcElement ) , "\t" );
1641            return false;
1642        }
1643    },
1644
1645
1646    resizeComplete: function( target, xStart, yStart, x, y, width, height ) {
1647       
1648        switch ( target.id ) {
1649            case "textarea-enclosure":
1650                var es = [ "text", "text_cpe" ];
1651                for ( var i = 0; i < es.length; i++ ) {
1652                    es[ i ] = DOM.getElement( es[ i ] );
1653                    if ( es[ i ] )
1654                        DOM.setHeight( es[ i ], height );
1655                }
1656                break;
1657
1658            /* expand here */
1659        }
1660
1661    },
1662   
1663   
1664    autoSave: function() {
1665        var data = DOM.getFormData( this.form );
1666        data["_autosave"] = 1;
1667
1668        if ( this.cpeList )
1669            this.cpeList.forEach( function( cpe ) { cpe.autoSave( data ) } );
1670
1671        /* don't cancel a pending save */
1672        if ( defined( this.autoSaveReq ) )
1673            return;
1674       
1675        var areas = [
1676            DOM.getElement( "autosave-notification" ),
1677            DOM.getElement( "autosave-notification-top" ),
1678            DOM.getElement( "autosave-notification-bottom" )
1679        ];
1680        if ( areas )
1681            for ( var i = 0; i < areas.length; i++ )
1682                if ( areas[ i ] )
1683                    areas[ i ].innerHTML = Template.process( "autoSave", { saving: true } );
1684
1685        this.autoSaveReq = TC.Client.call({
1686            load: this.getIndirectMethod( "autoSaveComplete" ),
1687            error: this.getIndirectMethod( "autoSaveError" ),
1688            method: 'POST',
1689            uri: this.form.action,
1690            arguments: data
1691        });
1692    },
1693
1694
1695    autoSaveComplete: function( c, r ) {
1696        this.autoSaveTimer = this.autoSaveReq = undefined;
1697
1698        log('auto save complete '+r);
1699        if ( r != "true" )
1700            return log.error( "Error auto-saving post: "+r );
1701       
1702        var areas = [
1703            DOM.getElement( "autosave-notification" ),
1704            DOM.getElement( "autosave-notification-top" ),
1705            DOM.getElement( "autosave-notification-bottom" )
1706        ];
1707        var d = new Date();
1708        if ( areas )
1709            for ( var i = 0; i < areas.length; i++ )
1710                if ( areas[ i ] )
1711                    areas[ i ].innerHTML = Template.process( "autoSave", {
1712                        saving: false,
1713                        hh: d.getHours().toString().pad( 2, "0" ),
1714                        mm: d.getMinutes().toString().pad( 2, "0" ),
1715                        ss: d.getSeconds().toString().pad( 2, "0" )
1716                    } );
1717    },
1718
1719   
1720    autoSaveError: function( c, r ) {
1721        this.autoSaveTimer = this.autoSaveReq = undefined;
1722       
1723        log.error( "Error auto-saving post" );
1724        var areas = [
1725            DOM.getElement( "autosave-notification" ),
1726            DOM.getElement( "autosave-notification-top" ),
1727            DOM.getElement( "autosave-notification-bottom" )
1728        ];
1729        if ( areas )
1730            for ( var i = 0; i < areas.length; i++ )
1731                if ( areas[ i ] )
1732                    areas[ i ].innerHTML = ''
1733    },
1734
1735
1736    setDirtyKeyDown: function( event ) {
1737        if ( this.dirtyKeyDownTimer )
1738            this.dirtyKeyDownTimer.stop();
1739        this.dirtyKeyDownTimer = new Timer( this.getIndirectMethod( "setDirty" ), 5000, 1 );
1740    },
1741
1742
1743    setDirty: function( event ) {
1744        var autoSaveDelay = this.autoSaveDelay;
1745        if ( event && event.target ) {
1746            var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1747            if ( form ) {
1748                log('found dirty form: '+form);
1749                this.form = form;
1750                if ( autoSaveDelay = parseInt( form.getAttribute( "mt:auto-save-delay" ) ) || 0 ) {
1751                    this.autoSaveDelay = autoSaveDelay;
1752                    log('using auto save delay: '+this.autoSaveDelay);
1753                }
1754            }
1755        }
1756           
1757        this.changed = true;
1758        if ( autoSaveDelay < 1 )
1759            return;
1760
1761        if ( defined( this.autoSaveTimer ) )
1762            return this.autoSaveTimer.reset();
1763        this.autoSaveTimer = new Timer( this.getIndirectMethod( "autoSave" ), autoSaveDelay, 1 );
1764    },
1765
1766
1767    clearDirty: function( event ) {
1768        this.changed = false;
1769    },
1770
1771   
1772    insertCode: function( code ) {
1773        if ( this.cpeList )
1774            this.cpeList[ 0 ].insertCode( code );
1775        else if ( this.editor )
1776            this.editor.insertHTML( code );
1777        else {
1778            var txt = DOM.getElement( "text" );
1779            setSelection( txt, code );
1780            DOM.focus( txt );
1781        }
1782    }
1783   
1784
1785} );
1786
1787if ( window.Calendar ) {
1788
1789MT.App.Calendar = new Class( Calendar, {
1790
1791
1792    open: function( data, callback ) {
1793        if ( data.date && data.date.length )
1794            data.date = data.date.replace( /^(\S+).*/, "$1" );
1795        arguments.callee.applySuper( this, arguments );
1796       
1797        /* reset invalid dates to the current date */
1798        if ( !this.dateObject )
1799            this.dateObject = new Date();
1800    },
1801
1802
1803    eventClick: function() {
1804        arguments.callee.applySuper( this, arguments );
1805        if ( this.callback )
1806            this.callback( this.dateObject );
1807    }
1808
1809
1810} );
1811
1812}
1813
1814
1815
1816
1817MT.App.Resizer = new Class( Object, {
1818
1819
1820    dragging: false,
1821    element: null,
1822   
1823    xLock: false,
1824    yLock: false,
1825
1826    xStart: null,
1827    yStart: null,
1828
1829
1830    init: function( callback ) {
1831        if ( callback )
1832            this.callback = callback;
1833    },
1834
1835
1836    destroy: function() {
1837        this.callback = null;
1838    },
1839
1840
1841    eventMouseDown: function( event ) {
1842        this.dragging = true;
1843       
1844        this.reset();
1845       
1846        this.target = event.attributeElement.getAttribute( "mt:target" );
1847
1848        /* x or y locking */
1849        var lock = event.attributeElement.getAttribute( "mt:lock" );
1850        if ( lock ) {
1851            if ( lock == "x" || lock == "X" )
1852                this.xLock = true;
1853            else if ( lock == "y" || lock == "Y" )
1854                this.yLock = true;
1855        }
1856       
1857        /* clone the drag node */
1858        this.element = event.attributeElement.cloneNode( true );
1859        /* using the current mouse position, set the positon of the drag obj */
1860        var d = DOM.getAbsoluteCursorPosition( event );
1861        this.yStart = d.y;
1862        this.xStart = d.x;
1863       
1864        var dm = DOM.getAbsoluteDimensions( event.attributeElement );
1865        var adm = DOM.getAbsoluteDimensions( event.attributeElement );
1866       
1867        if ( !this.yLock )
1868            DOM.setTop( this.element,  d.y );
1869        else
1870            DOM.setTop( this.element, adm.absoluteTop );
1871
1872        if ( !this.xLock )
1873            DOM.setLeft( this.element,  d.x );
1874        else
1875            DOM.setLeft( this.element, adm.absoluteLeft );
1876
1877        DOM.setWidth( this.element, dm.offsetWidth );
1878        DOM.setHeight( this.element, dm.offsetHeight );
1879       
1880        var mask = DOM.getElement( "resize-mask" );
1881        mask.insertBefore( this.element, mask.firstChild );
1882        DOM.addClassName( this.element, "moving" );
1883        DOM.removeClassName( mask, "hidden" );
1884        /* TODO autolayout this */
1885        DOM.setHeight( mask, finiteInt( mask.parentNode.clientHeight ) );
1886
1887        return event.stop();
1888    },
1889
1890
1891    eventMouseMove: function( event ) {
1892        if ( !this.dragging )
1893            return;
1894       
1895        var d = DOM.getAbsoluteCursorPosition( event );
1896       
1897        if ( !this.yLock )
1898            DOM.setTop( this.element, d.y );
1899
1900        if ( !this.xLock )
1901            DOM.setLeft( this.element, d.x );
1902
1903        return event.stop();
1904    },
1905
1906
1907    eventMouseUp: function( event ) {
1908        if ( !this.dragging )
1909            return;
1910       
1911        this.dragging = false;
1912        var d = DOM.getAbsoluteCursorPosition( event );
1913           
1914        DOM.addClassName( "resize-mask", "hidden" );
1915       
1916        /* cleanup */
1917        if ( this.element && this.element.parentNode )
1918            this.element.parentNode.removeChild( this.element );
1919
1920        var target = DOM.getElement( this.target );
1921        if ( !target )
1922            return this.reset();
1923
1924        var targetDim = DOM.getDimensions( target );
1925       
1926        var height = d.y - targetDim.offsetTop;
1927        if ( !this.yLock ) {
1928            var hMin = target.getAttribute( "mt:min-height" );
1929            if ( hMin )
1930                if ( height < parseInt( hMin ) )
1931                    height = parseInt( hMin );
1932       
1933            var hMax = target.getAttribute( "mt:max-height" );
1934            if ( hMax )
1935                if ( height > parseInt( hMax ) )
1936                    height = parseInt( hMax );
1937
1938            log('new height: '+height);
1939        }
1940
1941        var width = d.x - targetDim.offsetLeft;
1942        if ( !this.xLock ) {
1943            var wMin = target.getAttribute( "mt:min-width" );
1944            if ( wMin )
1945                if ( width < parseInt( wMin ) )
1946                    width = parseInt( wMin );
1947       
1948            var wMax = target.getAttribute( "mt:max-width" );
1949            if ( wMax )
1950                if ( width > parseInt( wMax ) )
1951                    width = parseInt( wMax );
1952
1953            log('new width: '+width);
1954        }
1955       
1956        /* give the callback a chance to stop us from setting this height and width */
1957        if ( this.callback && ( this.callback( target, this.xStart, this.yStart, d.x, d.y, width, height ) ) )
1958            return this.reset();
1959       
1960        if ( !this.yLock )
1961            DOM.setHeight( target, height );
1962
1963        if ( !this.xLock )
1964            DOM.setWidth( target, width );
1965
1966        var hUpdate = target.getAttribute( "mt:update-field-height" );
1967        if ( hUpdate && ( hUpdate = DOM.getElement( hUpdate ) ) )
1968            hUpdate.value = height;
1969       
1970        var wUpdate = target.getAttribute( "mt:update-field-width" );
1971        if ( wUpdate && ( wUpdate = DOM.getElement( wUpdate ) ) )
1972            wUpdate.value = width;
1973
1974        this.reset();
1975    },
1976
1977
1978    reset: function() {
1979        /* remove left over drag obj, if any */
1980        if ( this.element && this.element.parentNode )
1981            this.element.parentNode.removeChild( this.element );
1982       
1983        this.xStart = this.yStart = this.element = this.target = null;
1984        this.xLock = this.yLock = false;
1985    }
1986
1987
1988} );
1989
1990
1991MT.App.DefaultValue = new Class( Object, {
1992
1993
1994    init: function() {
1995        var es = DOM.getElementsByAttributeAndValue( document, "mt:delegate", "default-value" );
1996        for ( var i = 0; i < es.length; i++ ) {
1997            var val = es[ i ].getAttribute( "mt:default" );
1998            if ( !val )
1999                continue;
2000           
2001            if ( es[ i ].value != val )
2002                DOM.removeClassName( es[ i ], "input-hint" );
2003        }
2004    },
2005
2006
2007    eventFocus: function( event ) {
2008        var element = event.attributeElement;
2009        var val = element.getAttribute( "mt:default" );
2010        if ( !val )
2011            return;
2012
2013        DOM.removeClassName( element, "input-hint" );
2014       
2015        if ( element.value == val )
2016            element.value = "";
2017    },
2018
2019
2020    eventBlur: function( event ) {
2021        var element = event.attributeElement;
2022        var val = element.getAttribute( "mt:default" );
2023        if ( !val )
2024            return;
2025       
2026        var opts = {};
2027        /* simple options for now */
2028        var opt = element.getAttribute( "mt:delegate-options" );
2029        if ( opt && opt == "-class" ) {
2030            opts.noclassChange = true;
2031        }
2032       
2033        if ( element.value != "" )
2034            return;
2035       
2036        element.value = val;
2037        if ( opts.noclassChange )
2038            return;
2039
2040        DOM.addClassName( element, "input-hint" );
2041    },
2042   
2043   
2044    /* hate on IE */
2045    eventFocusIn: function( event ) {
2046        return this.eventFocus( event );
2047    },
2048   
2049
2050    eventFocusOut: function( event ) {
2051        this.eventBlur( event );
2052    },
2053
2054
2055    eventSubmit: function( event ) {
2056        return event.stop();
2057    }
2058   
2059   
2060} );
2061
2062
2063MT.App.TabContainer = new Class( Object, {
2064
2065    init: function() {
2066        var es = DOM.getElementsByAttributeAndValue( document, "mt:delegate", "tab-container" );
2067        var t;
2068        for ( var i = 0; i < es.length; i++ ) {
2069            if ( t = es[ i ].getAttribute( "mt:selected-tab" ) ) {
2070                this.selectTab( es[ i ], t );
2071                continue;
2072            }
2073
2074            if ( t = es[ i ].getAttribute( "mt:persist-tab-cookie" ) ) {
2075                log( 'found persisted tab setting: '+t);
2076                t = Cookie.fetch( t );
2077                if ( t && t.value && t.value != "" ) {
2078                    log( 'cookie: '+t.value);
2079                    this.selectTab( es[ i ], t.value );
2080                }
2081            }
2082        }
2083    },
2084
2085
2086    eventClick: function( event ) {
2087        var command = app.getMouseEventCommand( event );
2088        if (!event.commandElement) return;
2089        var tab = event.commandElement.getAttribute( "mt:tab" );
2090        if ( tab && command != "selectTab" )
2091            this.selectTab( event.attributeElement, tab );
2092
2093        switch( command ) {
2094           
2095            case "setEditorContent":
2096                event.stop();
2097                app.setEditor( "content" );
2098                break;
2099
2100            case "setEditorExtended":
2101                event.stop();
2102                app.setEditor( "extended" );
2103                break;
2104           
2105            case "selectTab":
2106                if ( !tab )
2107                    tab = event.commandElement.getAttribute( "mt:select-tab" );
2108
2109                if ( tab ) {
2110                    this.selectTab( event.attributeElement, tab );
2111                    var cookie = event.attributeElement.getAttribute( "mt:persist-tab-cookie" );
2112                    if ( cookie ) {
2113                        var d = new Date();
2114                        d.setYear( d.getYear() + 1902 ); /* two years */
2115                        Cookie.bake( cookie, tab, undefined, undefined, d );
2116                    }
2117                }
2118               
2119                event.stop();
2120                break;
2121
2122        }
2123    },
2124
2125
2126    selectTab: function( element, name ) {
2127        log('select tab '+name);
2128        var es = DOM.getElementsByAttribute( element, "mt:tab" );
2129        for ( var i = 0; i < es.length; i++ ) {
2130            if ( es[ i ].getAttribute( "mt:tab" ) == name )
2131                DOM.addClassName( es[ i ], "selected-tab" );
2132            else
2133                DOM.removeClassName( es[ i ], "selected-tab" );
2134        }
2135
2136        /* look for tab contents elements matching 'name' */
2137        es = DOM.getElementsByAttribute( element, "mt:tab-content" );
2138        for ( var i = 0; i < es.length; i++ ) {
2139            /* then hide everything except the tab content we want to show */
2140            if ( es[ i ].getAttribute( "mt:tab-content" ) == name )
2141                DOM.removeClassName( es[ i ], "hidden" );
2142            else
2143                DOM.addClassName( es[ i ], "hidden" );
2144        }
2145    }
2146
2147
2148} );
2149
2150
2151MT.App.NavMenu = new Class( Object, {
2152
2153    opened: false,
2154    outTimer: null,
2155    inTimer: null,
2156    el: null,
2157    al: null,
2158
2159
2160    eventMouseOver: function( event ) {
2161        var el = DOM.getFirstAncestorByClassName( event.target, "nav-menu", true );
2162        if ( !el )
2163            return;
2164       
2165        /* if they moused in, but moved to a new menu, reset the in timer */
2166        if ( this.inTimer && this.el && this.el !== el )
2167            this.inTimer.stop();
2168       
2169        this.al = event.attributeElement;
2170        this.el = el;
2171
2172        if ( this.outTimer )
2173            this.outTimer.stop();
2174
2175        if ( this.al.getAttribute( "mt:is-opened" ) == "1" )
2176            return this.openMenu();
2177       
2178        var delay = event.attributeElement.getAttribute( "mt:nav-delayed-open" ); // ms
2179
2180        if ( delay ) {
2181            delay = parseInt( delay );
2182            /* no hover in? */
2183            if ( delay < 0 )
2184                return;
2185            if ( this.inTimer )
2186                this.inTimer.stop();
2187            this.inTimer = new Timer( this.getIndirectMethod( "openMenu" ), delay, 1 );
2188            return;
2189        } else
2190            this.openMenu();
2191    },
2192
2193
2194    eventMouseOut: function( event ) {
2195        var el = DOM.getFirstAncestorByClassName( event.target, "nav-menu", true );
2196        if ( !el && defined( event.relatedTarget ) )
2197            el = DOM.getFirstAncestorByClassName( event.relatedTarget, "nav-menu", true );
2198        if ( !el )
2199            return;
2200
2201        this.al = event.attributeElement;
2202        this.el = el;
2203
2204        var delay = event.attributeElement.getAttribute( "mt:nav-delayed-close" ); // ms
2205        if ( delay ) {
2206            delay = parseInt( delay );
2207            /* no hover out? */
2208            if ( delay < 0 )
2209                return;
2210            if ( this.outTimer )
2211                this.outTimer.stop();
2212            this.outTimer = new Timer( this.getIndirectMethod( "closeMenu" ), delay, 1 );
2213        } else
2214            this.closeMenu();
2215    },
2216
2217
2218    eventClick: function( event ) {
2219        var command = app.getMouseEventCommand( event );
2220        if ( !command ) {
2221            if ( this.inTimer )
2222                this.inTimer.stop();
2223            return;
2224        }
2225
2226        var el = DOM.getFirstAncestorByClassName( event.target, "nav-menu", true );
2227        if ( !el )
2228            return;
2229        this.al = event.<