debug = function(obj){
    try{
        console.log(obj);
    }
    catch(e){

    }
};

editAreaCssFix = function(){
    if(jQuery('.content').css('position') == 'relative')
        jQuery('.content, #main_content, #editModelpage').css('position','static');
    else
        jQuery('.content, #main_content, #editModelpage').css('position','relative');
};

setPasswordError = function() {
    if(jQuery("#signupPassword").val() == jQuery("#confirmSignupPassword").val())
    {
        jQuery("#frmSignup input:submit").removeAttr("disabled");
        jQuery("#passwordError").addClass("hidePasswordError");
        jQuery("#passwordError").removeClass("showPasswordError");
    }else {
        jQuery("#frmSignup input:submit").attr("disabled", "true");
        jQuery("#passwordError").addClass("showPasswordError");
        jQuery("#passwordError").removeClass("hidePasswordError");
    }
};

setChangePasswordError = function() {
    if(jQuery("#newPassword").val() == jQuery("#confirmPassword").val())
    {
        jQuery("#frmChangePassword input:submit").removeAttr("disabled");
        jQuery("#passwordError").addClass("hidePasswordError");
        jQuery("#passwordError").removeClass("showPasswordError");
    }else {
        jQuery("#frmChangePassword input:submit").attr("disabled", "true");
        jQuery("#passwordError").addClass("showPasswordError");
        jQuery("#passwordError").removeClass("hidePasswordError");
    }
};

function clickUserSelect(checkbox) {
    if(checkbox.checked)
    {
        var found = false;
        jQuery('#usersSelect option').each(function(i, selected){
            var id = jQuery(selected).val();
            if(id == checkbox.value)
            {
                jQuery(selected).attr("selected", true);
                found = true;
            }
        });

        if(!found){
            var options = jQuery("#usersSelect").html();
            options += '<option value="' + checkbox.value + '" selected="true">' + checkbox.value + '</option>';
            jQuery("#usersSelect").html(options);
        }

    }else {
        jQuery('#usersSelect option').each(function(i, selected){
            var id = jQuery(selected).val();
            if(id == checkbox.value)
            {
                jQuery(selected).attr("selected", false);
            }
        });

    }
}

