/*
 * Adams & Remers
 *
 * PreviewTextEditor.js: editing toolbar for PreviewText
 */

function PreviewTextEditor(textarea) {
    this.textarea = textarea;
    this.initToolbar();
    this.fillToolbar();
}

PreviewTextEditor.method('initToolbar', function () {
    if (!document.createElement) return;
    if (!this.textarea.parentNode) return;
    if (!this.textarea.parentNode.insertBefore) return;

    // Create a wrapper div and put the textarea inside it.
    this.wrapper = document.createElement('div');
    this.wrapper.className = 'previewtext-editor';
    this.textarea.parentNode.insertBefore(this.wrapper, this.textarea);
    this.wrapper.appendChild(this.textarea);

    // Create the toolbar before the textarea.
    this.toolbar = document.createElement('ul');
    this.toolbar.className = 'toolbar';
    this.wrapper.insertBefore(this.toolbar, this.textarea);
});

PreviewTextEditor.method('addToolbarButton', function (cls, text, tooltip, handler) {
    if (!document.createElement) return;
    if (!document.createTextNode) return;
    if (!this.toolbar) return;
    if (!this.toolbar.appendChild) return;

    var button = document.createElement('li');
    var link = document.createElement('a');
    button.className = cls;
    link.appendChild(document.createTextNode(text));
    link.setAttribute('href', 'javascript:;');
    link.setAttribute('title', tooltip);
    addEvent(link, 'click', handler);
    button.appendChild(link);
    this.toolbar.appendChild(button);
});

PreviewTextEditor.method('fillToolbar', function () {
    this.addToolbarButton('h1', 'Heading 1',        'Heading (level 1)',
        this.changeSelectionHandler('\n\n== ', ' ==\n\n',
            false, 'Enter text for heading:', null, false));
    this.addToolbarButton('h2', 'Heading 2',        'Heading (level 2)',
        this.changeSelectionHandler('\n\n=== ', ' ===\n\n',
            false, 'Enter text for heading:', null, false));
    this.addToolbarButton('h3', 'Heading 3',        'Heading (level 3)',
        this.changeSelectionHandler('\n\n==== ', ' ====\n\n',
            false, 'Enter text for heading:', null, false));

    this.addToolbarButton('ul', 'Bulleted list',    'Bulleted list',
        this.changeSelectionHandler('', '', '\n* ', false,
            this.linePrefixer('* ', false), false));
    this.addToolbarButton('ol', 'Numbered list',    'Numbered list',
        this.changeSelectionHandler('', '', '\n# ', false,
            this.linePrefixer('# ', false), false));
    this.addToolbarButton('dl', 'Definition list',  'Definition list',
        this.changeSelectionHandler('; ', ' : ',
            '; definition : term', false, null, false));

    this.addToolbarButton('bq', 'Block quote',      'Block quote',
        this.changeSelectionHandler('', '', '\n: ', false,
            this.linePrefixer(': ', true), false));
    this.addToolbarButton('co', 'Callout box',      'Callout box',
        this.changeSelectionHandler('\n\n{{{ ', ' }}}\n\n',
            false, false, null, false));

    this.addToolbarButton('se', 'Strong emphasis',  'Strong emphasis',
        this.changeSelectionHandler('*', '*', false, false, null, false));
    this.addToolbarButton('we', 'Weak emphasis',    'Weak emphasis',
        this.changeSelectionHandler('_', '_', false, false, null, false));

    this.addToolbarButton('tb', 'Link/image',       'Insert link or image',
        this.toolboxHandler());
    this.addToolbarButton('xl', 'External link',    'Insert link to external website',
        this.externalLinkHandler());
});

// Update the textarea as described below, as needed for several toolbar
// buttons.
//  - If text is selected, put start_code and end_code at the start and
//    end of the selection. If transform is a function, apply it to the
//    selected text. However if replace is true and template is non-null,
//    replace selected text with the template, ignoring the start and
//    end codes.
//  - If no text is selected, the behaviour depends on the template and
//    prompt arguments.
//     - If template is a string, insert it at the current position
//       without the start and end codes.
//     - If template is null, then if prompt is false the start and end
//       codes will be inserted and the cursor left between them. If
//       prompt is a string, use it as the question in a prompt popup
//       window, and insert the text entered surrounded by the start
//       and end codes.

