var the = {
    beautify_in_progress: false,
    coloring_active: true,
    autocompletion_active: true,
    wraptext_active: false
}
var hlLine = null;
var lastPos = null;
var lastQuery = null;
var marked = [];
var pluginCodeMirror = null;
var pluginOptions = {
    lineNumbers: true,
    mode: "text/html",
    indentUnit: 4,
    matchBrackets: true,
    onCursorActivity: function() {
        pluginCodeMirror.setLineClass(hlLine, null);
        hlLine = pluginCodeMirror.setLineClass(pluginCodeMirror.getCursor().line, "activeline");
    },
    onKeyEvent: function(i, e) {
        if(the.autocompletion_active) {
            /* Hook into charcode < */
            if(String.fromCharCode(e.which == null ? e.keyCode : e.which) == "<") {
                e.stop();

                var cur = pluginCodeMirror.getCursor(false), token = pluginCodeMirror.getTokenAt(cur);
                pluginCodeMirror.replaceRange("<", cur);

                setTimeout(startComplete, 50);
                return true;

            // Hook into ctrl + space
            } if (e.keyCode == 32 && (e.ctrlKey || e.metaKey) && !e.altKey) {
                e.stop();
                return startComplete();
            }
        }
    },
    onHighlightComplete: onChangeCallback
}

// language and init
tinyMCEPopup.requireLangPack();
tinyMCEPopup.onInit.add(onLoadInit);


// onload
function onLoadInit() {
    tinyMCEPopup.resizeToInnerSize();

    // remove Gecko spellchecking
    if (tinymce.isGecko)
        document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck");

    // insert html from tinymce to htmlSource textarea
    document.getElementById("htmlSource").value = tinyMCEPopup.editor.getContent({ source_view : true });

    // format the source code using JS Beautifier plugin
    beautify("htmlSource");

    if(the.coloring_active) {
        // activate syntax coloring
        activateCodeColoring("htmlSource");
    }

    // Add word wrapping. Must be done after beautify() is called.
    if (the.wraptext_active) {
	activateWrapText();
	document.getElementById('wraptext').checked = true;
    } else {
	document.getElementById('wraptext').checked = false;
    }

    // resize window to fit the textarea
    resizeInputs("htmlSource");

    window.onresize = function(event) {
        // resize window to fit the textarea
        resizeInputs("htmlSource");
    }
}


// activating syntax coloring
function activateCodeColoring(id) {
    the.coloring_active = true;

    document.getElementById("search_replace").className = "";
    document.getElementById("reintendt").className = "";
    document.getElementById("autocompletion").disabled = false;

    pluginCodeMirror = CodeMirror.fromTextArea(document.getElementById(id), pluginOptions);
    hlLine = pluginCodeMirror.setLineClass(0, "activeline");
    pluginCodeMirror.focus();
}


// deactivating syntax coloring
function deactivateCodeColoring() {
    the.coloring_active = false;

    document.getElementById("undo").className = "disabled";
    document.getElementById("redo").className = "disabled";
    document.getElementById("search_replace").className = "disabled";
    document.getElementById("reintendt").className = "disabled";
    document.getElementById("autocompletion").disabled = true;

    pluginCodeMirror.toTextArea();
    pluginCodeMirror = null;

    // resize window to fit the textarea
    resizeInputs("htmlSource");
}

function activateWrapText() {
    the.wraptext_active = true;

    var elm = document.getElementById("htmlSource").nextSibling;
    if (elm.className.indexOf("wrapText") == -1) {
        elm.className += ' wrapText';
    }
}

function deactivateWrapText() {
    the.wraptext_active = false;

    var elm = document.getElementById("htmlSource").nextSibling;
    var classes = elm.className.split(" ");
    var wrap = classes.indexOf("wrapText");

    if (wrap) {
        classes.splice(wrap, 1);
        elm.className = classes.join(" ");
    }
}

// toggle highlighting using a checkbox
function toggleHighlighting(elm, id) {
    if (elm.checked) {
        activateCodeColoring(id);
    } else {
        deactivateCodeColoring();
    }
}

// toggle code autocompletion
function toggleAutocompletion(elm) {
    if (elm.checked) {
        the.autocompletion_active = true;
    } else {
        the.autocompletion_active = false;
    }
}

// toggle text wrapping
function toggleWrapText(elm) {
    if (elm.checked) {
        activateWrapText();
    } else {
        deactivateWrapText();
    }
}