var forioInterface = {
        firstLoad: false,
        skipAnim: false,

        highlightSimDesciptionBlock: function(){
                jQuery('.simDisplayLayout').hover(
                    function(){ jQuery(this).addClass('highlightedSim'); },
                    function(){ jQuery(this).removeClass('highlightedSim'); }
                );
        },
        resizeMainContent: function(callback)
        {
            callbcack = callback || null;
            forioInterface.resizeHandler(callback);
            jQuery(window).resize(function(){ forioInterface.resizeHandler(); });
        },
        resizeHandler: function(callback){
            var windowHeight = jQuery(window).height();
            var navHeight = jQuery('#navigation').outerHeight();
            var newHeight = windowHeight - navHeight - 34 - 10;
            jQuery('.content').css({'height':newHeight,'padding-bottom':0});
            jQuery('.run_sim .content').css({'height':parseInt(newHeight + 21),'padding-bottom':0});
            if(callback) callback();
        },

        callbackHandler: function(data) {
            dwr.util.setValue("main_content", data, { escapeHtml:false });

            var count = 20;

            var currentPage = parseInt(jQuery('#startIndex').attr('value'));
            var min = ((currentPage + 1)*count - (count - 1));
            var max = ((currentPage + 1)*count);
            var totalRecords = parseInt(jQuery('#totalRecords').attr('value'));
            (max > totalRecords)? max = totalRecords : max;
            var rest = (totalRecords  % count);


            (rest != 0) ? totalPages = ((totalRecords - rest) / count) + 1 : totalPages = ((totalRecords - rest) / count);


            if(jQuery('.pag_controls').length != 0){
                jQuery('.pag_controls').html('');

                if(min != 1) jQuery('.pag_controls').append('<a href="#" alt="' + currentPage +'">&laquo; Previous</a>');
                var maxCount = 10;
                if(totalPages > 1)
                {
                    var maxTotalPages = totalPages > maxCount ? maxCount : totalPages;

                    var startSearchPageIndex = 1;

                    if(currentPage     > maxCount/2 )
                    {
                        startSearchPageIndex = currentPage - maxCount/2;
                        maxTotalPages = maxTotalPages + startSearchPageIndex;
                    }

                    for(i=startSearchPageIndex;i<=maxTotalPages;i++)
                    {
                        jQuery('.pag_controls').append('<a href="#" alt="'+i+'">'+i+'</a>');
                    }
                }
                if(max != totalRecords) jQuery('.pag_controls').append('<a href="#" alt="'+parseInt(currentPage+2)+'">Next &raquo;</a>');
                pageControlHandler();
            }

            if(totalRecords > 0) forioInterface.setPaginationUI(totalRecords,min,max,currentPage+1);
            else jQuery('.pag_index').html('');

            forioInterface.highlightSimDesciptionBlock();

        },

        setPaginationUI: function(total,min,max,current){
            jQuery('.pagination').addClass('visible');
            if(total) jQuery('.totalResults_span').text(total);
            if(min) jQuery('.showingResultsMin_span').text(min);
            if(max) jQuery('.showingResultsMax_span').text(max);
            if(current) jQuery('.pag_controls a[alt='+current+']').addClass('current_page');
            else jQuery('.pag_controls a[alt=1]').addClass('current_page');
        },

        limitTagNumber: function(){
            var listItems = jQuery('.paginate').find('li');
            var total = listItems.size();
            forioInterface.showMoreTags.total = listItems.size();
            if(total > 15)
            {
                forioInterface.showMoreTags.currentPage = 1;
                jQuery('#tags.sidebar_item').find('li:lt(15)').show();
                jQuery('#tags.sidebar_item').after('<span id="tagControls"><a href="#" id="previousTags" onclick="forioInterface.showMoreTags.prevClickHandler(); return false;">&laquo; previous</a> &nbsp; <a href="#" id="nextTags" onclick="forioInterface.showMoreTags.nextClickHandler(); return false;">next &raquo;</a></span>');
            }
            else {
                jQuery('.paginate li').show();
            }
        },

        showMoreTags: {
            currentPage: 0,
            total: 0,
            nextClickHandler: function(){
                var nextPage = forioInterface.showMoreTags.currentPage + 1;
                var max = nextPage * 15;
                var min = parseInt(((nextPage - 1) * 15) - 1);
                var nextResults = jQuery('#tags.sidebar_item').find('li:lt('+max+'):gt('+ min +')');

                if(nextResults.size() > 0)
                {
                    forioInterface.showMoreTags.currentPage += 1;
                    jQuery('#tags.sidebar_item').find('li').hide();
                    nextResults.show();
                }
                if(max >= this.total) jQuery('#nextTags').hide();
                else jQuery('#nextTags').show();

                if(min != -1) jQuery('#previousTags').show();
                else jQuery('#previousTags').hide();
            },
            prevClickHandler: function(){
                var nextPage = forioInterface.showMoreTags.currentPage - 1;
                var max = nextPage * 15;
                var min = parseInt(((nextPage - 1) * 15) - 1);
                var nextResults = jQuery('#tags.sidebar_item').find('li:lt('+max+'):gt('+ min +')');

                if(nextResults.size() > 0)
                {
                    forioInterface.showMoreTags.currentPage -= 1;
                    jQuery('#tags.sidebar_item').find('li').hide();
                    nextResults.show();
                }
                if(max >= this.total) jQuery('#nextTags').hide();
                else jQuery('#nextTags').show();
                if(min != -1) jQuery('#previousTags').show();
                else jQuery('#previousTags').hide();
            }
        }
};

var newWindows = new Array();
function popups(href,targetWindow){
    if(!targetWindow) targetWindow = 'newWindow';
    targetWindow = escape(targetWindow);

    for (var i =0; i< newWindows.length; i++)
    {
        var newWin = newWindows[i];
        if (newWin != null && !newWin.closed && newWin.name == targetWindow) {
            newWin.close();
        }
    }

    var newWindow = window.open(href, targetWindow);
    if(newWindow.outerWidth < 1024 || newWindow.outerHeight < 700)
    {
        newWindow.focus();
        newWindow.resizeTo(1024,768);
    }
    newWindows.push(newWindow);

    if(!newWindow){

        alertDialog = new Dialog({title:'Popup blocker detected', cssClass:'warning',id:'popupBlocker'});

        if(jQuery.browser.msie){
            alertDialog.update({message:'<p>We have detected that your browser&#39;s pop-up blocker is preventing you from running the simulation. Please add this site to your list of allowed exceptions and try again.</p><br/><p><strong>Click on the options as shown below:</strong></p><br/><img src="'+Globals.imageBasePath+'/popup_instructions_ie.gif"/>',width:500, height:300});
        }
        else if(jQuery.browser.mozilla){
            alertDialog.update({message:'<p>We have detected that your browser&#39;s pop-up blocker is preventing you from running the simulation. Please add this site to your list of allowed exceptions and try again.</p><br/><p><strong>Click on the options as shown below:</strong></p><br/><img src="'+Globals.imageBasePath+'/popup_instructions_mozilla.gif"/>',width:532, height:300});
        }
        else if(jQuery.browser.safari){
            alertDialog.update({message:'<p>We have detected that your browser&#39;s pop-up blocker is preventing you from running the simulation. Please disable the Safari popup blocker and try again.</p><br/><p><strong>Go to the "Safari" menu and untick the option to block pop-ups, as shown below:</strong></p><br/><img src="'+Globals.imageBasePath+'/popup_instructions_safari.gif"/>',width:290, height:390});
        }
        else{
            alertDialog.update({message:'<p>We have detected that your browser&#39;s pop-up blocker is preventing you from running the simulation. Please disable your popup blocker and try again.</p>',width:290, height:600});
        }
        alertDialog.show();
    }
    else newWindow.focus();
}