PreviewTextEditor.method('changeSelection',
  function (start_code, end_code, template, prompt, transform, insert) {
    if (!this.textarea) return;

    if (document.selection && document.selection.createRange) {
        // Internet Explorer
        var selection, seltext, newtext;

        selection = document.selection.createRange();
        if (selection.parentElement() == this.textarea) {
            seltext = selection.text;
        } else {
            seltext = '';
        }

        if (template && (insert || !seltext)) {
            newtext = template;
        } else if (prompt && !seltext) {
            seltext = window.prompt(prompt, '');
            newtext = start_code + seltext + end_code;
        } else {
            if (transform) {
                newtext = start_code + transform(seltext) + end_code;
            } else {
                newtext = start_code + seltext + end_code;
            }
        }

        this.textarea.focus();
        document.selection.createRange().text = newtext;
        if (typeof this.textarea.caretPos != 'undefined') {
            this.textarea.caretPos = document.selection.createRange().duplicate();
        }

    } else if (typeof this.textarea.selectionStart != 'undefined') {
        // Mozilla
        var startPos, endPos, scrollTop, before, after, seltext, newtext;

        start_pos = this.textarea.selectionStart;
        end_pos = this.textarea.selectionEnd;
        scroll_top = this.textarea.scrollTop;
        before = this.textarea.value.substring(0, start_pos);
        after = this.textarea.value.substring(end_pos, this.textarea.value.length);

        // Find the current selection, or prompt for text if needed
        if (start_pos == end_pos) {
            if (template) {
                newtext = template;
            } else if (prompt) {
                seltext = window.prompt(prompt, '');
                newtext = start_code + seltext + end_code;
            } else {
                seltext = '';
                newtext = start_code + end_code;
            }
        } else {
            if (template && insert) {
                newtext = template;
            } else {
                seltext = this.textarea.value.substring(start_pos, end_pos);
                if (transform) {
                    newtext = start_code + transform(seltext) + end_code;
                } else {
                    newtext = start_code + seltext + end_code;
                }
            }
        }

        // Replace text
        this.textarea.value = before + newtext + after;

        // Move selection or caret to desired place - if text was selected
        // before, select the whole replaced section; if not place the caret
        // before the closing tag.
        this.textarea.focus();
        if ((start_pos != end_pos) || template) {
            this.textarea.selectionStart = before.length;
            this.textarea.selectionEnd = before.length + newtext.length;
        } else {
            this.textarea.selectionStart = before.length + start_code.length
              + seltext.length;
            this.textarea.selectionEnd = this.textarea.selectionStart;
        }
        this.textarea.scroll_top = scroll_top;
    }
});

PreviewTextEditor.method('changeSelectionHandler',
  function (start_code, end_code, template, prompt, transform, insert) {
    var editor = this;
    return function () {
        editor.changeSelection(start_code, end_code, template, prompt,
                               transform, insert);
    };
});

// Create a transformation function for use with changeSelection.
// The function will put the prefix at the start of each line in its
// argument. If blocks_only is true this will only apply to lines
// after another blank line.

PreviewTextEditor.method('linePrefixer', function (prefix, blocks_only) {
    var re, replacement;

    if (blocks_only) {
        re = new RegExp('(^|(\\r?\\n){2})', 'g');
        replacement = '\n\n' + prefix;
    } else {
        re = new RegExp('(^|\\r?\\n)', 'g');
        replacement = '\n' + prefix;
    }

    return function (s) {
        return s.replace(re, replacement);
    };
});


/*
 * Toolbox
 */

/* The toolbox is a popup window that allows the editor to navigate the site
   hierarchy and insert links to other pages in the site and images. The 
   toolbox HTML is in the skin template previewtext_editor_toolbox, and is
   adapted from the toolbox that comes with Epoz.

   When the user selects a document or image in the toolbox, it calls a
   JavaScript function on its opening window, either PreviewTextEditorInsertLink
   or PreviewTextEditorInsertImage. This is called on the window since
   the toolbox doesn't have a direct reference to the editor object.
   When we open the toolbox, install functions from this editor in the
   window, so subsequent clicks get back to this editor. This may cause
   confusion on pages where there is more than one PreviewTextEditor and
   two toolboxes are open simultaneously. */

PreviewTextEditor.method('toolboxHandler', function () {
    var editor = this;
    return function () {
        window.PreviewTextEditorInsertLink = editor.toolboxInsertLink();
        window.PreviewTextEditorInsertImage = editor.toolboxInsertImage();
        window.open('previewtext_editor_toolbox',
                    'PreviewTextEditorToolbox',
                    'toolbar=no,location=no,status=no,menubar=no,directories=no,scollbars=yes,resizable=yes,width=400,height=400');
    };
});

PreviewTextEditor.method('toolboxInsertLink', function () {
    var editor = this;
    return function (url) {
        editor.changeSelection('[[' + url + '|', ']]', null,
                               'Enter link text:', null, false);
    };
});

PreviewTextEditor.method('toolboxInsertImage', function () {
    var editor = this;
    return function (url) {
        var alt_text = window.prompt('Enter alternate text for image: (displayed when image cannot be shown, eg in text-only browsers)', '');
        var caption = window.prompt('Enter caption for image, or leave blank for no caption:', '');
        var format = window.prompt('Enter image alignment (left or right):', 'right');
        if (caption) {
            var image_code = '[[Image:' + url + '|' + alt_text + '|' + format + '|' + caption + ']]';
        } else {
            var image_code = '[[Image:' + url + '|' + alt_text + '|' + format + ']]';
        }
        editor.changeSelection('', '', image_code, null, null, true);
    };
});


/*
 * External links
 */

PreviewTextEditor.method('externalLinkHandler', function () {
    var editor = this;
    return function () {
        var address = window.prompt('Enter the full URL of the link target, including the protocol (eg http://) at the start:', '');
        if (!address) return;
        editor.changeSelection('[[' + address + '|', ']]', null,
                               'Enter link text:', null, false);
    };
});


/*
 * Create PreviewTextEditors for all textareas with class="previewtext"
 */

function initPreviewTextEditor() {
    if (!document.getElementsByTagName) return;
    var textareas = document.getElementsByTagName('textarea');
    for (var i = 0; i < textareas.length; i++) {
        if (checkClass(textareas[i], 'previewtext')) {
            var editor = new PreviewTextEditor(textareas[i]);
        }
    }
}

addEvent(window, 'load', initPreviewTextEditor);

