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

Revision 1280, 94.1 kB (checked in by auno, 23 months ago)

Fixed showing drop down menu when user don't use OK/Cancel button of "Display Options". BugzID:66505

  • 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        if ( this.constructor.Resizer ) {
1340            this.setDelegate( "resizer", new this.constructor.Resizer( this.getIndirectMethod( "resizeComplete" ) ) );
1341            this.setDelegateListener( "eventMouseUp", "resizer" );
1342            this.setDelegateListener( "eventMouseMove", "resizer" );
1343        }
1344
1345        if ( this.constructor.DefaultValue )
1346            this.setDelegate( "defaultValue", new this.constructor.DefaultValue() );
1347       
1348        if ( this.constructor.TabContainer )
1349            this.setDelegate( "tabContainer", new this.constructor.TabContainer() );
1350           
1351        var forms = DOM.getElementsByTagAndAttribute( this.document, "form", "mt:auto-save" );
1352        if ( forms.length )
1353            window.onbeforeunload = this.getIndirectEventListener( "eventBeforeUnload" );
1354
1355        for ( var i = 0; i < forms.length; i++ ) {
1356            var autosave = truth( forms[ i ].getAttribute( "mt:auto-save" ) );
1357            if ( !autosave )
1358                continue;
1359
1360            this.form = forms[ i ];
1361
1362            var ad = forms[ i ].getAttribute( "mt:auto-save-delay" );
1363            var autoSaveDelay;
1364            if ( ad !== null ) {
1365                autoSaveDelay = parseInt( ad ) || 0;
1366                this.autoSaveDelay = autoSaveDelay;
1367            } else
1368                log.warn("auto-save-delay not defined on this form. Defaulting to "+this.autoSaveDelay);
1369
1370            log('using auto save delay: '+this.autoSaveDelay);
1371
1372            var es = Array.fromPseudo(
1373                forms[ i ].getElementsByTagName( "input" ),
1374                forms[ i ].getElementsByTagName( "textarea" )
1375            );
1376            for ( var j = 0; j < es.length; j++ ) {
1377                if ( es[ j ].getAttribute && es[ j ].getAttribute( "mt:watch-change" ) ) {
1378                    log('adding watcher to '+es[ j ].name);
1379                    DOM.addEventListener( es[ j ], "change", this.getIndirectEventListener( "setDirty" ) );
1380                }
1381                if ( autosave && es[ j ].nodeName == "TEXTAREA" ) {
1382                    /* don't attach to the editor textarea in this form */
1383                    if ( this.editor && es[ j ].id == this.editor.textarea.element.id )
1384                        continue;
1385                    DOM.addEventListener( es[ j ], "keydown", this.getIndirectEventListener( "setDirtyKeyDown" ) );
1386                }
1387            }
1388        }
1389
1390        if ( MT.App.dirty )
1391            this.changed = true;   
1392    },
1393
1394
1395    destroyObject: function() {
1396        this.autoSaveReq = null;
1397        this.autoSaveTimer = null;
1398        this.form = null;
1399        this.cpeList = null;
1400        arguments.callee.applySuper( this, arguments );
1401    },
1402
1403
1404    initFormElements: function() {
1405        var forms = document.getElementsByTagName( "form" );
1406        for( var i = 0; i < forms.length; i++ ) {
1407            forms[ i ].submitted = false;
1408            DOM.addEventListener( forms[ i ], "submit", this.getIndirectEventListener( "eventSubmit" ) );
1409            var tareas = forms[ i ].getElementsByTagName( "textarea" );
1410            var tabs = 0;
1411            for ( var j = 0; j < tareas.length; j++ ) {
1412                if ( ( tabs = tareas[ j ].getAttribute( "mt:allow-tabs" ) ) )
1413                    if ( truth ( tabs ) )
1414                        this.attachTabsToTextarea( tareas[ j ] );
1415
1416                if ( tareas[ j ].getAttribute( "mt:editor" ) == "codepress" ) {
1417                    if ( this.constructor.CodePress.isSupported() ) {
1418                        var ed = new this.constructor.CodePress( tareas[ j ] );
1419                        if ( ed ) {
1420                            if ( !this.cpeList )
1421                                this.cpeList = [];
1422                            this.cpeList.push( ed );
1423                        }
1424                    }
1425                }
1426            }
1427        }
1428    },
1429
1430
1431    eventSubmit: function( event ) {
1432        var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1433        if ( !form )
1434            return;
1435           
1436        if ( form.getAttribute( "mt:once" ) ) {
1437       
1438            if ( form.submitted )
1439                return event.stop();
1440       
1441            this.toggleSubmit( form, true );
1442        }
1443
1444        if ( this.cpeList )
1445            this.cpeList.forEach( function( cpe ) { cpe.onSubmit() } );
1446
1447        form.submitted = true;
1448    },
1449
1450
1451    eventBeforeUnload: function( event ) {
1452        if ( this.changed ) {
1453            if ( this.constructor.Editor )
1454                return event.returnValue = Editor.strings.unsavedChanges;
1455            else if ( window.Editor )
1456                return event.returnValue = window.Editor.strings.unsavedChanges;
1457        }
1458       
1459        return undefined;
1460    },
1461
1462
1463    toggleSubmit: function( form, disable ) {
1464        /* sane default */
1465        if ( !disable )
1466            disable = false;
1467        var elements = form.getElementsByTagName( "*" );
1468        for( var i = 0; i < elements.length; i++ ) {
1469            var element = elements[ i ];
1470            var tagName = element.tagName.toLowerCase();
1471            var type = element.getAttribute( "type" );
1472            type = type ? type.toLowerCase() : "";
1473            if ( tagName == "button" || 
1474                (tagName == "input" && (type == "button" || type == "submit" || type == "image")) )
1475                element.disabled = disable;
1476        }
1477    },
1478
1479
1480    closeFlyouts: function( target ) {
1481        var flyout;
1482        var es = Array.fromPseudo( this.openFlyouts );
1483        for ( var i = 0, len = es.length; i < len; i++ ) {
1484            if ( ( flyout = DOM.getElement( es[ i ] ) ) ) {
1485                if ( target && DOM.hasAncestor( target, flyout ) )
1486                    continue;
1487                DOM.addClassName( flyout, "hidden" );
1488                this.openFlyouts.remove( es[ i ] );
1489                showDropDown();
1490            }
1491        }
1492    },
1493
1494
1495    eventClick: function( event ) {
1496        var command = this.getMouseEventCommand( event );
1497
1498        switch( command ) {
1499           
1500            case "openSelectBlog":
1501                app.openDialog( '__mode=dialog_select_weblog&amp;select_favorites=1&return_args='
1502                    + escape( event.commandElement.getAttribute( "mt:href" ) ) );
1503                break;
1504
1505            case "goToLocation":
1506                this.gotoLocation( event.commandElement.getAttribute( "href" ) );
1507                break;
1508           
1509            case "autoSave":
1510                this.autoSave();
1511                break;
1512
1513            case "setModeCodepressOn":
1514                this.cpeList.forEach( function( cpe ) { cpe.toggleOn( true ); } );
1515                break;
1516
1517            case "setModeCodepressOff":
1518                this.cpeList.forEach( function( cpe ) { cpe.toggleOff( true ); } );
1519                break;
1520
1521            case "openFlyout":
1522                var name = event.commandElement.getAttribute( "mt:flyout" );
1523                var el = DOM.getElement( name );
1524                if ( !defined( el ) )
1525                    return;
1526
1527                this.closeFlyouts( event.target );
1528
1529                DOM.removeClassName( el, "hidden" );
1530                this.targetElement = event.target;
1531                this.applyAutolayouts( el );
1532                this.targetElement = null;
1533
1534                hideDropDown();
1535                this.openFlyouts.add( name );
1536
1537                break;
1538               
1539            case "closeFlyout":
1540                this.closeFlyouts();
1541               
1542                break;
1543
1544            default:
1545                this.closeFlyouts( event.target );
1546
1547                var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1548                if ( !form )
1549                    return;
1550
1551                var mode = event.target.getAttribute( "mt:mode" );
1552                if ( !mode && event.commandElement )
1553                    mode = event.commandElement.getAttribute( "mt:mode" );
1554
1555                if ( mode ) {
1556                    log('setting __mode in this form: '+mode);
1557                    var elements = form.getElementsByTagName( "input" );
1558                    for( var i = 0; i < elements.length; i++ ) {
1559                        if ( elements[ i ].name == "__mode" ) {
1560                            log('found __mode element');
1561                            elements[ i ].value = mode;
1562                            break;
1563                        }
1564                    }
1565                }
1566
1567                if ( command == "submit" ) {
1568                    event.stop();
1569                    var msg;
1570                    if ( event.commandElement && ( msg = event.commandElement.getAttribute( "mt:confirm-msg" ) ) )
1571                        if ( !confirm( msg ) )
1572                            return;
1573                    form.submit();
1574                }
1575
1576                return;
1577
1578        }
1579        return event.stop();
1580    },
1581   
1582
1583    /* from blog selector transient */
1584    gotoUrl: function( url ) {
1585        if ( url )
1586            this.gotoLocation( url );
1587    },
1588
1589
1590    toggleActive: function( id ) {
1591        log('toggleactive:'+id);
1592        var div = DOM.getElement( id );
1593        if ( DOM.hasClassName( div, 'active' ) )
1594            DOM.removeClassName( div, 'active' );
1595        else
1596            DOM.addClassName( div, 'active' );
1597    },
1598
1599
1600    openDialog: function( params ) {
1601        this.closeFlyouts();
1602        show("dialog-container");
1603        /* TODO remove this cruft */
1604        /*  handle escape key for closing modal dialog */
1605        DOM.addEventListener( document.body, "keypress", dialogKeyPress, true );
1606        openDialogUrl( ScriptURI + "?" + params );
1607        /* IE hack to get the dialog modal to reflow */
1608        if ( document.all && DOM.getElement( "dialog-container" ) ) {
1609            DOM.addClassName( "dialog-container", "hidden" );
1610            new Timer(function() {
1611                DOM.removeClassName( "dialog-container", "hidden" );
1612            }, 500, 1 );
1613        }
1614    },
1615
1616
1617    attachTabsToTextarea: function( element ) {
1618        DOM.addEventListener( element, "keypress", this.getIndirectMethod( "eventKeyPressAllowTabs" ) );
1619        DOM.addEventListener( element, "keydown", this.getIndirectMethod( "eventKeyDownAllowTabs" ) );
1620    },
1621
1622
1623    eventKeyPressAllowTabs: function( event ) {
1624        if ( event.keyCode == 9 )
1625            return event.stop();
1626    },
1627   
1628   
1629    eventKeyDownAllowTabs: function( event ) {
1630        if ( event.keyCode == 9 ) {
1631                    TC.setSelectionValue( ( event.target || event.srcElement ) , "\t" );
1632            return false;
1633        }
1634    },
1635
1636
1637    resizeComplete: function( target, xStart, yStart, x, y, width, height ) {
1638       
1639        switch ( target.id ) {
1640            case "textarea-enclosure":
1641                var es = [ "text", "text_cpe" ];
1642                for ( var i = 0; i < es.length; i++ ) {
1643                    es[ i ] = DOM.getElement( es[ i ] );
1644                    if ( es[ i ] )
1645                        DOM.setHeight( es[ i ], height );
1646                }
1647                break;
1648
1649            /* expand here */
1650        }
1651
1652    },
1653   
1654   
1655    autoSave: function() {
1656        var data = DOM.getFormData( this.form );
1657        data["_autosave"] = 1;
1658
1659        if ( this.cpeList )
1660            this.cpeList.forEach( function( cpe ) { cpe.autoSave( data ) } );
1661
1662        /* don't cancel a pending save */
1663        if ( defined( this.autoSaveReq ) )
1664            return;
1665       
1666        var areas = [
1667            DOM.getElement( "autosave-notification" ),
1668            DOM.getElement( "autosave-notification-top" ),
1669            DOM.getElement( "autosave-notification-bottom" )
1670        ];
1671        if ( areas )
1672            for ( var i = 0; i < areas.length; i++ )
1673                if ( areas[ i ] )
1674                    areas[ i ].innerHTML = Template.process( "autoSave", { saving: true } );
1675
1676        this.autoSaveReq = TC.Client.call({
1677            load: this.getIndirectMethod( "autoSaveComplete" ),
1678            error: this.getIndirectMethod( "autoSaveError" ),
1679            method: 'POST',
1680            uri: this.form.action,
1681            arguments: data
1682        });
1683    },
1684
1685
1686    autoSaveComplete: function( c, r ) {
1687        this.autoSaveTimer = this.autoSaveReq = undefined;
1688
1689        log('auto save complete '+r);
1690        if ( r != "true" )
1691            return log.error( "Error auto-saving post: "+r );
1692       
1693        var areas = [
1694            DOM.getElement( "autosave-notification" ),
1695            DOM.getElement( "autosave-notification-top" ),
1696            DOM.getElement( "autosave-notification-bottom" )
1697        ];
1698        var d = new Date();
1699        if ( areas )
1700            for ( var i = 0; i < areas.length; i++ )
1701                if ( areas[ i ] )
1702                    areas[ i ].innerHTML = Template.process( "autoSave", {
1703                        saving: false,
1704                        hh: d.getHours().toString().pad( 2, "0" ),
1705                        mm: d.getMinutes().toString().pad( 2, "0" ),
1706                        ss: d.getSeconds().toString().pad( 2, "0" )
1707                    } );
1708    },
1709
1710   
1711    autoSaveError: function( c, r ) {
1712        this.autoSaveTimer = this.autoSaveReq = undefined;
1713       
1714        log.error( "Error auto-saving post" );
1715        var areas = [
1716            DOM.getElement( "autosave-notification" ),
1717            DOM.getElement( "autosave-notification-top" ),
1718            DOM.getElement( "autosave-notification-bottom" )
1719        ];
1720        if ( areas )
1721            for ( var i = 0; i < areas.length; i++ )
1722                if ( areas[ i ] )
1723                    areas[ i ].innerHTML = ''
1724    },
1725
1726
1727    setDirtyKeyDown: function( event ) {
1728        if ( this.dirtyKeyDownTimer )
1729            this.dirtyKeyDownTimer.stop();
1730        this.dirtyKeyDownTimer = new Timer( this.getIndirectMethod( "setDirty" ), 5000, 1 );
1731    },
1732
1733
1734    setDirty: function( event ) {
1735        var autoSaveDelay = this.autoSaveDelay;
1736        if ( event && event.target ) {
1737            var form = DOM.getFirstAncestorByTagName( event.target, "form", true );
1738            if ( form ) {
1739                log('found dirty form: '+form);
1740                this.form = form;
1741                if ( autoSaveDelay = parseInt( form.getAttribute( "mt:auto-save-delay" ) ) || 0 ) {
1742                    this.autoSaveDelay = autoSaveDelay;
1743                    log('using auto save delay: '+this.autoSaveDelay);
1744                }
1745            }
1746        }
1747           
1748        this.changed = true;
1749        if ( autoSaveDelay < 1 )
1750            return;
1751
1752        if ( defined( this.autoSaveTimer ) )
1753            return this.autoSaveTimer.reset();
1754        this.autoSaveTimer = new Timer( this.getIndirectMethod( "autoSave" ), autoSaveDelay, 1 );
1755    },
1756
1757
1758    clearDirty: function( event ) {
1759        this.changed = false;
1760    },
1761
1762   
1763    insertCode: function( code ) {
1764        if ( this.cpeList )
1765            this.cpeList[ 0 ].insertCode( code );
1766        else if ( this.editor )
1767            this.editor.insertHTML( code );
1768        else {
1769            var txt = DOM.getElement( "text" );
1770            setSelection( txt, code );
1771            DOM.focus( txt );
1772        }
1773    }
1774   
1775
1776} );
1777
1778if ( window.Calendar ) {
1779
1780MT.App.Calendar = new Class( Calendar, {
1781
1782
1783    open: function( data, callback ) {
1784        if ( data.date && data.date.length )
1785            data.date = data.date.replace( /^(\S+).*/, "$1" );
1786        arguments.callee.applySuper( this, arguments );
1787       
1788        /* reset invalid dates to the current date */
1789        if ( !this.dateObject )
1790            this.dateObject = new Date();
1791    },
1792
1793
1794    eventClick: function() {
1795        arguments.callee.applySuper( this, arguments );
1796        if ( this.callback )
1797            this.callback( this.dateObject );
1798    }
1799
1800
1801} );
1802
1803}
1804
1805
1806
1807
1808MT.App.Resizer = new Class( Object, {
1809
1810
1811    dragging: false,
1812    element: null,
1813   
1814    xLock: false,
1815    yLock: false,
1816
1817    xStart: null,
1818    yStart: null,
1819
1820
1821    init: function( callback ) {
1822        if ( callback )
1823            this.callback = callback;
1824    },
1825
1826
1827    destroy: function() {
1828        this.callback = null;
1829    },
1830
1831
1832    eventMouseDown: function( event ) {
1833        this.dragging = true;
1834       
1835        this.reset();
1836       
1837        this.target = event.attributeElement.getAttribute( "mt:target" );
1838
1839        /* x or y locking */
1840        var lock = event.attributeElement.getAttribute( "mt:lock" );
1841        if ( lock ) {
1842            if ( lock == "x" || lock == "X" )
1843                this.xLock = true;
1844            else if ( lock == "y" || lock == "Y" )
1845                this.yLock = true;
1846        }
1847       
1848        /* clone the drag node */
1849        this.element = event.attributeElement.cloneNode( true );
1850        /* using the current mouse position, set the positon of the drag obj */
1851        var d = DOM.getAbsoluteCursorPosition( event );
1852        this.yStart = d.y;
1853        this.xStart = d.x;
1854       
1855        var dm = DOM.getAbsoluteDimensions( event.attributeElement );
1856        var adm = DOM.getAbsoluteDimensions( event.attributeElement );
1857       
1858        if ( !this.yLock )
1859            DOM.setTop( this.element,  d.y );
1860        else
1861            DOM.setTop( this.element, adm.absoluteTop );
1862
1863        if ( !this.xLock )
1864            DOM.setLeft( this.element,  d.x );
1865        else
1866            DOM.setLeft( this.element, adm.absoluteLeft );
1867
1868        DOM.setWidth( this.element, dm.offsetWidth );
1869        DOM.setHeight( this.element, dm.offsetHeight );
1870       
1871        var mask = DOM.getElement( "resize-mask" );
1872        mask.insertBefore( this.element, mask.firstChild );
1873        DOM.addClassName( this.element, "moving" );
1874        DOM.removeClassName( mask, "hidden" );
1875        /* TODO autolayout this */
1876        DOM.setHeight( mask, finiteInt( mask.parentNode.clientHeight ) );
1877
1878        return event.stop();
1879    },
1880
1881
1882    eventMouseMove: function( event ) {
1883        if ( !this.dragging )
1884            return;
1885       
1886        var d = DOM.getAbsoluteCursorPosition( event );
1887       
1888        if ( !this.yLock )
1889            DOM.setTop( this.element, d.y );
1890
1891        if ( !this.xLock )
1892            DOM.setLeft( this.element, d.x );
1893
1894        return event.stop();
1895    },
1896
1897
1898    eventMouseUp: function( event ) {
1899        if ( !this.dragging )
1900            return;
1901       
1902        this.dragging = false;
1903        var d = DOM.getAbsoluteCursorPosition( event );
1904           
1905        DOM.addClassName( "resize-mask", "hidden" );
1906       
1907        /* cleanup */
1908        if ( this.element && this.element.parentNode )
1909            this.element.parentNode.removeChild( this.element );
1910
1911        var target = DOM.getElement( this.target );
1912        if ( !target )
1913            return this.reset();
1914
1915        var targetDim = DOM.getDimensions( target );
1916       
1917        var height = d.y - targetDim.offsetTop;
1918        if ( !this.yLock ) {
1919            var hMin = target.getAttribute( "mt:min-height" );
1920            if ( hMin )
1921                if ( height < parseInt( hMin ) )
1922                    height = parseInt( hMin );
1923       
1924            var hMax = target.getAttribute( "mt:max-height" );
1925            if ( hMax )
1926                if ( height > parseInt( hMax ) )
1927                    height = parseInt( hMax );
1928
1929            log('new height: '+height);
1930        }
1931
1932        var width = d.x - targetDim.offsetLeft;
1933        if ( !this.xLock ) {
1934            var wMin = target.getAttribute( "mt:min-width" );
1935            if ( wMin )
1936                if ( width < parseInt( wMin ) )
1937                    width = parseInt( wMin );
1938       
1939            var wMax = target.getAttribute( "mt:max-width" );
1940            if ( wMax )
1941                if ( width > parseInt( wMax ) )
1942                    width = parseInt( wMax );
1943
1944            log('new width: '+width);
1945        }
1946       
1947        /* give the callback a chance to stop us from setting this height and width */
1948        if ( this.callback && ( this.callback( target, this.xStart, this.yStart, d.x, d.y, width, height ) ) )
1949            return this.reset();
1950       
1951        if ( !this.yLock )
1952            DOM.setHeight( target, height );
1953
1954        if ( !this.xLock )
1955            DOM.setWidth( target, width );
1956
1957        var hUpdate = target.getAttribute( "mt:update-field-height" );
1958        if ( hUpdate && ( hUpdate = DOM.getElement( hUpdate ) ) )
1959            hUpdate.value = height;
1960       
1961        var wUpdate = target.getAttribute( "mt:update-field-width" );
1962        if ( wUpdate && ( wUpdate = DOM.getElement( wUpdate ) ) )
1963            wUpdate.value = width;
1964
1965        this.reset();
1966    },
1967
1968
1969    reset: function() {
1970        /* remove left over drag obj, if any */
1971        if ( this.element && this.element.parentNode )
1972            this.element.parentNode.removeChild( this.element );
1973       
1974        this.xStart = this.yStart = this.element = this.target = null;
1975        this.xLock = this.yLock = false;
1976    }
1977
1978
1979} );
1980
1981
1982MT.App.DefaultValue = new Class( Object, {
1983
1984
1985    init: function() {
1986        var es = DOM.getElementsByAttributeAndValue( document, "mt:delegate", "default-value" );
1987        for ( var i = 0; i < es.length; i++ ) {
1988            var val = es[ i ].getAttribute( "mt:default" );
1989            if ( !val )
1990                continue;
1991           
1992            if ( es[ i ].value != val )
1993                DOM.removeClassName( es[ i ], "input-hint" );
1994        }
1995    },
1996
1997
1998    eventFocus: function( event ) {
1999        var element = event.attributeElement;
2000        var val = element.getAttribute( "mt:default" );
2001        if ( !val )
2002            return;
2003
2004        DOM.removeClassName( element, "input-hint" );
2005       
2006        if ( element.value == val )
2007            element.value = "";
2008    },
2009
2010
2011    eventBlur: function( event ) {
2012        var element = event.attributeElement;
2013        var val = element.getAttribute( "mt:default" );
2014        if ( !val )
2015            return;
2016       
2017        var opts = {};
2018        /* simple options for now */
2019        var opt = element.getAttribute( "mt:delegate-options" );
2020        if ( opt && opt == "-class" ) {
2021            opts.noclassChange = true;
2022        }
2023       
2024        if ( element.value != "" )
2025            return;
2026       
2027        element.value = val;
2028        if ( opts.noclassChange )
2029            return;
2030
2031        DOM.addClassName( element, "input-hint" );
2032    },
2033   
2034   
2035    /* hate on IE */
2036    eventFocusIn: function( event ) {
2037        return this.eventFocus( event );
2038    },
2039   
2040
2041    eventFocusOut: function( event ) {
2042        this.eventBlur( event );
2043    },
2044
2045
2046    eventSubmit: function( event ) {
2047        return event.stop();
2048    }
2049   
2050   
2051} );
2052
2053
2054MT.App.TabContainer = new Class( Object, {
2055
2056    init: function() {
2057        var es = DOM.getElementsByAttributeAndValue( document, "mt:delegate", "tab-container" );
2058        var t;
2059        for ( var i = 0; i < es.length; i++ ) {
2060            if ( t = es[ i ].getAttribute( "mt:selected-tab" ) ) {
2061                this.selectTab( es[ i ], t );
2062                continue;
2063            }
2064
2065            if ( t = es[ i ].getAttribute( "mt:persist-tab-cookie" ) ) {
2066                log( 'found persisted tab setting: '+t);
2067                t = Cookie.fetch( t );
2068                if ( t && t.value && t.value != "" ) {
2069                    log( 'cookie: '+t.value);
2070                    this.selectTab( es[ i ], t.value );
2071                }
2072            }
2073        }
2074    },
2075
2076
2077    eventClick: function( event ) {
2078        var command = app.getMouseEventCommand( event );
2079        if (!event.commandElement) return;
2080        var tab = event.commandElement.getAttribute( "mt:tab" );
2081        if ( tab && command != "selectTab" )
2082            this.selectTab( event.attributeElement, tab );
2083
2084        switch( command ) {
2085           
2086            case "setEditorContent":
2087                event.stop();
2088                app.setEditor( "content" );
2089                break;
2090
2091            case "setEditorExtended":
2092                event.stop();
2093                app.setEditor( "extended" );
2094                break;
2095           
2096            case "selectTab":
2097                if ( !tab )
2098                    tab = event.commandElement.getAttribute( "mt:select-tab" );
2099
2100                if ( tab ) {
2101                    this.selectTab( event.attributeElement, tab );
2102                    var cookie = event.attributeElement.getAttribute( "mt:persist-tab-cookie" );
2103                    if ( cookie ) {
2104                        var d = new Date();
2105                        d.setYear( d.getYear() + 1902 ); /* two years */
2106                        Cookie.bake( cookie, tab, undefined, undefined, d );
2107                    }
2108                }
2109               
2110                event.stop();
2111                break;
2112
2113        }
2114    },
2115
2116
2117    selectTab: function( element, name ) {
2118        log('select tab '+name);
2119        var es = DOM.getElementsByAttribute( element, "mt:tab" );
2120        for ( var i = 0; i < es.length; i++ ) {
2121            if ( es[ i ].getAttribute( "mt:tab" ) == name )
2122                DOM.addClassName( es[ i ], "selected-tab" );
2123            else
2124                DOM.removeClassName( es[ i ], "selected-tab" );
2125        }
2126
2127        /* look for tab contents elements matching 'name' */
2128        es = DOM.getElementsByAttribute( element, "mt:tab-content" );
2129        for ( var i = 0; i < es.length; i++ ) {
2130            /* then hide everything except the tab content we want to show */
2131            if ( es[ i ].getAttribute( "mt:tab-content" ) == name )
2132                DOM.removeClassName( es[ i ], "hidden" );
2133            else
2134                DOM.addClassName( es[ i ], "hidden" );
2135        }
2136    }
2137
2138
2139} );
2140
2141
2142MT.App.NavMenu = new Class( Object, {
2143
2144    opened: false,
2145    outTimer: null,
2146    inTimer: null,
2147    el: null,
2148    al: null,
2149
2150
2151    eventMouseOver: function( event ) {
2152        var el = DOM.getFirstAncestorByClassName( event.target, "nav-menu", true );
2153        if ( !el )
2154            return;
2155       
2156        /* if they moused in, but moved to a new menu, reset the in timer */
2157        if ( this.inTimer && this.el && this.el !== el )
2158            this.inTimer.stop();
2159       
2160        this.al = event.attributeElement;
2161        this.el = el;
2162
2163        if ( this.outTimer )
2164            this.outTimer.stop();
2165
2166        if ( this.al.getAttribute( "mt:is-opened" ) == "1" )
2167            return this.openMenu();
2168       
2169        var delay = event.attributeElement.getAttribute( "mt:nav-delayed-open" ); // ms
2170
2171        if ( delay ) {
2172            delay = parseInt( delay );
2173            /* no hover in? */
2174            if ( delay < 0 )
2175                return;
2176            if ( this.inTimer )
2177                this.inTimer.stop();
2178            this.inTimer = new Timer( this.getIndirectMethod( "openMenu" ), delay, 1 );
2179            return;
2180        } else
2181            this.openMenu();
2182    },
2183
2184
2185    eventMouseOut: function( event ) {
2186        var el = DOM.getFirstAncestorByClassName( event.target, "nav-menu", true );
2187        if ( !el && defined( event.relatedTarget ) )
2188            el = DOM.getFirstAncestorByClassName( event.relatedTarget, "nav-menu", true );
2189        if ( !el )
2190            return;
2191
2192        this.al = event.attributeElement;
2193        this.el = el;
2194
2195        var delay = event.attributeElement.getAttribute( "mt:nav-delayed-close" ); // ms
2196        if ( delay ) {
2197            delay = parseInt( delay );
2198            /* no hover out? */
2199            if ( delay < 0 )
2200                return;
2201            if ( this.outTimer )
2202                this.outTimer.stop();
2203            this.outTimer = new Timer( this.getIndirectMethod( "closeMenu" ), delay, 1 );
2204        } else
2205            this.closeMenu();
2206    },
2207
2208
2209    eventClick: function( event ) {
2210        var command = app.getMouseEventCommand( event );
2211        if ( !command ) {
2212            if ( this.inTimer )
2213                this.inTimer.stop();
2214            return;
2215        }
2216
2217        var el = DOM.getFirstAncestorByClassName( event.target, "nav-menu", true );
2218        if ( !el )
2219            return;
2220        this.al = event.attributeElement;
2221
2222        if ( command == "openMenu" ) {
2223            event.stop();
2224            this.openMenu( el );
2225        }
2226    },
2227
2228
2229    openMenu: function() {
2230        if ( !this.el )
2231            return;
2232
2233        if ( this.outTimer )
2234            this.outTimer.stop();