// save content back to tinymce editor
function saveContent() {
    tinyMCEPopup.editor.setContent(pluginCodeMirror.getValue(), { source_view : true });
    tinyMCEPopup.close();
}


// resize textarea input
function resizeInputs(id) {
    var vp = tinyMCEPopup.dom.getViewPort(window), el;

    el = document.getElementById(id);

    if (el) {
        // el.style.width = (vp.w - 20) + "px";
        el.style.height = (vp.h - 65) + "px";
    }
}


// toggle search window
function toggleSearch(elm, id) {
    if (!the.coloring_active)
        return false;

    elm.className = "";
    var element = document.getElementById(id);

    if (element.style.display == "none") {
        elm.className = "selected";
        element.style.display = "block";
    }
    else {
        element.style.display = "none";
    }
}






/**
*  CodeMirror 2
*  -------------
*
*  CodeMirror 2 is a rewrite of CodeMirror 1 (http://github.com/marijnh/CodeMirror).
*  The docs live here http://codemirror.net/2/manual.html,
*  and the project page is http://codemirror.net/2/.
*
*  http://codemirror.net/
*
*  Copyright (C) 2011 by Marijn Haverbeke <marijnh@gmail.com>
*/


CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
    var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
    var jsMode = CodeMirror.getMode(config, "javascript");
    var cssMode = CodeMirror.getMode(config, "css");

    function html(stream, state) {
        var style = htmlMode.token(stream, state.htmlState);
        if (style == "xml-tag" && stream.current() == ">" && state.htmlState.context) {
            if (/^script$/i.test(state.htmlState.context.tagName)) {
                state.token = javascript;
                state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
            } else if (/^style$/i.test(state.htmlState.context.tagName)) {
                state.token = css;
                state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
            }
        }
        return style;
    }
    function maybeBackup(stream, pat, style) {
        var cur = stream.current();
        var close = cur.search(pat);
        if (close > -1) stream.backUp(cur.length - close);
        return style;
    }
    function javascript(stream, state) {
        if (stream.match(/^<\/\s*script\s*>/i, false)) {
            state.token = html;
            state.curState = null;
            return html(stream, state);
        }
        return maybeBackup(stream, /<\/\s*script\s*>/, jsMode.token(stream, state.localState));
    }
    function css(stream, state) {
        if (stream.match(/^<\/\s*style\s*>/i, false)) {
            state.token = html;
            state.localState = null;
            return html(stream, state);
        }
        return maybeBackup(stream, /<\/\s*style\s*>/, cssMode.token(stream, state.localState));
    }

    return {
        startState: function() {
            var state = htmlMode.startState();
            return {token: html, localState: null, htmlState: state};
        },

        copyState: function(state) {
            if (state.localState)
                var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
            return {token: state.token, localState: local, htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
        },

        token: function(stream, state) {
            return state.token(stream, state);
        },

        indent: function(state, textAfter) {
            if (state.token == html || /^\s*<\//.test(textAfter))
                return htmlMode.indent(state.htmlState, textAfter);
            else if (state.token == javascript)
                return jsMode.indent(state.localState, textAfter);
            else
                return cssMode.indent(state.localState, textAfter);
        },

        electricChars: "/{}:"
    }
});

CodeMirror.defineMIME("text/html", "htmlmixed");

////////////////////////
// undo last action
function undo() {
    pluginCodeMirror.undo();
}


// redo last action
function redo() {
    pluginCodeMirror.redo();
}


// callback to onchange event on editor
function onChangeCallback(editor) {
    var undo = editor.historySize().undo;
    var redo = editor.historySize().redo;

    if(undo > 0) document.getElementById("undo").className = "";
    else document.getElementById("undo").className = "disabled";

    if(redo > 0) document.getElementById("redo").className = "";
    else document.getElementById("redo").className = "disabled";
}

////////////////////////
// reintendt html source code
function reIntendt(id) {
    if(!the.coloring_active) return false;

    deactivateCodeColoring();
    document.getElementById("searchWindow").style.display = "none";
    beautify(id);
    activateCodeColoring(id);
}

////////////////////////
// search and replace

// unmarks all searched words
function unmark() {
    for (var i = 0; i < marked.length; ++i) marked[i]();
    marked.length = 0;
}