function logoutIfSessionInvalid() {
//    KeepSessionAlive.isSessionAlive("", function(data) {
//            if(!data) {
//                document.location.href = jQuery("#timeoutUrl").val();
//            }
//        });
}

var timer = {
    set: function(){
//        if(jQuery("#timeoutUrl").length > 0){
//            jQuery(document).everyTime("100s", "timeout", function() {
//                logoutIfSessionInvalid();
//            });
//        }
    },

    update: function(){
        jQuery(document).stopTime("timeout");
        timer.set();
    },

    poll: function(){
        jQuery(document).stopTime("timeout");
        jQuery(document).everyTime("300s", "keepSessionAlive", function() {
            var authApiCall;
            if (Globals.simulationPath)
            {
                authApiCall = Globals.apiBasePath+'api/authentication' + Globals.simulationPath + "/";
            }
            else
            {
                authApiCall = Globals.apiBasePath+'api/authentication';
            }
            utility.load(authApiCall);
        }, 0);
    }

};

// called by Flex
var preventTimeout = function(){
    timer.update();
};

var userControls = {
    removeSimFromFavorites: function(id){
        utility.load(Globals.linkBasePath + id + "/favorites/delete/" ,null,function()
        {
            window.location.reload(true);
        });
    }
};

var simControls = {
    addTags: function(){
        jQuery('#add_tags_form').toggle();
    },
    deleteSim: function(simPath, destination){
        if (destination)
        {
            utility.post(Globals.linkBasePath + simPath +"/delete/","",null,function(){ window.location = destination;});
        }
        else
        {
            utility.post(Globals.linkBasePath + simPath +"/delete/","",null,function(){ window.location.reload(true);});
        }
    },
    setSimulationUrl: function() {

        jQuery(".setSimulationUrl").keyup(function(event) {
            var callbackFunction = function(data) {
                jQuery("#path").val(data);
                jQuery("#simulationUrl").text(data);
            };

            var errorHandlerFunction = function(message) {
                //alert("Select Proper Author : " + message);
            };

            SimulationPathService.getNewPath(jQuery('#name').val(), jQuery('#authorId').val(),
                    {callback: callbackFunction, errorHandler :errorHandlerFunction, async: true}
            );

        });

    },
    setSimulationUrlAlt: function() {

        jQuery(".setSimulationUrlAlt").keyup(function(event) {
            var callbackFunction = function(data) {
                jQuery("#path").val(data);
                jQuery("#simulationUrlAlt").text(data);
            };

            var errorHandlerFunction = function(message) {
                //alert("Select Proper Author : " + message);
            };

            SimulationPathService.getNewPath(jQuery('#nameAlt').val(), jQuery('#authorId').val(),
                    {callback: callbackFunction, errorHandler :errorHandlerFunction, async: true}
            );

        });

    },
    getEmbedTags: function(width, height, swfFilePath, url){
        return "<object width='"+width+"' height='"+height+"'><param name='movie' value='"+url+swfFilePath+"'></param><param name='allowFullScreen' value='true'></param><param name='allowscriptaccess' value='always'></param><embed src='"+url+swfFilePath+"' type='application/x-shockwave-flash' allowscriptaccess='always' allowfullscreen='true' width='"+width+"' height='"+height+"'></embed></object>";
    }
};

var dropdown = {
    init: function(selectors){
        var selectorString = selectors.join();
        jQuery(selectorString).click(function(event){
            if(jQuery(event.originalTarget).attr('id') == 'mini_avatar') event.preventDefault();
            (jQuery(this).hasClass('clicked')) ? jQuery(this).data('stick',true) : jQuery(this).data('stick',false);
        });
        jQuery(selectorString).hover(
            function(){ dropdown.show(jQuery(this)); },
            function(){ dropdown.hide(); }
        );

        jQuery('.content').click(function(){
            jQuery(selectorString).data('stick',false);
            dropdown.hide();
        });

        jQuery('.menu_item, #navLvl1 li > a, #navInsideSim li > a').click(function(event){
            if(Globals.modelEdit.changed)
            {
                var confirmation = confirm("You have made changes to your model, if you leave this page without saving, you will lose all changes");
                if(confirmation)
                {
                    if(jQuery(this).find('.popup').length == 0 && jQuery(this).find('.active_link').length == 1) window.location = jQuery(this).find('.active_link a').attr('href');
                }
                else
                {
                    return false;
                }
            }
            else
            {
                if(jQuery(this).find('.popup').length == 0 && jQuery(this).find('.active_link').length == 1) window.location = jQuery(this).find('.active_link a').attr('href');
            }
        });
    },
    show: function(el){
        dropdown.hide();
        el.addClass('clicked');
        el.children('.dropped_down').show();
    },
    hide: function(){
        jQuery('.clicked').each(function(){
            if(jQuery(this).data('stick') != true)
            {
                jQuery(this).children('.dropped_down').hide();
                jQuery(this).removeClass('clicked');
            }
        });
    }
};

