root/trunk/mt-static/mt.js

Revision 4879, 96.8 kB (checked in by takayama, 5 weeks ago)

* Removed error message. bugid:102827

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