// search for query from #query input
function searchCode() {
    unmark();

    var text = document.getElementById("query").value;
    if (!text)  return false;

    if(!pluginCodeMirror.getSearchCursor(text).findNext()) {
        alert(tinyMCEPopup.getLang('codemagic_dlg.nothing_found'));
        return false;
    }

    for (var cursor = pluginCodeMirror.getSearchCursor(text); cursor.findNext();)
        marked.push(pluginCodeMirror.markText(cursor.from(), cursor.to(), "searched"));

    if (lastQuery != text) lastPos = null;

    var cursor = pluginCodeMirror.getSearchCursor(text, lastPos || pluginCodeMirror.getCursor());
    if (!cursor.findNext()) {
        cursor = pluginCodeMirror.getSearchCursor(text);
        if (!cursor.findNext()) return;
    }
    pluginCodeMirror.setSelection(cursor.from(), cursor.to());
    lastQuery = text; lastPos = cursor.to();
}

// replace
function replaceCode() {
    unmark();

    var text = document.getElementById("query").value;
    var replace = document.getElementById("replace").value;

    if (!text) return false;

    if(!pluginCodeMirror.getSearchCursor(text).findNext()) {
        alert(tinyMCEPopup.getLang('codemagic_dlg.nothing_to_replace'));
        return false;
    }

    for (var cursor = pluginCodeMirror.getSearchCursor(text); cursor.findNext();)
        pluginCodeMirror.replaceRange(replace, cursor.from(), cursor.to());
}


////////////////////////
// autocompletion
var tagNames = ("a abbr acronym address applet area b base basefont bdo big blockquote body br button" +
                " caption center cite code col colgroup dd del dfn dir div dl dt em fieldset font form frame " +
                " frameset h1 h2 h3 h4 h5 h6 head hr html i iframe img input ins isindex kbd label legend li link map" +
                " menu meta noframes noscript object ol optgroup option p param pre q s samp script select small" +
                " span strike strong style sub sup table tbody td textarea tfoot th thead title tr tt u ul var").split(" ");

var pairedTags = ("a abbr acronym address applet b bdo big blockquote body button" +
                  " caption center cite code colgroup del dfn dir div dl em fieldset font form" +
                  " frameset h1 h2 h3 h4 h5 h6 head html i iframe ins kbd label legend li map" +
                  " menu noframes noscript object ol optgroup option p pre q s samp script select small" +
                  " span strike strong style sub sup table tbody td textarea tfoot th thead title tr tt u ul var").split(" ");

var unPairedTags = ("area base basefont br col dd dt frame hr img input isindex link meta param").split(" ");

var specialTags = {
    "applet" : { tag: 'applet width="" height=""></applet>', cusror: 8 },
    "area" : { tag: 'area alt="" />', cusror: 6 },
    "base" : { tag: 'base href="" />', cusror: 7 },
    "form" : { tag: 'form action=""></form>', cusror: 9 },
    "img" : { tag: 'img src="" alt="" />', cusror: 6 },
    "map" : { tag: 'map name=""></map>', cusror: 7 },
    "meta" : { tag: 'meta content="" />', cusror: 10 },
    "optgroup" : { tag: 'optgroup label=""></optgroup>', cusror: 8 },
    "param" : { tag: 'param name="" />', cusror: 7 },
    "script" : { tag: 'script type=""></script>', cusror: 7 },
    "style" : { tag: 'style type=""></style>', cusror: 7 },
    "textarea" : { tag: 'textarea cols="" rows=""></textarea>', cusror: 7 }
}

// Minimal event-handling wrapper.
function stopEvent() {
    if (this.preventDefault) {this.preventDefault(); this.stopPropagation();}
    else {this.returnValue = false; this.cancelBubble = true;}
}
function addStop(event) {
    if (!event.stop) event.stop = stopEvent;
    return event;
}
function connect(node, type, handler) {
    function wrapHandler(event) {
        handler(addStop(event || window.event));
    }

    if (typeof node.addEventListener == "function")
      node.addEventListener(type, wrapHandler, false);
    else
      node.attachEvent("on" + type, wrapHandler);
}

function forEach(arr, f) {
    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}

Array.prototype.inArray = function(value){
    for (var key in this)
        if (this[key] === value) return true;
    return false;
}