var sliderLogin = {
    speed: 500,
    init: function(){
        // Esc key listener
        jQuery(document).keypress(function(event){
            if(event.keyCode == 27)
            {
                sliderLogin.hide();
                dropdown.hide();
            }
        });

        jQuery('#slide_control a#slideThis, #slidey_forms input:submit').click(function(event){
            if(this.nodeName == 'A') { event.preventDefault(); }
            sliderLogin.toggle();
        });

    },

    show: function(){
        jQuery('#slide_login').animate({ top: '0' }, this.speed );
        jQuery('#slideThis').text('Close panel');
        var iconURL = jQuery('#panelIcon').attr('src').substr(0,jQuery('#panelIcon').attr('src').lastIndexOf('/')+1);
        var newIconURL = iconURL+'close_panel.gif';
        jQuery('#panelIcon').attr('src',newIconURL);
        var login = jQuery('#StxtUserName').val();
        if(login != '') jQuery('#txtPassword').focus();
        else jQuery('#StxtFirstNames').focus();
    },

    hide: function(){
        jQuery('#slide_login').animate({ top: '-360px' }, this.speed ) ;
        jQuery('#slideThis').text('Login or Register');
        var iconURL = jQuery('#panelIcon').attr('src').substr(0,jQuery('#panelIcon').attr('src').lastIndexOf('/')+1);
        var newIconURL = iconURL+'contact-new.gif';
        jQuery('#panelIcon').attr('src',newIconURL);
    },

    toggle: function(){
        if(jQuery('#slide_login').css('top') == "-360px")
        {
            this.show();
        }
        else
        {
            this.hide();
        }
    }
};

var editable = {
    selector:'',
    id:'',
    set: function(selector,id){
        this.selector = selector;
        this.id = parseInt(id);
        id = parseInt(id);
        jQuery(selector).addClass('editable');
        jQuery(selector).hover(
            function(){ jQuery(this).addClass('hover'); },
            function(){ jQuery(this).removeClass('hover'); }
        );
        jQuery(selector).click(function(){
                jQuery(this).removeClass('editable');
                jQuery(this).removeClass('hover');
                jQuery(this).data('bound',false);
                var width = jQuery(this).width() - 5;
                var height = jQuery(this).height();
                (height < 100) ? height = 100 : height = height;
                var textarea ='';
                textarea += '<form id="enter_description_form" action="'+Globals.linkBasePath+'simulations/editDescription.html" method="post">';
                textarea += '<input type="hidden" name="id" value="'+id+'" />';
                textarea += '<textarea name="description" id="enter_description" style="margin: 0; padding: 0; height: '+height+'px; width: '+width+'px;">';
                textarea += jQuery("#description_text input").val();
                textarea += '</textarea>';
                textarea += '<div style="text-align:right;"><input type="submit" value="Submit Changes" /> &nbsp; <input type="button" value="Cancel" onclick="editable.cancel(); return false;" /></div>';
                textarea += '</form>';
                jQuery(this).after(textarea);
                jQuery(this).hide();
                jQuery('#enter_description').focus();
        });
        jQuery(selector).data('bound',true);
    },
    cancel: function(){
        jQuery(this.selector).addClass('editable');
        jQuery(this.selector).show();
        jQuery('#enter_description_form').hide();
    }
};

function setPopupBehavior(){
    jQuery('.popup').click(function(event){
        popups(jQuery(this).attr('href'), jQuery(this).attr('rel'));
        event.preventDefault();
    });
}

function changeParentUrl(url)
{
    document.location=url;
}

