root/branches/release-41/mt-static/mt.js @ 2735

Revision 2735, 95.8 kB (checked in by bsmith, 17 months ago)

bugzid:80375 - if element is hidden no need to apply display:none

  • 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 (window.app) window.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
577
578function toggleDisable(id, state) {
579    var id = DOM.getElement( id );
580    if ((id.disabled && state) || !id)
581        return false;
582    else if (!id.disabled && !state)
583        return false;
584    if ( !id.disabled )
585        id.disabled="disabled";
586    else
587        id.disabled="";
588}
589
590function toggleDisplayOptions() {
591    return toggleActive('display-options');
592}
593
594function toggleEntryDisplayOptions() {
595    return toggleActive('display-options-widget');
596}
597
598function toggleActive( id ) {
599    var id = DOM.getElement( id );
600    if ( !id )
601        return false;
602    if ( DOM.hasClassName( id, 'active' ) )
603        DOM.removeClassName( id, 'active' );
604    else
605        DOM.addClassName( id, 'active' );
606    return false;
607}
608
609function toggleHidden( id ) {
610    var id = DOM.getElement( id );
611    if ( !id )
612        return false;
613    if ( DOM.hasClassName( id, 'hidden' ) )
614        DOM.removeClassName( id, 'hidden' );
615    else
616        DOM.addClassName( id, 'hidden' );
617    return false;
618}
619
620function toggle( id ) {
621    var id = DOM.getElement( id );
622    if ( !id )
623        return false;
624    if ( DOM.hasClassName( id, 'hidden' ) ) {
625        DOM.removeClassName( id, 'hidden' );
626        DOM.addClassName( id, 'active' );
627    } else {
628        DOM.removeClassName( id, 'active' );
629        DOM.addClassName( id, 'hidden' );
630    }
631    return false;
632}
633
634function toggleClass( id, c ) {
635    if ( !id )
636        return false;
637    if ( DOM.hasClassName( id, c ) ) {
638        DOM.removeClassName( id, c );
639    } else {
640        DOM.addClassName( id, c );
641    }
642    return false;
643}
644
645function tabToggle(selectedTab, tabs) {
646    log.warn('tabToggle is deprecated. use mt tab delegate');
647    for (var i = 0; i < tabs.length; i++) {
648        var tabObject = getByID(tabs[i] + '-tab');
649        var contentObject = getByID(tabs[i] + '-panel');
650           
651        if (tabObject && contentObject) {
652            if (tabs[i] == selectedTab) {
653                DOM.addClassName( tabObject, 'selected-tab' );
654                DOM.addClassName( contentObject, 'selected-tab-panel' );
655            } else {
656                DOM.removeClassName( tabObject, 'selected-tab' );
657                DOM.removeClassName( contentObject, 'selected-tab-panel' );
658            }
659        }
660    }
661    return false;
662}
663
664function show(id, d, style) {
665    var el = getByID(id, d);
666    if (!el) return;
667    if ( DOM.hasClassName( el, "hidden" ) ) {
668        DOM.removeClassName ( el, "hidden");
669    } else {
670        el.style.display = style ? style : 'block';
671    }
672    /* hack */
673    if ( DOM.hasClassName( el, "autolayout-height-parent" ) )
674        DOM.setHeight( el, finiteInt( el.parentNode.clientHeight ) );
675}
676
677function hide(id, d) {
678    var el = getByID(id, d);
679    if (!el) return;
680    if ( DOM.hasClassName( el, "hidden" ) ) {
681        return false;
682    } else {
683        DOM.addClassName ( el, "hidden");
684    }
685    if ( window.app )
686        app.reflow();
687}
688
689function showReply(id, d, style) {
690    var el = getByID(id, d);
691    if (!el) return;
692    el.style.visibility = style ? style : 'visible';
693}
694
695function hideReply(id, d) {
696    var el = getByID(id, d);
697    if (!el) return;
698    el.style.visibility = 'hidden';
699}
700
701function toggleSubPrefs(c) {
702    var div = TC.elementOrId((c.name || c.id)+"-prefs") || TC.elementOrId((c.name || c.id)+'_prefs');
703    if (div) {
704        if (c.type) {
705            var on = c.type == 'checkbox' ? c.checked : c.value != 0;
706            if (on) {
707                TC.removeClassName(div, "hidden");
708            } else {
709                TC.addClassName(div, "hidden");
710            }
711            // div.style.display = on ? "block" : "none";
712        } else {
713            var on = div.style.display && div.style.display != "none";
714            if (on) {
715                TC.addClassName(div, "hidden");
716            } else {
717                TC.removeClassName(div, "hidden");
718            }
719            // div.style.display = on ? "none" : "block";
720        }
721    }
722    return false;
723}
724
725function toggleAdvancedPrefs(evt, c) {
726    evt = evt || window.event;
727    var id;
728    var obj;
729    if (!c || (typeof c != 'string')) {
730        c = c || evt.target || evt.srcElement;
731        id = c.id || c.name;
732        obj = c;
733    } else {
734        id = c;
735    }
736    var div = getByID( id + '-advanced');
737    if (div) {
738        if (obj) {
739            var shiftKey = evt ? evt.shiftKey : undefined;
740                if (evt && shiftKey && obj.type == 'checkbox')
741                obj.checked = true;
742            var on = obj.type == 'checkbox' ? obj.checked : obj.value != 0;
743            if (on && shiftKey) {
744                if (div.style.display == "block")
745                    div.style.display = "none";
746                else
747                    div.style.display = "block";
748            } else {
749                div.style.display = "none";
750            }
751        } else {
752            if (div.style.display == "block")
753                div.style.display = "none";
754            else
755                div.style.display = "block";
756        }
757    }
758    return false;
759}
760
761function trans(str) {
762    if (Lexicon && Lexicon[str])
763        str = Lexicon[str];
764    if (arguments.length > 1)
765        for (var i = 1; i <= arguments.length; i++) {
766            /* This matches [_#] or [_#:comment] */
767            str = str.replace(new RegExp('\\[_' + i + '(?:\:[^\\]]+)?\\]', 'g'), arguments[i]);
768            var re = new RegExp('\\[quant,_' + i + ',(.+?)(?:,(.+?))?(?:\:[^\\]]+)?\\]');
769            var matches;
770            while (matches = str.match(re)) {
771                if (arguments[i] != 1)
772                    str = str.replace(re, arguments[i] + ' ' +
773                        ((typeof(matches[2]) != 'undefined') ? matches[2]
774                                                               : matches[1]
775                                                                 + 's'));
776                else
777                    str = str.replace(re, arguments[i] + ' ' + matches[1]);
778            }
779        }
780    return str;
781}
782
783function junkScoreNudge(amount, id, max) {
784    if (max == undefined) max = 10;
785    var fld = getByID(id);
786    score = fld.value;
787    score.replace(/\+/, '');
788    score = parseFloat(score) + amount;
789    if (isNaN(score)) score = amount;
790    if (score > max) score = max;
791    if (score < 0) score = 0;
792    fld.value = score;
793    return false;
794}
795
796var dirify_table = {
797    "\u00C0": 'A',    // A`
798    "\u00E0": 'a',    // a`
799    "\u00C1": 'A',    // A'
800    "\u00E1": 'a',    // a'
801    "\u00C2": 'A',    // A^
802    "\u00E2": 'a',    // a^
803    "\u0102": 'A',    // latin capital letter a with breve
804    "\u0103": 'a',    // latin small letter a with breve
805    "\u00C6": 'AE',   // latin capital letter AE
806    "\u00E6": 'ae',   // latin small letter ae
807    "\u00C5": 'A',    // latin capital letter a with ring above
808    "\u00E5": 'a',    // latin small letter a with ring above
809    "\u0100": 'A',    // latin capital letter a with macron
810    "\u0101": 'a',    // latin small letter a with macron
811    "\u0104": 'A',    // latin capital letter a with ogonek
812    "\u0105": 'a',    // latin small letter a with ogonek
813    "\u00C4": 'A',    // A:
814    "\u00E4": 'a',    // a:
815    "\u00C3": 'A',    // A~
816    "\u00E3": 'a',    // a~
817    "\u00C8": 'E',    // E`
818    "\u00E8": 'e',    // e`
819    "\u00C9": 'E',    // E'
820    "\u00E9": 'e',    // e'
821    "\u00CA": 'E',    // E^
822    "\u00EA": 'e',    // e^
823    "\u00CB": 'E',    // E:
824    "\u00EB": 'e',    // e:
825    "\u0112": 'E',    // latin capital letter e with macron
826    "\u0113": 'e',    // latin small letter e with macron
827    "\u0118": 'E',    // latin capital letter e with ogonek
828    "\u0119": 'e',    // latin small letter e with ogonek
829    "\u011A": 'E',    // latin capital letter e with caron
830    "\u011B": 'e',    // latin small letter e with caron
831    "\u0114": 'E',    // latin capital letter e with breve
832    "\u0115": 'e',    // latin small letter e with breve
833    "\u0116": 'E',    // latin capital letter e with dot above
834    "\u0117": 'e',    // latin small letter e with dot above
835    "\u00CC": 'I',    // I`
836    "\u00EC": 'i',    // i`
837    "\u00CD": 'I',    // I'
838    "\u00ED": 'i',    // i'
839    "\u00CE": 'I',    // I^
840    "\u00EE": 'i',    // i^
841    "\u00CF": 'I',    // I:
842    "\u00EF": 'i',    // i:
843    "\u012A": 'I',    // latin capital letter i with macron
844    "\u012B": 'i',    // latin small letter i with macron
845    "\u0128": 'I',    // latin capital letter i with tilde
846    "\u0129": 'i',    // latin small letter i with tilde
847    "\u012C": 'I',    // latin capital letter i with breve
848    "\u012D": 'i',    // latin small letter i with breve
849    "\u012E": 'I',    // latin capital letter i with ogonek
850    "\u012F": 'i',    // latin small letter i with ogonek
851    "\u0130": 'I',    // latin capital letter with dot above
852    "\u0131": 'i',    // latin small letter dotless i
853    "\u0132": 'IJ',   // latin capital ligature ij
854    "\u0133": 'ij',   // latin small ligature ij
855    "\u0134": 'J',    // latin capital letter j with circumflex
856    "\u0135": 'j',    // latin small letter j with circumflex
857    "\u0136": 'K',    // latin capital letter k with cedilla
858    "\u0137": 'k',    // latin small letter k with cedilla
859    "\u0138": 'k',    // latin small letter kra
860    "\u0141": 'L',    // latin capital letter l with stroke
861    "\u0142": 'l',    // latin small letter l with stroke
862    "\u013D": 'L',    // latin capital letter l with caron
863    "\u013E": 'l',    // latin small letter l with caron
864    "\u0139": 'L',    // latin capital letter l with acute
865    "\u013A": 'l',    // latin small letter l with acute
866    "\u013B": 'L',    // latin capital letter l with cedilla
867    "\u013C": 'l',    // latin small letter l with cedilla
868    "\u013F": 'l',    // latin capital letter l with middle dot
869    "\u0140": 'l',    // latin small letter l with middle dot
870    "\u00D2": 'O',    // O`
871    "\u00F2": 'o',    // o`
872    "\u00D3": 'O',    // O'
873    "\u00F3": 'o',    // o'
874    "\u00D4": 'O',    // O^
875    "\u00F4": 'o',    // o^
876    "\u00D6": 'O',    // O:
877    "\u00F6": 'o',    // o:
878    "\u00D5": 'O',    // O~
879    "\u00F5": 'o',    // o~
880    "\u00D8": 'O',    // O/
881    "\u00F8": 'o',    // o/
882    "\u014C": 'O',    // latin capital letter o with macron
883    "\u014D": 'o',    // latin small letter o with macron
884    "\u0150": 'O',    // latin capital letter o with double acute
885    "\u0151": 'o',    // latin small letter o with double acute
886    "\u014E": 'O',    // latin capital letter o with breve
887    "\u014F": 'o',    // latin small letter o with breve
888    "\u0152": 'OE',   // latin capital ligature oe
889    "\u0153": 'oe',   // latin small ligature oe
890    "\u0154": 'R',    // latin capital letter r with acute
891    "\u0155": 'r',    // latin small letter r with acute
892    "\u0158": 'R',    // latin capital letter r with caron
893    "\u0159": 'r',    // latin small letter r with caron
894    "\u0156": 'R',    // latin capital letter r with cedilla
895    "\u0157": 'r',    // latin small letter r with cedilla
896    "\u00D9": 'U',    // U`
897    "\u00F9": 'u',    // u`
898    "\u00DA": 'U',    // U'
899    "\u00FA": 'u',    // u'
900    "\u00DB": 'U',    // U^
901    "\u00FB": 'u',    // u^
902    "\u00DC": 'U',    // U:
903    "\u00FC": 'u',    // u:
904    "\u016A": 'U',    // latin capital letter u with macron
905    "\u016B": 'u',    // latin small letter u with macron
906    "\u016E": 'U',    // latin capital letter u with ring above
907    "\u016F": 'u',    // latin small letter u with ring above
908    "\u0170": 'U',    // latin capital letter u with double acute
909    "\u0171": 'u',    // latin small letter u with double acute
910    "\u016C": 'U',    // latin capital letter u with breve
911    "\u016D": 'u',    // latin small letter u with breve
912    "\u0168": 'U',    // latin capital letter u with tilde
913    "\u0169": 'u',    // latin small letter u with tilde
914    "\u0172": 'U',    // latin capital letter u with ogonek
915    "\u0173": 'u',    // latin small letter u with ogonek
916    "\u00C7": 'C',    // ,C
917    "\u00E7": 'c',    // ,c
918    "\u0106": 'C',    // latin capital letter c with acute
919    "\u0107": 'c',    // latin small letter c with acute
920    "\u010C": 'C',    // latin capital letter c with caron
921    "\u010D": 'c',    // latin small letter c with caron
922    "\u0108": 'C',    // latin capital letter c with circumflex
923    "\u0109": 'c',    // latin small letter c with circumflex
924    "\u010A": 'C',    // latin capital letter c with dot above
925    "\u010B": 'c',    // latin small letter c with dot above
926    "\u010E": 'D',    // latin capital letter d with caron
927    "\u010F": 'd',    // latin small letter d with caron
928    "\u0110": 'D',    // latin capital letter d with stroke
929    "\u0111": 'd',    // latin small letter d with stroke
930    "\u00D1": 'N',    // N~
931    "\u00F1": 'n',    // n~
932    "\u0143": 'N',    // latin capital letter n with acute
933    "\u0144": 'n',    // latin small letter n with acute
934    "\u0147": 'N',    // latin capital letter n with caron
935    "\u0148": 'n',    // latin small letter n with caron
936    "\u0145": 'N',    // latin capital letter n with cedilla
937    "\u0146": 'n',    // latin small letter n with cedilla
938    "\u0149": 'n',    // latin small letter n preceded by apostrophe
939    "\u014A": 'N',    // latin capital letter eng
940    "\u014B": 'n',    // latin small letter eng
941    "\u00DF": 'ss',   // double-s
942    "\u015A": 'S',    // latin capital letter s with acute
943    "\u015B": 's',    // latin small letter s with acute
944    "\u0160": 'S',    // latin capital letter s with caron
945    "\u0161": 's',    // latin small letter s with caron
946    "\u015E": 'S',    // latin capital letter s with cedilla
947    "\u015F": 's',    // latin small letter s with cedilla
948    "\u015C": 'S',    // latin capital letter s with circumflex
949    "\u015D": 's',    // latin small letter s with circumflex
950    "\u0218": 'S',    // latin capital letter s with comma below
951    "\u0219": 's',    // latin small letter s with comma below
952    "\u0164": 'T',    // latin capital letter t with caron
953    "\u0165": 't',    // latin small letter t with caron
954    "\u0162": 'T',    // latin capital letter t with cedilla
955    "\u0163": 't',    // latin small letter t with cedilla
956    "\u0166": 'T',    // latin capital letter t with stroke
957    "\u0167": 't',    // latin small letter t with stroke
958    "\u021A": 'T',    // latin capital letter t with comma below
959    "\u021B": 't',    // latin small letter t with comma below
960    "\u0192": 'f',    // latin small letter f with hook
961    "\u011C": 'G',    // latin capital letter g with circumflex
962    "\u011D": 'g',    // latin small letter g with circumflex
963    "\u011E": 'G',    // latin capital letter g with breve
964    "\u011F": 'g',    // latin small letter g with breve
965    "\u0120": 'G',    // latin capital letter g with dot above
966    "\u0121": 'g',    // latin small letter g with dot above
967    "\u0122": 'G',    // latin capital letter g with cedilla
968    "\u0123": 'g',    // latin small letter g with cedilla
969    "\u0124": 'H',    // latin capital letter h with circumflex
970    "\u0125": 'h',    // latin small letter h with circumflex
971    "\u0126": 'H',    // latin capital letter h with stroke
972    "\u0127": 'h',    // latin small letter h with stroke
973    "\u0174": 'W',    // latin capital letter w with circumflex
974    "\u0175": 'w',    // latin small letter w with circumflex
975    "\u00DD": 'Y',    // latin capital letter y with acute
976    "\u00FD": 'y',    // latin small letter y with acute
977    "\u0178": 'Y',    // latin capital letter y with diaeresis
978    "\u00FF": 'y',    // latin small letter y with diaeresis
979    "\u0176": 'Y',    // latin capital letter y with circumflex
980    "\u0177": 'y',    // latin small letter y with circumflex
981    "\u017D": 'Z',    // latin capital letter z with caron
982    "\u017E": 'z',    // latin small letter z with caron
983    "\u017B": 'Z',    // latin capital letter z with dot above
984    "\u017C": 'z',    // latin small letter z with dot above
985    "\u0179": 'Z',    // latin capital letter z with acute
986    "\u017A": 'z'     // latin small letter z with acute
987};
988
989function dirify (s) {
990    s = s.replace(/<[^>]+>/g, '');
991    for (var p in dirify_table)
992        if (s.indexOf(p) != -1)
993            s = s.replace(new RegExp(p, "g"), dirify_table[p]);
994    s = s.toLowerCase();
995    s = s.replace(/&[^;\s]+;/g, '');
996    s = s.replace(/[^-a-z0-9_ ]/g, '');
997    s = s.replace(/\s+/g, '_');
998    s = s.replace(/_+$/, '');
999    s = s.replace(/_+/g, '_');
1000    return s;
1001}
1002
1003function setElementValue(domID, newVal) {
1004    getByID(domID).value = newVal;
1005}
1006
1007/* pager and datasource */
1008
1009/***
1010 * Datasource class
1011 * A class for navigating and displaying data from an AJAX datasource.
1012 * Methods:
1013 *   constructor(el, datatype): Creates a new datasource using DOM
1014 *     element 'el' as a container for the data (table rows typically)
1015 *     and datatype is used to communicate the '_type' parameter to
1016 *     the server.
1017 *   setPager(pager): Sets a Pager class object which is refreshed
1018 *     upon receiving new data.
1019 *   search(string): Invokes a search on the datasource.
1020 *   navigate(offset): Used to navigate to a particular offset within
1021 *     the dataset.
1022 */
1023Datasource = new Class(Object, {
1024    init: function(el, datatype) {
1025        // this.id = id;
1026        // this.document = doc || document;
1027        this.element = TC.elementOrId(el);
1028        this.searching = false;
1029        this.navigating = false;
1030        this.type = datatype;
1031        this.onUpdate = null;
1032    },
1033    setPager: function(pager, pager2) {
1034        this.pager = pager;
1035        if (pager2) this.pager2 = pager2
1036        if (pager) pager.datasource = this;
1037        if (pager2) pager2.datasource = this;
1038        if (pager) pager.render();
1039        if (pager2) pager2.render();
1040    },
1041    search: function(str) {
1042        if (this.searching) return;
1043
1044        var doc = TC.getOwnerDocument(this.element);
1045        var args = doc.location.search;
1046        args = args.replace(/^\?/, '');
1047        args = args.replace(/&?offset=\d+/, '');
1048        args = 'search=' + escape(str) + (args ? '&' + args : '') + '&json=1';
1049        if (this.type) {
1050            args = args.replace(/&?_type=\w+/, '');
1051            args += '&_type=' + this.type;
1052        }
1053
1054        this.searching = true;
1055        if (this.pager)
1056            this.pager.render();
1057        if (this.pager2)
1058            this.pager2.render();
1059        var self = this;
1060        TC.Client.call({
1061            'load': function(c,r) { self.searched(r); },
1062            'error': function() { alert("Error during search."); self.searched(null); },
1063            'method': 'POST',
1064            'uri': ScriptURI,
1065            'arguments': args
1066        });
1067    },
1068    searched: function(c) {
1069        this.searching = false;
1070        if (c) {
1071            try {
1072                data = eval('(' + c + ')');
1073                this.update(data['html']);
1074                if (this.pager)
1075                    this.pager.setState({});
1076                if (this.pager2)
1077                    this.pager2.setState({});
1078            } catch (e) {
1079                alert("Error in response: " + e);
1080                if (this.pager)
1081                    this.pager.render();
1082                if (this.pager2)
1083                    this.pager2.render();
1084            }
1085        } else {
1086            if (this.pager)
1087                this.pager.render();
1088            if (this.pager2)
1089                this.pager2.render();
1090        }
1091    },
1092    update: function(html) {
1093        if (!this.element) return;
1094        this.element.innerHTML = html;
1095        this.updated();
1096    },
1097    updated: function() {
1098        if (this.onUpdate) this.onUpdate(this);
1099    },
1100    navigate: function(offset) {
1101        if (offset == null) return;
1102        if (this.navigating) return;
1103
1104        var doc = TC.getOwnerDocument(this.element);
1105        var args = doc.location.search;
1106        args = args.replace(/^\?/, '');
1107        //args = args.replace(/&?search=[^&]+/, '');
1108        //args = args.replace(/&?do_search=1/, '');
1109        args = args.replace(/&?offset=\d+/, '');
1110        args = 'offset=' + offset + (args ? '&' + args : '') + '&json=1';
1111        if (this.type) {
1112            args = args.replace(/&?_type=\w+/, '');
1113            args = args + '&_type=' + this.type;
1114        }
1115
1116        this.navigating = true;
1117        if (this.pager)
1118            this.pager.render();
1119        if (this.pager2)
1120            this.pager2.render();
1121        var self = this;
1122        TC.Client.call({
1123            'load': function(c,r) { self.navigated(r); },
1124            'error': function() { alert("Error in request."); self.navigated(null); },
1125            'method': 'POST',
1126            'uri': ScriptURI,
1127            'arguments': args
1128        });
1129        return false;
1130    },
1131    navigated: function(c) {
1132        var data;
1133        this.navigating = false;
1134        if (c) {
1135            try {
1136                data = eval('(' + c + ')');
1137                this.update(data['html']);
1138                if (this.pager)
1139                    this.pager.setState(data['pager']);
1140                if (this.pager2)
1141                    this.pager2.setState(data['pager']);
1142            } catch (e) {
1143                alert("Error in response: " + e);
1144                if (this.pager)
1145                    this.pager.render();
1146                if (this.pager2)
1147                    this.pager2.render();
1148            }
1149        } else {
1150            if (this.pager) this.pager.render();
1151            if (this.pager2) this.pager2.render();
1152        }
1153    }
1154});
1155//These two lines are to translate phrases in list_tags.tmpl
1156//trans("The tag '[_2]' already exists. Are you sure you want to merge '[_1]' with '[_2]'?");
1157//trans("The tag '[_2]' already exists. Are you sure you want to merge '[_1]' with '[_2]' across all weblogs?");
1158
1159/***
1160 * Pager class
1161 * Expects a 'state' object containing:
1162 *   offset: offset into listing (10, means first row displayed is 11)
1163 *   listTotal: total number of rows in dataset
1164 *   rows: number of rows being displayed
1165 *   chronological: boolean, whether listing is reverse-chronological
1166 *     or not.
1167 *  Methods:
1168 *    constructor(el): constructs using DOM element el as a container
1169 *    setDatasource(ds): used to assign a datasource object
1170 *    setState(state): used to update state settings
1171 *    previous: navigates datasource to previous page
1172 *    next: navigates datasource to next page
1173 *    first: navigates datasource to first page
1174 *    last: navigates datasource to last page
1175 *    previousOffset: calculates and returns offset for previous page
1176 *    nextOffset: calculates and returns offset for next page
1177 *    lastOffset: calculates and returns offset for 'last' page
1178 *    render: refreshes the pagination controls
1179 */
1180Pager = new Class(Object, {
1181    init: function(el) {
1182        this.element = TC.elementOrId(el);
1183        this.state = {};
1184    },
1185    setDatasource: function(ds) {
1186        this.datasource = ds;
1187        this.render();
1188    },
1189    setState: function(state) {
1190        this.state = state;
1191        this.render();
1192    },
1193    previous: function(e) {
1194        this.navigate(this.previousOffset());
1195        return TC.stopEvent(e || window.event);
1196    },
1197    navigate: function(offset) {
1198        if (offset == null) return;
1199        if (this.datasource)
1200            return this.datasource.navigate(offset);
1201        // traditional navigation...
1202        var doc = TC.getOwnerDocument(this.element);
1203        new_loc = doc.location.href;
1204        if (this.state.method == 'POST') {
1205            new_loc += '?' + this.state.return_args;
1206        }
1207        new_loc = new_loc.replace(/&?offset=\d+/, '');
1208        new_loc += '&offset=' + offset;
1209        window.location = new_loc;
1210        return false;
1211    },
1212    previousOffset: function() {
1213        if (this.state.offset > 0) {
1214            var offset = this.state.offset - this.state.limit;
1215            if (offset < 0)
1216                offset = 0;
1217            return offset;
1218        }
1219        return null;
1220    },
1221    nextOffset: function() {
1222        if (this.state.listTotal) {
1223            var listStart = (this.state.offset ? this.state.offset : 0) + 1;
1224            var offset = (this.state.offset ? this.state.offset : 0) + this.state.rows;
1225            if (offset >= this.state.listTotal) {
1226                offset = null;
1227            }
1228            return offset;
1229        }
1230        return null;
1231    },
1232    lastOffset: function() {
1233        var offset = 0;
1234        if (this.state.listTotal) {
1235            var listStart = (this.state.offset ? this.state.offset : 0) + 1;
1236            var listEnd = (this.state.offset ? this.state.offset : 0) + this.state.rows;
1237            if (listEnd >= this.state.listTotal) {
1238                offset = null;
1239            } else {
1240                offset = this.state.listTotal - this.state.rows;
1241                if (offset < listStart)
1242                    offset = null;
1243            }
1244            return offset;
1245        }
1246        return null;
1247    },
1248    next: function(e) {
1249        this.navigate(this.nextOffset());
1250        return TC.stopEvent(e || window.event);
1251    },
1252    first: function(e) {
1253        this.navigate(0);
1254        return TC.stopEvent(e || window.event);
1255    },
1256    last: function(e) {
1257        this.navigate(this.lastOffset());
1258        return TC.stopEvent(e || window.event);
1259    },
1260    render: function() {
1261        if (!this.element) return;
1262
1263        /*
1264        This long method is concerned with creating the elements of
1265        the pagination control. It refreshes the controls based on
1266        the 'state' member of the Pager object. This control is
1267        typically tied to a Datasource object. So the navigation
1268        links of the control will influence the Datasource.
1269        Likewise, upon navigating the Datasource, it will invoke
1270        the pager to refresh when the data has been updated.
1271
1272        pager.rows (number of rows shown)
1273        pager.listTotal (total number of rows in datasource)
1274        pager.offset (offset currently used)
1275        pager.chronological (boolean, whether the listing is chronological or not)
1276        */
1277        var html = '';
1278        /* TODO - this can all be replaced with a js template */
1279        if (this.datasource && this.datasource.navigating) {
1280            // TODO: change this to use a CSS class instead.
1281            html = "<div>" + trans('Loading...') + " <img src=\"" + StaticURI + "images/indicator.white.gif\" height=\"10\" width=\"10\" alt=\"...\" /></div>";
1282            this.element.innerHTML = html;
1283        } else if ((this.state.rows != null) && (this.state.rows > 0)) {
1284            this.element.innerHTML = '';
1285            var listStart = (this.state.offset ? this.state.offset : 0) + 1;
1286            var listEnd = (this.state.offset ? this.state.offset : 0) + this.state.rows;
1287
1288            var doc = TC.getOwnerDocument(this.element);
1289            var self = this;
1290            // pagination control structure
1291            if (this.state.offset > 0) {
1292                var link = doc.createElement('a');
1293                link.href = 'javascript:void(0)';
1294                link.onclick = function(e) { return self.first(e) };
1295                link.className = 'start';
1296                link.innerHTML = '<em>&lt;&lt;</em>&nbsp;';
1297                this.element.appendChild(link);
1298            } else {
1299                var txt = doc.createElement('span');
1300                txt.className = 'start-disabled';
1301                txt.innerHTML = '<em>&lt;&lt;</em>&nbsp;';
1302                this.element.appendChild(txt);
1303            }
1304            if (this.previousOffset() != null) {
1305                var link = doc.createElement('a');
1306                link.href = 'javascript:void(0)';
1307                link.onclick = function(e) { return self.previous(e) };
1308                link.className = 'to-start';
1309                link.innerHTML = '<em>&lt;</em>&nbsp;';
1310                this.element.appendChild(link);
1311            } else {
1312                var txt = doc.createElement('span');
1313                txt.className = 'to-start-disabled';
1314                txt.innerHTML = '<em>&lt;</em>&nbsp;';
1315                this.element.appendChild(txt);
1316            }
1317            var showing = doc.createElement('span');
1318            showing.className = 'current-rows';
1319            if (this.state.listTotal)
1320                showing.innerHTML = trans('[_1] &ndash; [_2] of [_3]', listStart, listEnd, this.state.listTotal);
1321            else
1322                showing.innerHTML = trans('[_1] &ndash; [_2]', listStart, listEnd);
1323            this.element.appendChild(showing);
1324            if (this.nextOffset() != null) {
1325                var link = doc.createElement('a');
1326                link.href = 'javascript:void(0)';
1327                link.onclick = function(e) { return self.next(e) };
1328                link.className = 'to-end';
1329                link.innerHTML = '&nbsp;<em>&gt;</em>';
1330                this.element.appendChild(link);
1331            } else {
1332                var txt = doc.createElement('span');
1333                txt.className = 'to-end-disabled';
1334                txt.innerHTML = '&nbsp;<em>&gt;</em>';
1335                this.element.appendChild(txt);
1336            }
1337            if (this.lastOffset() != null) {
1338                var link = doc.createElement('a');
1339                link.href = 'javascript:void(0)';
1340                link.onclick = function(e) { return self.last(e) };
1341                link.className = 'end';
1342                link.innerHTML = '&nbsp;<em>&gt;&gt;</em>';
1343                this.element.appendChild(link);
1344            } else {
1345                var txt = doc.createElement('span');
1346                txt.className = 'end-disabled';
1347                txt.innerHTML = '&nbsp;<em>&gt;&gt;</em>';
1348                this.element.appendChild(txt);
1349            }
1350        } else {
1351            this.element.innerHTML = '';
1352        }
1353    }
1354});
1355
1356
1357MT = {};
1358
1359
1360if ( window.App ) {
1361
1362App.singletonConstructor =
1363MT.App = new Class( App, {
1364 
1365
1366    NAMESPACE: "mt",
1367    changed: false,
1368    autoSaveDelay: 15000, /* ms */
1369
1370
1371    initComponents: function() {
1372        arguments.callee.applySuper( this, arguments );
1373        this.openFlyouts = [];
1374
1375        this.setDelegate( "navMenu", new this.constructor.NavMenu() );
1376
1377        this.initFormElements();
1378       
1379        if ( this.constructor.Resizer ) {
1380            this.setDelegate( "resizer", new this.constructor.Resizer( this.getIndirectMethod( "resizeComplete" ) ) );
1381            this.setDelegateListener( "eventMouseUp", "resizer" );
1382            this.setDelegateListener( "eventMouseMove", "resizer" );
1383        }
1384
1385        if ( this.constructor.DefaultValue )
1386            this.setDelegate( "defaultValue", new this.constructor.DefaultValue() );
1387       
1388        if ( this.constructor.TabContainer )
1389            this.setDelegate( "tabContainer", new this.constructor.TabContainer() );
1390           
1391        var forms = DOM.getElementsByTagAndAttribute( this.document, "form", "mt:auto-save" );
1392        if ( forms.length )
1393            window.onbeforeunload = this.getIndirectEventListener( "eventBeforeUnload" );
1394
1395        for ( var i = 0; i < forms.length; i++ ) {
1396            var autosave = truth( forms[ i ].getAttribute( "mt:auto-save" ) );
1397            if ( !autosave )
1398                continue;
1399
1400            this.form = forms[ i ];
1401
1402            var ad = forms[ i ].getAttribute( "mt:auto-save-delay" );
1403            var autoSaveDelay;
1404            if ( ad !== null ) {
1405                autoSaveDelay = parseInt( ad ) || 0;
1406                this.autoSaveDelay = autoSaveDelay;
1407            } else
1408                log.warn("auto-save-delay not defined on this form. Defaulting to "+this.autoSaveDelay);
1409
1410            log('using auto save delay: '+this.autoSaveDelay);
1411
1412            var es = Array.fromPseudo(
1413                forms[ i ].getElementsByTagName( "input" ),
1414                forms[ i ].getElementsByTagName( "textarea" )
1415            );
1416            for ( var j = 0; j < es.length; j++ ) {
1417                if ( es[ j ].getAttribute && es[ j ].getAttribute( "mt:watch-change" ) ) {
1418                    log('adding watcher to '+es[ j ].name);
1419                    DOM.addEventListener( es[ j ], "change", this.getIndirectEventListener( "setDirty" ) );
1420                }
1421                if ( autosave && es[ j ].nodeName == "TEXTAREA" ) {
1422                    /* don't attach to the editor textarea in this form */
1423                    if ( this.editor && es[ j ].id == this.editor.textarea.element.id )
1424                        continue;
1425                    DOM.addEventListener( es[ j ], "keydown", this.getIndirectEventListener( "setDirtyKeyDown" ) );
1426                }
1427            }
1428        }
1429
1430        if ( MT.App.dirty )
1431            this.changed = true;   
1432    },
1433
1434
1435    destroyObject: function() {
1436        this.autoSaveReq = null;
1437        this.autoSaveTimer = null;
1438        this.form = null;
1439        this.cpeList = null;
1440        arguments.callee.applySuper( this, arguments );
1441    },
1442
1443
1444    reflow: function() {
1445        arguments.callee.applySuper( this, arguments );
1446        /* fix a display issue */
1447        var navEl = DOM.getElement( "content-nav" );
1448        var navConEl = DOM.getElement( "content-header-inner" );
1449        if ( navEl && navConEl ) {
1450            var d = DOM.getAbsoluteDimensions( navConEl );
1451            navEl.style.top = "-" + (d.clientHeight - 13) + "px";
1452        }
1453    },
1454
1455
1456    initFormElements: function() {
1457        var forms = document.getElementsByTagName( "form" );
1458        for( var i = 0; i < forms.length; i++ ) {
1459            forms[ i ].submitted = false;
1460            DOM.addEventListener( forms[ i ], "submit", this.getIndirectEventListener( "eventSubmit" ) );
1461            var tareas = forms[ i ].getElementsByTagName( "textarea" );
1462            var tabs = 0;
1463            for ( var j = 0; j < tareas.length; j++ ) {
1464                if ( ( tabs = tareas[ j ].getAttribute( "mt:allow-tabs" ) ) )
1465                    if ( truth ( tabs ) )
1466                        this.attachTabsToTextarea( tareas[ j ] );
1467
1468                if ( tareas[ j ].getAttribute( "mt:editor" ) == "codepress" ) {
1469                    if ( this.constructor.CodePress.isSupported() ) {
1470                        var ed = new this.constructor.CodePress( tareas[ j ] );
1471                        if ( ed ) {
1472                            if ( !this.cpeList )
1473                                this.cpeList = [];
1474                            this.cpeList.push( ed );
1475                        }
1476                    }
1477                }
1478            }
1479        }
1480    },
1481
1482
1483    eventSubmit: function( event ) {
1484        var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1485        if ( !form )
1486            return;
1487           
1488        if ( form.getAttribute( "mt:once" ) ) {
1489       
1490            if ( form.submitted )
1491                return event.stop();
1492       
1493            this.toggleSubmit( form, true );
1494        }
1495
1496        if ( this.cpeList )
1497            this.cpeList.forEach( function( cpe ) { cpe.onSubmit() } );
1498
1499        form.submitted = true;
1500    },
1501
1502
1503    eventBeforeUnload: function( event ) {
1504        if ( this.changed ) {
1505            if ( this.constructor.Editor )
1506                return event.returnValue = Editor.strings.unsavedChanges;
1507            else if ( window.Editor )
1508                return event.returnValue = window.Editor.strings.unsavedChanges;
1509        }
1510       
1511        return undefined;
1512    },
1513
1514
1515    toggleSubmit: function( form, disable ) {
1516        /* sane default */
1517        if ( !disable )
1518            disable = false;
1519        var elements = form.getElementsByTagName( "*" );
1520        for( var i = 0; i < elements.length; i++ ) {
1521            var element = elements[ i ];
1522            var tagName = element.tagName.toLowerCase();
1523            var type = element.getAttribute( "type" );
1524            type = type ? type.toLowerCase() : "";
1525            if ( tagName == "button" || 
1526                (tagName == "input" && (type == "button" || type == "submit" || type == "image")) )
1527                element.disabled = disable;
1528        }
1529    },
1530
1531
1532    closeFlyouts: function( target ) {
1533        var flyout;
1534        var es = Array.fromPseudo( this.openFlyouts );
1535        for ( var i = 0, len = es.length; i < len; i++ ) {
1536            if ( ( flyout = DOM.getElement( es[ i ] ) ) ) {
1537                if ( target && DOM.hasAncestor( target, flyout ) )
1538                    continue;
1539                DOM.addClassName( flyout, "hidden" );
1540                this.openFlyouts.remove( es[ i ] );
1541                showAllDropDown();
1542            }
1543        }
1544    },
1545
1546
1547    eventClick: function( event ) {
1548        var command = this.getMouseEventCommand( event );
1549
1550        switch( command ) {
1551           
1552            case "openSelectBlog":
1553                app.openDialog( '__mode=dialog_select_weblog&amp;select_favorites=1&return_args='
1554                    + escape( event.commandElement.getAttribute( "mt:href" ) ) );
1555                break;
1556
1557            case "goToLocation":
1558                this.gotoLocation( event.commandElement.getAttribute( "href" ) );
1559                break;
1560           
1561            case "autoSave":
1562                this.autoSave();
1563                break;
1564
1565            case "setModeCodepressOn":
1566                this.cpeList.forEach( function( cpe ) { cpe.toggleOn( true ); } );
1567                break;
1568
1569            case "setModeCodepressOff":
1570                this.cpeList.forEach( function( cpe ) { cpe.toggleOff( true ); } );
1571                break;
1572
1573            case "openFlyout":
1574                var name = event.commandElement.getAttribute( "mt:flyout" );
1575                var el = DOM.getElement( name );
1576                if ( !defined( el ) )
1577                    return;
1578
1579                this.closeFlyouts( event.target );
1580
1581                DOM.removeClassName( el, "hidden" );
1582                this.targetElement = event.target;
1583                this.applyAutolayouts( el );
1584                this.targetElement = null;
1585
1586                hideAllDropDown();
1587                showDropDown( el );
1588                this.openFlyouts.add( name );
1589
1590                break;
1591               
1592            case "closeFlyout":
1593                this.closeFlyouts();
1594               
1595                break;
1596
1597            default:
1598                this.closeFlyouts( event.target );
1599
1600                var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1601                if ( !form )
1602                    return;
1603
1604                var mode = event.target.getAttribute( "mt:mode" );
1605                if ( !mode && event.commandElement )
1606                    mode = event.commandElement.getAttribute( "mt:mode" );
1607
1608                if ( mode ) {
1609                    log('setting __mode in this form: '+mode);
1610                    var elements = form.getElementsByTagName( "input" );
1611                    for( var i = 0; i < elements.length; i++ ) {
1612                        if ( elements[ i ].name == "__mode" ) {
1613                            log('found __mode element');
1614                            elements[ i ].value = mode;
1615                            break;
1616                        }
1617                    }
1618                }
1619
1620                if ( command == "submit" ) {
1621                    event.stop();
1622                    var msg;
1623                    if ( event.commandElement && ( msg = event.commandElement.getAttribute( "mt:confirm-msg" ) ) )
1624                        if ( !confirm( msg ) )
1625                            return;
1626                    form.submit();
1627                }
1628
1629                return;
1630
1631        }
1632        return event.stop();
1633    },
1634   
1635
1636    /* from blog selector transient */
1637    gotoUrl: function( url ) {
1638        if ( url )
1639            this.gotoLocation( url );
1640    },
1641
1642
1643    toggleActive: function( id ) {
1644        log('toggleactive:'+id);
1645        var div = DOM.getElement( id );
1646        if ( DOM.hasClassName( div, 'active' ) )
1647            DOM.removeClassName( div, 'active' );
1648        else
1649            DOM.addClassName( div, 'active' );
1650    },
1651
1652
1653    openDialog: function( params ) {
1654        this.closeFlyouts();
1655        show("dialog-container");
1656        /* TODO remove this cruft */
1657        /*  handle escape key for closing modal dialog */
1658        DOM.addEventListener( document.body, "keypress", dialogKeyPress, true );
1659        openDialogUrl( ScriptURI + "?" + params );
1660        /* IE hack to get the dialog modal to reflow */
1661        if ( document.all && DOM.getElement( "dialog-container" ) ) {
1662            DOM.addClassName( "dialog-container", "hidden" );
1663            new Timer(function() {
1664                DOM.removeClassName( "dialog-container", "hidden" );
1665            }, 500, 1 );
1666        }
1667    },
1668
1669
1670    attachTabsToTextarea: function( element ) {
1671        DOM.addEventListener( element, "keypress", this.getIndirectMethod( "eventKeyPressAllowTabs" ) );
1672        DOM.addEventListener( element, "keydown", this.getIndirectMethod( "eventKeyDownAllowTabs" ) );
1673    },
1674
1675
1676    eventKeyPressAllowTabs: function( event ) {
1677        if ( event.keyCode == 9 )
1678            return event.stop();
1679    },
1680   
1681   
1682    eventKeyDownAllowTabs: function( event ) {
1683        if ( event.keyCode == 9 ) {
1684                    TC.setSelectionValue( ( event.target || event.srcElement ) , "\t" );
1685            return false;
1686        }
1687    },
1688
1689
1690    resizeComplete: function( target, xStart, yStart, x, y, width, height ) {
1691       
1692        switch ( target.id ) {
1693            case "textarea-enclosure":
1694                var es = [ "text", "text_cpe" ];
1695                for ( var i = 0; i < es.length; i++ ) {
1696                    es[ i ] = DOM.getElement( es[ i ] );
1697                    if ( es[ i ] )
1698                        DOM.setHeight( es[ i ], height );
1699                }
1700                break;
1701
1702            /* expand here */
1703        }
1704
1705    },
1706   
1707   
1708    autoSave: function() {
1709        var data = DOM.getFormData( this.form );
1710        data["_autosave"] = 1;
1711
1712        if ( this.cpeList )
1713            this.cpeList.forEach( function( cpe ) { cpe.autoSave( data ) } );
1714
1715        /* don't cancel a pending save */
1716        if ( defined( this.autoSaveReq ) )
1717            return;
1718       
1719        var areas = [
1720            DOM.getElement( "autosave-notification" ),
1721            DOM.getElement( "autosave-notification-top" ),
1722            DOM.getElement( "autosave-notification-bottom" )
1723        ];
1724        if ( areas )
1725            for ( var i = 0; i < areas.length; i++ )
1726                if ( areas[ i ] )
1727                    areas[ i ].innerHTML = Template.process( "autoSave", { saving: true } );
1728
1729        this.autoSaveReq = TC.Client.call({
1730            load: this.getIndirectMethod( "autoSaveComplete" ),
1731            error: this.getIndirectMethod( "autoSaveError" ),
1732            method: 'POST',
1733            uri: this.form.action,
1734            arguments: data
1735        });
1736    },
1737
1738
1739    autoSaveComplete: function( c, r ) {
1740        this.autoSaveTimer = this.autoSaveReq = undefined;
1741
1742        log('auto save complete '+r);
1743        if ( r != "true" )
1744            return log.error( "Error auto-saving post: "+r );
1745       
1746        var areas = [
1747            DOM.getElement( "autosave-notification" ),
1748            DOM.getElement( "autosave-notification-top" ),
1749            DOM.getElement( "autosave-notification-bottom" )
1750        ];
1751        var d = new Date();
1752        if ( areas )
1753            for ( var i = 0; i < areas.length; i++ )
1754                if ( areas[ i ] )
1755                    areas[ i ].innerHTML = Template.process( "autoSave", {
1756                        saving: false,
1757                        hh: d.getHours().toString().pad( 2, "0" ),
1758                        mm: d.getMinutes().toString().pad( 2, "0" ),
1759                        ss: d.getSeconds().toString().pad( 2, "0" )
1760                    } );
1761    },
1762
1763   
1764    autoSaveError: function( c, r ) {
1765        this.autoSaveTimer = this.autoSaveReq = undefined;
1766       
1767        log.error( "Error auto-saving post" );
1768        var areas = [
1769            DOM.getElement( "autosave-notification" ),
1770            DOM.getElement( "autosave-notification-top" ),
1771            DOM.getElement( "autosave-notification-bottom" )
1772        ];
1773        if ( areas )
1774            for ( var i = 0; i < areas.length; i++ )
1775                if ( areas[ i ] )
1776                    areas[ i ].innerHTML = ''
1777    },
1778
1779
1780    setDirtyKeyDown: function( event ) {
1781        if ( this.dirtyKeyDownTimer )
1782            this.dirtyKeyDownTimer.stop();
1783        this.dirtyKeyDownTimer = new Timer( this.getIndirectMethod( "setDirty" ), 5000, 1 );
1784    },
1785
1786
1787    setDirty: function( event ) {
1788        var autoSaveDelay = this.autoSaveDelay;
1789        if ( event && event.target ) {
1790            var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1791            if ( form ) {
1792                log('found dirty form: '+form);
1793                this.form = form;
1794                if ( autoSaveDelay = parseInt( form.getAttribute( "mt:auto-save-delay" ) ) || 0 ) {
1795                    this.autoSaveDelay = autoSaveDelay;
1796                    log('using auto save delay: '+this.autoSaveDelay);
1797                }
1798            }
1799        }
1800           
1801        this.changed = true;
1802        if ( autoSaveDelay < 1 )
1803            return;
1804
1805        if ( defined( this.autoSaveTimer ) )
1806            return this.autoSaveTimer.reset();
1807        this.autoSaveTimer = new Timer( this.getIndirectMethod( "autoSave" ), autoSaveDelay, 1 );
1808    },
1809
1810
1811    clearDirty: function( event ) {
1812        this.changed = false;
1813    },
1814
1815   
1816    insertCode: function( code ) {
1817        if ( this.cpeList )
1818            this.cpeList[ 0 ].insertCode( code );
1819        else if ( this.editor )
1820            this.editor.insertHTML( code );
1821        else {
1822            var txt = DOM.getElement( "text" );
1823            setSelection( txt, code );
1824            DOM.focus( txt );
1825        }
1826    }
1827   
1828
1829} );
1830
1831if ( window.Calendar ) {
1832
1833MT.App.Calendar = new Class( Calendar, {
1834
1835
1836    open: function( data, callback ) {
1837        if ( data.date && data.date.length )
1838            data.date = data.date.replace( /^(\S+).*/, "$1" );
1839        arguments.callee.applySuper( this, arguments );
1840       
1841        /* reset invalid dates to the current date */
1842        if ( !this.dateObject )
1843            this.dateObject = new Date();
1844    },
1845
1846
1847    eventClick: function() {
1848        arguments.callee.applySuper( this, arguments );
1849        if ( this.callback )
1850            this.callback( this.dateObject );
1851    }
1852
1853
1854} );
1855
1856}
1857
1858
1859
1860
1861MT.App.Resizer = new Class( Object, {
1862
1863
1864    dragging: false,
1865    element: null,
1866   
1867    xLock: false,
1868    yLock: false,
1869
1870    xStart: null,
1871    yStart: null,
1872
1873
1874    init: function( callback ) {
1875        if ( callback )
1876            this.callback = callback;
1877    },
1878
1879
1880    destroy: function() {
1881        this.callback = null;
1882    },
1883
1884
1885    eventMouseDown: function( event ) {
1886        this.dragging = true;
1887       
1888        this.reset();
1889       
1890        this.target = event.attributeElement.getAttribute( "mt:target" );
1891
1892        /* x or y locking */
1893        var lock = event.attributeElement.getAttribute( "mt:lock" );
1894        if ( lock ) {
1895            if ( lock == "x" || lock == "X" )
1896                this.xLock = true;
1897            else if ( lock == "y" || lock == "Y" )
1898                this.yLock = true;
1899        }
1900       
1901        /* clone the drag node */
1902        this.element = event.attributeElement.cloneNode( true );
1903        /* using the current mouse position, set the positon of the drag obj */
1904        var d = DOM.getAbsoluteCursorPosition( event );
1905        this.yStart = d.y;
1906        this.xStart = d.x;
1907       
1908        var dm = DOM.getAbsoluteDimensions( event.attributeElement );
1909        var adm = DOM.getAbsoluteDimensions( event.attributeElement );
1910       
1911        if ( !this.yLock )
1912            DOM.setTop( this.element,  d.y );
1913        else
1914            DOM.setTop( this.element, adm.absoluteTop );
1915
1916        if ( !this.xLock )
1917            DOM.setLeft( this.element,  d.x );
1918        else
1919            DOM.setLeft( this.element, adm.absoluteLeft );
1920
1921        DOM.setWidth( this.element, dm.offsetWidth );
1922        DOM.setHeight( this.element, dm.offsetHeight );
1923       
1924        var mask = DOM.getElement( "resize-mask" );
1925        mask.insertBefore( this.element, mask.firstChild );
1926        DOM.addClassName( this.element, "moving" );
1927        DOM.removeClassName( mask, "hidden" );
1928        /* TODO autolayout this */
1929        DOM.setHeight( mask, finiteInt( mask.parentNode.clientHeight ) );
1930
1931        return event.stop();
1932    },
1933
1934
1935    eventMouseMove: function( event ) {
1936        if ( !this.dragging )
1937            return;
1938       
1939        var d = DOM.getAbsoluteCursorPosition( event );
1940       
1941        if ( !this.yLock )
1942            DOM.setTop( this.element, d.y );
1943
1944        if ( !this.xLock )
1945            DOM.setLeft( this.element, d.x );
1946
1947        return event.stop();
1948    },
1949
1950
1951    eventMouseUp: function( event ) {
1952        if ( !this.dragging )
1953            return;
1954       
1955        this.dragging = false;
1956        var d = DOM.getAbsoluteCursorPosition( event );
1957           
1958        DOM.addClassName( "resize-mask", "hidden" );
1959       
1960        /* cleanup */
1961        if ( this.element && this.element.parentNode )
1962            this.element.parentNode.removeChild( this.element );
1963
1964        var target = DOM.getElement( this.target );
1965        if ( !target )
1966            return this.reset();
1967
1968        var targetDim = DOM.getDimensions( target );
1969       
1970        var height = d.y - targetDim.offsetTop;
1971        if ( !this.yLock ) {
1972            var hMin = target.getAttribute( "mt:min-height" );
1973            if ( hMin )
1974                if ( height < parseInt( hMin ) )
1975                    height = parseInt( hMin );
1976       
1977            var hMax = target.getAttribute( "mt:max-height" );
1978            if ( hMax )
1979                if ( height > parseInt( hMax ) )
1980                    height = parseInt( hMax );
1981
1982            log('new height: '+height);
1983        }
1984
1985        var width = d.x - targetDim.offsetLeft;
1986        if ( !this.xLock ) {
1987            var wMin = target.getAttribute( "mt:min-width" );
1988            if ( wMin )
1989                if ( width < parseInt( wMin ) )
1990                    width = parseInt( wMin );
1991       
1992            var wMax = target.getAttribute( "mt:max-width" );
1993            if ( wMax )
1994                if ( width > parseInt( wMax ) )
1995                    width = parseInt( wMax );
1996
1997            log('new width: '+width);
1998        }
1999       
2000        /* give the callback a chance to stop us from setting this height and width */
2001        if ( this.callback && ( this.callback( target, this.xStart, this.yStart, d.x, d.y, width, height ) ) )
2002            return this.reset();
2003       
2004        if ( !this.yLock )
2005            DOM.setHeight( target, height );
2006
2007        if ( !this.xLock )
2008            DOM.setWidth( target, width );
2009
2010        var hUpdate = target.getAttribute( "mt:update-field-height" );
2011        if ( hUpdate && ( hUpdate = DOM.getElement( hUpdate ) ) )
2012            hUpdate.value = height;
2013       
2014        var wUpdate = target.getAttribute( "mt:update-field-width" );
2015        if ( wUpdate && ( wUpdate = DOM.getElement( wUpdate ) ) )
2016            wUpdate.value = width;
2017
2018        this.reset();
2019    },
2020
2021
2022    reset: function() {
2023        /* remove left over drag obj, if any */
2024        if ( this.element && this.element.parentNode )
2025            this.element.parentNode.removeChild( this.element );
2026       
2027        this.xStart = this.yStart = this.element = this.target = null;
2028        this.xLock = this.yLock = false;
2029    }
2030
2031
2032} );
2033
2034
2035MT.App.DefaultValue = new Class( Object, {
2036
2037
2038    init: function() {
2039        var es = DOM.getElementsByAttributeAndValue( document, "mt:delegate", "default-value" );
2040        for ( var i = 0; i < es.length; i++ ) {
2041            var val = es[ i ].getAttribute( "mt:default" );
2042            if ( !val )
2043                continue;
2044           
2045            if ( es[ i ].value != val )
2046                DOM.removeClassName( es[ i ], "input-hint" );
2047        }
2048    },
2049
2050
2051    eventFocus: function( event ) {
2052        var element = event.attributeElement;
2053        var val = element.getAttribute( "mt:default" );
2054        if ( !val )
2055            return;
2056
2057        DOM.removeClassName( element, "input-hint" );
2058       
2059        if ( element.value == val )
2060            element.value = "";
2061    },
2062
2063
2064    eventBlur: function( event ) {
2065        var element = event.attributeElement;
2066        var val = element.getAttribute( "mt:default" );
2067        if ( !val )
2068            return;
2069       
2070        var opts = {};
2071        /* simple options for now */
2072        var opt = element.getAttribute( "mt:delegate-options" );
2073        if ( opt && opt == "-class" ) {
2074            opts.noclassChange = true;
2075        }
2076       
2077        if ( element.value != "" )
2078            return;
2079       
2080        element.value = val;
2081        if ( opts.noclassChange )
2082            return;
2083
2084        DOM.addClassName( element, "input-hint" );
2085    },
2086   
2087   
2088    /* hate on IE */
2089    eventFocusIn: function( event ) {
2090        return this.eventFocus( event );
2091    },
2092   
2093
2094    eventFocusOut: function( event ) {
2095        this.eventBlur( event );
2096    },
2097
2098
2099    eventSubmit: function( event ) {
2100        return event.stop();
2101    }
2102   
2103   
2104} );
2105
2106
2107MT.App.TabContainer = new Class( Object, {
2108
2109    init: function() {
2110        var es = DOM.getElementsByAttributeAndValue( document, "mt:delegate", "tab-container" );
2111        var t;
2112        for ( var i = 0; i < es.length; i++ ) {
2113            if ( t = es[ i ].getAttribute( "mt:selected-tab" ) ) {
2114                this.selectTab( es[ i ], t );
2115                continue;
2116            }
2117
2118            if ( t = es[ i ].getAttribute( "mt:persist-tab-cookie" ) ) {
2119                log( 'found persisted tab setting: '+t);
2120                t = Cookie.fetch( t );
2121                if ( t && t.value && t.value != "" ) {
2122                    log( 'cookie: '+t.value);
2123                    this.selectTab( es[ i ], t.value );
2124                }
2125            }
2126        }
2127    },
2128
2129
2130    eventClick: function( event ) {
2131        var command = app.getMouseEventCommand( event );
2132        if (!event.commandElement) return;
2133        var tab = event.commandElement.getAttribute( "mt:tab" );
2134        if ( tab && command != "selectTab" )
2135            this.selectTab( event.attributeElement, tab );
2136
2137        switch( command ) {
2138           
2139            case "setEditorContent":
2140                event.stop();
2141                app.setEditor( "content" );
2142                break;
2143
2144            case "setEditorExtended":
2145                event.stop();
2146                app.setEditor( "extended" );
2147                break;
2148           
2149            case "selectTab":
2150                if ( !tab )
2151                    tab = event.commandElement.getAttribute( "mt:select-tab" );
2152
2153                if ( tab ) {
2154                    this.selectTab( event.attributeElement, tab );
2155                    var cookie = event.attributeElement.getAttribute( "mt:persist-tab-cookie" );
2156                    if ( cookie ) {
2157                        var d = new Date();
2158                        d.setYear( d.getYear() + 1902 ); /* two years */
2159                        Cookie.bake( cookie, tab, undefined, undefined, d );
2160                    }
2161                }
2162               
2163                event.stop();
2164                break;
2165
2166        }
2167    },
2168
2169
2170    selectTab: function( element, name ) {
2171        log('select tab '+name);
2172        var es = DOM.getElementsByAttribute( element, "mt:tab" );
2173        for ( var i = 0; i < es.length; i++ ) {
2174            if ( es[ i ].getAttribute( "mt:tab" ) == name )
2175                DOM.addClassName( es[ i ], "selected-tab" );
2176            else
2177                DOM.removeClassName( es[ i ], "selected-tab" );
2178        }
2179
2180        /* look for tab contents elements matching 'name' */
2181        es = DOM.getElementsByAttribute( element, "mt:tab-content" );
2182        for ( var i = 0; i < es.length; i++ ) {
2183            /* then hide everything except the tab content we want to show */
2184            if ( es[ i ].getAttribute( "mt:tab-content" ) == name )
2185                DOM.removeClassName( es[ i ], "hidden" );
2186            else
2187                DOM.addClassName( es[ i ], "hidden" );
2188        }
2189    }
2190
2191
2192} );
2193
2194
2195MT.App.NavMenu = new Class( Object, {
2196
2197    opened: false,
2198    outTimer: null,
2199    inTimer: null,
2200    el: null,
2201    al: null,
2202
2203
2204    eventMouseOver: function( event ) {
2205        var el = DOM.getFirstAncestorByClassName( event.target, "nav-menu", true );
2206        if ( !el )
2207            return;
2208       
2209        /* if they moused in, but moved to a new menu, reset the in timer */
2210        if ( this.inTimer && this.el && this.el !== el )
2211            this.inTimer.stop();
2212       
2213        this.al = event.attributeElement;
2214        this.el = el;
2215
2216        if ( this.outTimer )
2217            this.outTimer.stop();
2218
2219        if ( this.al.getAttribute( "mt:is-opened" ) == "1" )
2220            return this.openMenu();
2221       
2222        var delay = event.attributeElement.getAttribute( "mt:nav-delayed-open" ); // ms
2223
2224        if ( delay ) {
2225            delay = parseInt( delay );
2226            /* no hover in? */
2227            if ( delay < 0 )
2228                return;
2229            if ( this.inTimer )
2230                this.inTimer.stop();
2231            this.inTimer = new Timer( this.getIndirectMethod( "openMenu" ), delay, 1 );
2232            return;