// autocompletion start
function startComplete() {
    var startingTag, unPaired;

    // We want a single cursor position.
    if (pluginCodeMirror.somethingSelected()) return;

    // Find the token at the cursor
    var cur = pluginCodeMirror.getCursor(false), token = pluginCodeMirror.getTokenAt(cur), tprop = token;

    if(token.string.indexOf("<") == 0 && token.string.indexOf("</") != 0) {
        token.string = token.string.replace("<", "");
        token.start++;
        startingTag = true;
    } else if(token.string.indexOf("</") == 0) {
        token.string = token.string.replace("</", "");
        token.start += 2;
        startingTag = false;
    } else {
        return;
    }

    // get the tags
    var completions = getCompletions(token, startingTag);
    if (!completions.length) return;


    function insert(str) {
        if(str == "") return;

        // trim
        str = str.replace(/^\s+|\s+$/g,"");

        // is this an unpaired tag?
        unPaired = unPairedTags.inArray(str) ? true : false;

        if(specialTags[str] != null && startingTag) {
            var insertTag = specialTags[str].tag;
            var jumpTo = (token.start + str.length + specialTags[str].cusror);
        } else if(startingTag && unPaired) {
            var insertTag = str + " />";
            var jumpTo = (token.start + str.length + 3);
        } else if (startingTag) {
            var insertTag = str + "></" + str + ">";
            var jumpTo = (token.start + str.length + 1);
        } else {
            var insertTag = str + ">";
            var jumpTo = (token.start + str.length + 1);
        }

        pluginCodeMirror.replaceRange(insertTag, {line: cur.line, ch: token.start}, {line: cur.line, ch: token.end});
        pluginCodeMirror.setCursor({line: cur.line, ch: jumpTo});
    }

    // When there is only one completion, use it directly.
    /*if (completions.length == 1) {
        insert(completions[0]); return true;
    }*/

    // Build the select widget
    var complete = document.createElement("div");
    complete.className = "completions";

    var sel = complete.appendChild(document.createElement("select"));
    sel.multiple = false;

    if (completions.length == 1) sel.multiple = true;

    for (var i = 0; i < completions.length; ++i) {
        var opt = sel.appendChild(document.createElement("option"));
        opt.appendChild(document.createTextNode(completions[i]));
    }

    sel.firstChild.selected = true;
    sel.size = Math.min(10, completions.length);

    var pos = pluginCodeMirror.cursorCoords();
    complete.style.left = pos.x + "px";

    // top position
    if(pos.yBot > 448) pos.yBot = pos.yBot - 165;
    complete.style.top = pos.yBot + "px";

    document.body.appendChild(complete);


    var done = false;
    function close() {
        if (done) return;
        done = true;
        complete.parentNode.removeChild(complete);
    }
    function pick() {
        insert(sel.options[sel.selectedIndex].innerHTML);
        close();
        setTimeout(function(){
            pluginCodeMirror.focus();
        }, 50);
    }

    // bind events
    connect(sel, "blur", close);
    connect(sel, "keydown", function(event) {
        var code = event.keyCode;

        // Enter, space, tab
        if (code == 13 || code == 32 || code == 9) {event.stop(); pick();}
        // Escape (FIX: closes the tinymce popup window)
        else if (code == 27) {
            event.stop(); close(); pluginCodeMirror.focus();
        }
        // other than arrow up/down
        else if (code != 38 && code != 40 && code != 16 && code != 17 && code != 18 && code != 91 && code != 92) {
            close();
            pluginCodeMirror.focus();
            if(code != 39 && code != 37) setTimeout(startComplete, 50);
            else event.stop();
        }
    });
    connect(sel, "dblclick", pick);

    sel.focus();

    // Opera sometimes ignores focusing a freshly created node
    if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
    return true;
}

function getCompletions(token, startingTag) {
    var found = [], start = token.string;

    function maybeAdd(str) {
        if (str.indexOf(start) == 0) found.push(str);
    }

    if(startingTag) {
        forEach(tagNames, maybeAdd)
    } else {
        forEach(pairedTags, maybeAdd)
    }

    return found;
}





/**
*  JS Beautifier
*  ---------------
*  ...or, more specifically, all of the code powering jsbeautifier.org.
*
*
*  Written by Einar Lielmanis, <einar@jsbeautifier.org>
*
*  Thanks to Jason Diamond, Patrick Hof, Nochum Sossonko, Andreas Schneider, Dave
*  Vasilevsky, Vital Batmanov, Ron Baldwin, Gabriel Harrison, Chris J. Hull and
*  others.
*/
function beautify(id) {
    if (the.beautify_in_progress)
        return;

    the.beautify_in_progress = true;
    var source = document.getElementById(id).value.replace(/^\s+/, '');
    var indent_size = 4;
    var indent_char = ' ';
    var brace_style = 'collapse';  // collapse, expand, end-expand

    document.getElementById(id).value = style_html(source, indent_size, indent_char, 120, brace_style);
    the.beautify_in_progress = false;
}