jQuery(document).ready(function(){

    // prevent array param from posting with extra []
    // see: http://forum.jquery.com/topic/jquery-post-1-4-1-is-appending-to-vars-when-posting-from-array-within-array
    jQuery.ajaxSettings.traditional = true;

    // Init Application
    sliderLogin.init();
    timer.set();
    dropdown.init(['#navLvl1 ol li', '#navInsideSim ol li']);
    forioInterface.limitTagNumber();
    forioInterface.highlightSimDesciptionBlock();

/*
    jQuery("#forioEquations").each(function() {
        timer.poll();
    });*/
    jQuery("#designerBox").each(function() {
        timer.poll();
    });

    jQuery(".closeWindow").click(function(){
        window.close();
    });

    jQuery(".webflowDisable").click(function(e){
        /** Edit by Adam on 7/28/11
         * In ie 9 the first click event was firing, causing the second event to be aborted
         * Throw in a prevent default to avoid this behavior
         **/
        e.preventDefault();
        jQuery("#webFlowEvent").attr("name", jQuery(this).attr("name"));
        jQuery("#webFlowEvent").attr("value", jQuery(this).attr("value"));
        jQuery(this).attr("disabled", "true");
        jQuery("#webFlowEvent").click();
    });

    jQuery("#deleteModel").click(function() {
        var result = confirm("Are you sure?");
        if (result) jQuery('#deleteModelForm').submit();
            else return false;
    });

    jQuery("#partialReviewLink").click(function(event){
        jQuery("#_eventId_partialReview").click();
    });

    jQuery("#confirmSignupPassword").keyup(function(event){
        setPasswordError();
    });

    jQuery("#signupPassword").keyup(function(event){
        setPasswordError();
    });

    jQuery("#confirmPassword").keyup(function(event){
        setChangePasswordError();
    });
    jQuery("#newPassword").keyup(function(event){
        setChangePasswordError();
    });

    jQuery(".autoUploadFile").change(function() {
        this.form._eventId_submit.click();
    });

});

jQuery(window).resize(function(){
    jQuery('#designerObject').css('width',jQuery('#main_content').outerWidth());
    jQuery('#designerObject').css('height',jQuery('#main_content').height());
});


//authentication
var authenticationController = (function($) {
    function formToObject(selector) {
        var fields = {};

        $(selector).find(':input').each(function() {
            if(this.type != 'submit' && this.tagName != 'button') {
                fields[this.name] = $(this).val();
            }
        });
        return fields;
    }

    function signup(first, last, email, org, pass) {
        $.ajax({
            url: Globals.linkBasePath + 'api/user',
            type: 'POST',
            data: {
                firstName: first,
                lastName: last,
                email: email,
                organizationName: org,
                password: pass,
                sendEmail: true
            },
            error: function(data) {
                $('#form-sign-up').find('input[type="submit"]').val('Register').removeAttr('disabled');
                $('.authentication-message').html($.parseJSON(data['responseText']).message);
            },
            success: function () {
                signin(email, pass);
            }
        });
    }

    function signin(username, password) {
        $.ajax({
            url: Globals.linkBasePath + 'api/authentication',
            type: 'POST',
            data: {
                user_action: 'login',
                email: username,
                password: password
            },
            error: function(data) {
                $('#form-sign-in').find('input[type="submit"]').val('Sign in').removeAttr('disabled');
                $('.authentication-message').html($.parseJSON(data['responseText']).message);
            },
            success: function () {
                window.location = Globals.linkBasePath +'home/';
            }
        });
    }

    return {
        init: function() {

            if($('#authentication-menu').length > 0) {
                $('#toggle-authentication-menu').click(function() {
                    var $that = $(this),
                        $login = $('#login-name'),
                        $password = $('#password');

                    $('#authentication-menu').toggle();

                    if($login.val() == null || $login.val() === '')   {
                        $login.focus();
                    } else {
                        $password.focus();
                    }

                    $that.parent('.dropdown').toggleClass('open');

                });
                $('#register').click(function() {
                    $('#form-sign-up').show();
                    $('#form-sign-in').hide();
                });
                $('#sign-in').click(function() {
                    $('#form-sign-in').show();
                    $('#form-sign-up').hide();
                });

                $('#form-sign-up').validate({
                    submitHandler: function(form) {
                        var fields = formToObject(form);
                        $(form).find('input[type="submit"]').attr({'disabled':'disabled', value: 'Registering...'});
                        signup(fields['first-name'], fields['last-name'], fields['email'], fields['org-name'], fields['new-password']);
                    }
                });

                $('#confirm-password').rules('add', { equalTo: '#new-password' });

                // fixes enter submitting forms in ie.
                $('#authentication-menu').find('input').keydown(function(e) {
                    if(e.keyCode === 13) {
                        $(this).parents('form').submit();
                    }
                })

                $('#form-sign-in').validate({
                    submitHandler: function(form) {
                        var fields = formToObject(form);
                        $(form).find('input[type="submit"]').attr({'disabled':'disabled', value: 'Signing in...'});
                        signin(fields['login-name'], fields['password']);
                    }
                });
            }

            $('#navigation').delegate('.close', 'click', function() {
                $(this).parent().remove();
            });
        }
    }
})(window.jQuery);

var FORIO = window['FORIO'] || {};

FORIO.utils = (function($, undefined) {
    var isMac = /Mac/.test(navigator.platform);
    return {
        isMac: isMac
    }
})(window.jQuery);

FORIO.controllers = FORIO['controllers'] || {};

FORIO.controllers.editor = (function($, undefined) {
    var editor,
        searcher,
        searchTerm,
        isDirty = true,
        isLookingAhead = true, // next/prev flag
        matches = [],
        isCaseInsensitive,
        isRegex,
        isWrapping = true,
        isCompletelyLoaded,
        currentLine,
        commonKeyMap = {
            'Tab' : 'indentOrInsertTab',
            'Shift-Tab' : 'dedentOrRemoveTab'
        },
        macKeyMap = {
            'Cmd-H': 'autoComplete'
        },
        pcKeyMap = {
            'Ctrl-H': 'autoComplete'
        },
        $editor,
        $editpage,
        $save,
        $indentMode,
        $indentSize,
        $lineNumbers,
        $findAndReplaceDialog,
        $ignoreCase,
        $regex,
        $toFind,
        $wrap,
        $toReplace,
        $replace,
        $replaceAll,
        $findAndReplace,
        $replaceAllAndFindAndReplace,
        $prev,

        _createRegex = function(str) {
            if(!isRegex) {
                str = str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
            }
            return isCaseInsensitive ? new RegExp(str, 'i') : new RegExp(find);
        },

        _findAll = function(find) {
            if(isDirty) { // dirty
                var cursPos = editor.getCursor(); // get the current cursor position
                matches.length = 0; // reset the array
                searcher = editor.getSearchCursor(_createRegex(find)); // search

                while(searcher.findNext()) { // push each search result into an array
                    matches.push({from: searcher.from(), to: searcher.to(), visited: false});
                }

                for(var i = 0, j = 0, l = matches.length; j < l; j++) { // reorder
                    var match = matches[i].from;

                    if(match.line <= cursPos.line && match.ch < cursPos.ch) {
                        matches.push(matches.splice(i,1)[0]); // if the match was found before the cursor position, move it to the end of the list
                    } else {
                        i++; // move the index up only when the match was in the right position
                    }
                }
                isDirty = false; // unset dirty
                isLookingAhead = true; // always looking ahead initially
            }
        },

        _find = function(find, next) {
            if(find == null || find === '') {
                alert('Please enter a search term.');
                return;
            }
            _findAll(find);
            if(next && !isLookingAhead) {
                _changeDirection();
                isLookingAhead = true;
            } else if (isLookingAhead && !next) {
                _changeDirection();
                isLookingAhead = false;
            }
            _select();
        },

        _changeDirection = function() {
            if(matches.length > 0) {
                matches.reverse();
                matches.push(matches.shift());
            }
        },

        _select = function () {
            if(matches.length > 0 && (isWrapping || ($.grep(matches, function(m) { return !m.visited; })).length > 0)) {
                editor.setSelection(matches[0].from, matches[0].to);
                matches[0].visited = true;
                matches.push(matches.shift());
            } else if(matches.length > 0) {
                alert('No new matches found.');
            } else {
                alert('No matches found.');
            }
        },

        _replace = function(replaceWith) {
            editor.replaceSelection(replaceWith)
        },

        _replaceAll = function(find, replaceWith) {
            searcher = editor.getSearchCursor(_createRegex(find));

            while(searcher.findNext())
                searcher.replace(replaceWith);

            isDirty = true;
        },

        _findAndReplace = function(find, replaceWith) {
            if(editor.getSelection() === find) {
                _replace(replaceWith);
            } else {
                _find(find, true /*look ahead*/);
                _replace(replaceWith);
            }
        },

        _save = function() {
            editor.save();
            $('#form-import-model').submit();
        },

        _toggleCaseSensitive = function() {
            isCaseInsensitive = !isCaseInsensitive;
            isDirty = true;
        },

        _toggleRegex = function() {
            isRegex = !isRegex;
            isDirty = true;
        },

        _toggleWrap = function() {
            isWrapping = !isWrapping;
        }

        _coordsEqual = function(a, b) {
            return a.x === b.x
                && a.y === b.y
                && a.yBot === b.yBot;
        },

        _highlightActiveLine = function() {
            if(!currentLine) {
                currentLine = editor.setLineClass(0, "activeline");
            } else {
                editor.setLineClass(currentLine, null);
                currentLine = editor.setLineClass(editor.getCursor().line, 'activeline');
            }
        },

        _highlightSelectedSymbol = function() {
            $('.cm-selected-symbol').remove();

            if(editor.somethingSelected()) {
                var selection = editor.getSelection(),
                    cursPos = editor.getCursor(true),
                    token = editor.getTokenAt({ line: cursPos.line, ch: parseInt(cursPos.ch,10) + 1 }),
                    allowedTypes = ['function', 'keyword', 'type', 'identifier'];

                if($.inArray(token.className, allowedTypes) != -1 && selection === token.string) {
                    var cursor = editor.getSearchCursor(new RegExp('\\b' + selection + '\\b')),
                        $code = $(editor.getScrollerElement().lastChild),
                        adjustXBy = $code.offset().left,
                        adjustYBy = $code.offset().top;

                    while (cursor.findNext()) {
                        var startPos = editor.charCoords(cursor.from()),
                            endPos = editor.charCoords(cursor.to()),
                            top = parseInt(startPos.y, 10),
                            left = parseInt(startPos.x, 10),
                            height = parseInt(startPos.yBot, 10) - top;
                            width = parseInt(endPos.x,10) - left;

                        if(!_coordsEqual(startPos,editor.cursorCoords(true))) {
                            $code.append(
                                $('<span />')
                                    .css({ position: 'absolute', top: top - adjustYBy, left: left - adjustXBy, width: width, height: height })
                                    .addClass('cm-selected-symbol')
                            );
                        }
                    }
                }
            }
        },

        _initCodeMirror = function(selector, options) {
            return CodeMirror.fromTextArea($(selector)[0], options);
        },

        _resizeEditor = function() {
            var newHeight = $('.content').height() - $('.content .instructions').height() - 100;
            $editor.width($editpage.width());
            $editor.height(newHeight);
        },

        _handleKeyEvents = function(e) {
            if (e.keyCode === 83 && (FORIO.utils.isMac ? e.metaKey : e.ctrlKey) && !e.altKey) { // cmd + s
                _save();
                e.preventDefault();

            } else if (e.keyCode === 70 && (FORIO.utils.isMac ? e.metaKey : e.ctrlKey) && !e.altKey) { // cmd + f
                if(!$toFind.is(':visible')) {
                    $toFind.val(editor.getSelection());
                    $findAndReplaceDialog.show();
                }
                $toFind.focus();

                e.preventDefault();
            }
        },

        _setIndentMode = function() {
            var val = $(this).val();

            editor.setOption('indentWithTabs', val === 'tabs');
            localStorage['indent-mode'] = val;
        },
        
        _setTheme = function () {
            var val = $(this).val();

            editor.setOption('theme', val);
            localStorage['theme'] = val;
        },

        _setIndentSize = function() {
            var size = parseInt($(this).val(), 10);

            editor.setOption('indentUnit', size);
            localStorage['indent-size'] = size;

            // can only make tabs of 4 spaces!
            if(size >= 4) {
                $indentMode.removeAttr('disabled');
            } else {
                $indentMode.val('spaces');
                $indentMode.attr('disabled', 'disabled');
            }
        },

        _setLineNumbers = function() {
            var checked = $(this).is(':checked');

            editor.setOption('lineNumbers', checked);
            localStorage['line-numbers'] = checked;
        },

        _checkSearchTerm = function () {
            var value = $(this).val();

            if(searchTerm !== value) {
                isDirty = true;
                searchTerm = value
            }

            if(value) {
                $replaceAllAndFindAndReplace.removeAttr('disabled');
            } else {
                $replaceAllAndFindAndReplace.attr({ disabled: 'disabled' });
            }
        },

        _handlePrev = function() {
            _find($toFind.val(), false /*look previous*/);
        },

        _handleReplace = function () {
            _replace($toReplace.val());
        },

        _handleReplaceAll = function() {
            _replaceAll($toFind.val(), $toReplace.val() || '');
        },

        _handleFindAndReplace = function() {
            _findAndReplace($toFind.val(), $toReplace.val());
        },

        _handleNext = function (e) {
            e.preventDefault();
            _find($toFind.val(), true /*look next*/);
        },

        _handleClose = function (e) {
            if(e.keyCode === 27) {
                e.preventDefault();
                $(this).hide();
            }
        },

        codeMirrorOptions = {
            lineNumbers : true,
            matchBrackets: true,
            tabSize: 4,
            indentUnit: 4,
            theme: "forio",
            extraKeys : commonKeyMap, /* auto complete disabled for now: $.extend({}, commonKeyMap, FORIO.utils.isMac ? macKeyMap : pcKeyMap), */
            onCursorActivity : function() {
                _highlightActiveLine();
                _highlightSelectedSymbol();
            },
            onHighlightComplete : function () {
                if(!isCompletelyLoaded)
                    $editpage.fadeIn();
                isCompletelyLoaded = true;
            }
        };

    return {
        init: function() {
            // global hotkeys
            $(document).bind('keydown keypress', _handleKeyEvents);

            // init editor
            editor = _initCodeMirror('#content', codeMirrorOptions);

            $editor = $(editor.getWrapperElement());
            $editpage = $('#editModelpage'),
            $save = $('#save'),
            $indentMode = $('#indent-mode'),
            $indentSize = $('#indent-size'),
            $lineNumbers = $('#line-numbers'),
            $theme = $('#theme'),
            $findAndReplaceDialog = $('#find-and-replace-dialog'),
            $ignoreCase = $('#ignore-case'),
            $regex = $('#regex'),
            $wrap = $('#wrap'),
            $toFind = $('#to-find'),
            $toReplace = $('#to-replace')
            $replace = $('#replace')
            $replaceAll = $('#replace-all'),
            $findAndReplace = $('#find-and-replace'),
            $replaceAllAndFindAndReplace = $('#replace-all, #find-and-replace'),
            $prev = $('#prev');

            // resize editor to fit window
            forioInterface.resizeMainContent(_resizeEditor);
            $(window).resize(function() {
                forioInterface.resizeMainContent(_resizeEditor);
            });

            // bind editor settings
            $indentMode.change(_setIndentMode);
            $indentSize.change(_setIndentSize);
            $lineNumbers.change(_setLineNumbers);
            $theme.change(_setTheme);

            // set defaults
            $indentMode.val(localStorage['indent-mode'] || 'tabs').change();
            $indentSize.val(parseInt(localStorage['indent-size'], 10) || 4).change();
            if(localStorage['line-numbers'] && localStorage['line-numbers'] === 'false') {
                $lineNumbers.removeAttr('checked').change();
            }
            $replaceAllAndFindAndReplace.attr({ disabled: 'disabled' });
            $theme.val(localStorage['theme'] || "forio").change();

            // make search cursor dirty
            $toFind.keyup(_checkSearchTerm);

            // bind find and replace dialog
            $prev.click(_handlePrev);
            $replace.click(_handleReplace);
            $replaceAll.click(_handleReplaceAll);
            $findAndReplace.click(_handleFindAndReplace);
            $findAndReplaceDialog.submit(_handleNext).keyup(_handleClose);
            $ignoreCase.change(_toggleCaseSensitive);
            $regex.change(_toggleRegex);
            $wrap.change(_toggleWrap);

            // save model
            $save.click(_save);

            // other bindings
            $("#ignoreErrorsSaveModel").click(function()
            {
                jQuery("#ignoreErrors").attr('value','true');
                jQuery("#form-import-model").submit();
            });
            
            // Fix a bug with short models not being highlighted
            setTimeout(codeMirrorOptions.onCursorActivity, 150);
        },
        getEditor: function() {
            return editor;
        }
    }
})(window.jQuery);

// localStorage polyfill
if (!window.localStorage) {
  Object.defineProperty(window, "localStorage", new (function () {
    var aKeys = [], oStorage = {};
    Object.defineProperty(oStorage, "getItem", {
      value: function (sKey) { return sKey ? this[sKey] : null; },
      writable: false,
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "key", {
      value: function (nKeyId) { return aKeys[nKeyId]; },
      writable: false,
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "setItem", {
      value: function (sKey, sValue) {
        if(!sKey) { return; }
        document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
      },
      writable: false,
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "length", {
      get: function () { return aKeys.length; },
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "removeItem", {
      value: function (sKey) {
        if(!sKey) { return; }
        var sExpDate = new Date();
        sExpDate.setDate(sExpDate.getDate() - 1);
        document.cookie = escape(sKey) + "=; expires=" + sExpDate.toGMTString() + "; path=/";
      },
      writable: false,
      configurable: false,
      enumerable: false
    });
    this.get = function () {
      var iThisIndx;
      for (var sKey in oStorage) {
        iThisIndx = aKeys.indexOf(sKey);
        if (iThisIndx === -1) { oStorage.setItem(sKey, oStorage[sKey]); }
        else { aKeys.splice(iThisIndx, 1); }
        delete oStorage[sKey];
      }
      for (aKeys; aKeys.length > 0; aKeys.splice(0, 1)) { oStorage.removeItem(aKeys[0]); }
      for (var iCouple, iKey, iCouplId = 0, aCouples = document.cookie.split(/\s*;\s*/); iCouplId < aCouples.length; iCouplId++) {
        iCouple = aCouples[iCouplId].split(/\s*=\s*/);
        if (iCouple.length > 1) {
          oStorage[iKey = unescape(iCouple[0])] = unescape(iCouple[1]);
          aKeys.push(iKey);
        }
      }
      return oStorage;
    };
    this.configurable = false;
    this.enumerable = true;
  })());
}


