Skip to content
Snippets Groups Projects
vplide.js 89.7 KiB
Newer Older
// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/
//
// VPL for Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// VPL for Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with VPL for Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * IDE Control
 * @package mod_vpl
 * @copyright 2017 Juan Carlos Rodríguez-del-Pino
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @author Juan Carlos Rodríguez-del-Pino <jcrodriguez@dis.ulpgc.es>
 */

/* globals Promise */
/* globals MathJax */
define(
    [
        'jquery',
        'jqueryui',
        'mod_vpl/vplutil',
        'mod_vpl/vplidefile',
        'mod_vpl/vplidebutton',
        'mod_vpl/vplterminal',
        'mod_vpl/vplvnc',
    ],
    function($, jqui, VPLUtil, VPLFile, VPLIDEButtons, VPLTerminal, VPLVNCClient) {
        if (typeof VPLIDE !== 'undefined') {
            return VPLIDE;
        }
        var vplIdeInstance;
        var VPLIDE = function(rootId, options) {
            var self = this;
            var fileManager;
            var adjustTabsTitles;
            var autoResizeTab;
            var showErrorMessage;
            var updateMenu;
            var minNumberOfFiles = options.minfiles || 0;
            var maxNumberOfFiles = options.maxfiles || 0;
            var restrictedEdit = options.restrictededitor || options.example;
            var readOnly = options.example;
            var fullScreen = false;
            var scrollBarWidth = VPLUtil.scrollBarWidth();
            VPLUtil.setStr({...M.str.mod_vpl, ...M.str.moodle, ...options.str});
            var str = VPLUtil.str;

            $("head").append('<meta name="viewport" content="initial-scale=1">')
                          .append('<meta name="viewport" width="device-width">');
            if (typeof rootObj != 'object') {
                throw new Error("VPL: constructor tag_id not found");
            }
            var optionsToCheck = {
                'new': true,
                'rename': true,
                'delete': true,
                'save': true,
                'run': true,
                'edit': true,
                'debug': true,
                'evaluate': true,
                'import': true,
                'resetfiles': true,
                'correctedfiles': true,
                'sort': true,
                'multidelete': true,
                'theme': true,
                'console': true,
                'comments': true
            if ((typeof options.loadajaxurl) == 'undefined') {
                options.loadajaxurl = options.ajaxurl;
            }
            (function() {
                var activateModification = (minNumberOfFiles < maxNumberOfFiles);
                options.new = activateModification;
                options.rename = activateModification;
                options.delete = activateModification;
                options.comments = options.comments && !options.example;
            })();
            options.sort = (maxNumberOfFiles - minNumberOfFiles >= 2);
            options.multidelete = options.sort;
            options.import = !restrictedEdit;
            var isOptionAllowed = function(op) {
                if (!optionsToCheck[op]) {
                    return true;
                }
                return options[op];
            options.console = isOptionAllowed('run') || isOptionAllowed('debug');
            if ((typeof options.fontSize) == 'undefined') {
                options.fontSize = 12;
            }
            options.fontSize = parseInt(options.fontSize);
            /**
             * Handler for dragover event.
             * @param {object} e event.
             */
            function dragoverHandler(e) {
                if (restrictedEdit) {
                    e.originalEvent.dataTransfer.dropEffect = 'none';
                } else {
                    e.originalEvent.dataTransfer.dropEffect = 'copy';
                }
                e.preventDefault();
            }
            /**
             * Handler for drop event.
             * @param {object} e event.
             * @returns {boolean}
             */
            function dropHandler(e) {
                if (restrictedEdit) { // No drop allowed.
                    e.stopImmediatePropagation();
                    return false;
                }
                var droppedFiles = [];
                // Function that lists all files and subfiles of given entry into droppedFiles.
                var listDroppedFiles = function(entry, path="") {
                    return new Promise(function(resolve) {
                        if (entry.isFile) {
                            // Current entry is a file : add it to the list.
                            entry.file(function(file) {
                                // Change its name s.t. it preserves directories structure.
                                var fullName = path + file.name;
                                Object.defineProperty(file, "name", {
                                });
                                droppedFiles.push(file);
                                resolve();
                            });
                        } else if (entry.isDirectory) {
                            // Current entry is a directory : process its content.
                            var dirReader = entry.createReader();
                            dirReader.readEntries(function(entries) {
                                var dirPromises = [];
                                for (var i=0; i<entries.length; i++) {
                                    dirPromises.push(listDroppedFiles(entries[i], path + entry.name + "/"));
                                }
                                Promise.all(dirPromises).then(resolve);
                            });
                        } else {
                            // This is neither a directory nor a file : ignore it.
                            resolve();
                        }
                    });
                };
                var dt = e.originalEvent.dataTransfer;

                // List every element of the drop event.
                var promises = [];
                for (var i=0; i<dt.items.length; i++) {
                    promises.push(listDroppedFiles(dt.items[i].webkitGetAsEntry()));
                }

                // Drop files.
                if (dt.files.length > 0) {
                    Promise.all(promises)
                        VPLUtil.readSelectedFiles(droppedFiles, function(file) {
                            return fileManager.addFile(file, true, updateMenu, showErrorMessage);
                        },
                            fileManager.fileListVisibleIfNeeded();
                        });
                        return;
                    });
                    e.stopImmediatePropagation();
                    return false;
                }
            }
            rootObj.on('drop', dropHandler);
            rootObj.on('dragover', dragoverHandler);
            /**
             * Handler for paste limited by restrictedEdit var.
             * @param {object} e event.
             * @returns {boolean}
             */
            function restrictedPaste(e) {
                if (restrictedEdit) {
                    e.stopPropagation();
                    return false;
                }
            }
            // Init editor vars.
            var menu = $('#vpl_menu');
            var menuButtons = new VPLIDEButtons(menu, isOptionAllowed);
            var tr = $('#vpl_tr');
            var fileListContainer = $('#vpl_filelist');
            var fileList = $('#vpl_filelist_header');
            var fileListContent = $('#vpl_filelist_content');
            var tabsUl = $('#vpl_tabs_ul');
            var tabs = $('#vpl_tabs');
            var resultContainer = $('#vpl_results');
            var result = $('#vpl_results_accordion');
            fileListContainer.vplMinWidth = 80;
            /**
             * Avoids selecting grade.
             * @param {object} event Unuse.
             * @param {object} ui UI origen.
             * @returns {boolean}
             */
            function avoidSelectGrade(event, ui) {
                if ("newHeader" in ui) {
                    if (ui.newHeader.hasClass('vpl_ide_accordion_t_grade')) {
                        return false;
                    }
                }
            function FileManager() {
                var tabsUl = $('#vpl_tabs_ul');
                var tabs = $('#vpl_tabs').tabs("widget");
                var files = [];
                var openFiles = [];
                var modified = true;
                var self = this;
                (function() {
                    var version;
                    self.setVersion = function(v) {
                       version = v;
                    };
                    self.getVersion = function() {
                       return version;
                    };
                })();
                this.updateFileList = function() {
                    self.generateFileList();
                };
                this.fileNameExists = function(name) {
                    var checkName = name.toLowerCase();
                    for (var i = 0; i < files.length; i++) {
                        if (files[i].getFileName().toLowerCase() == checkName) {
                            return i;
                        }
                    }
                    return -1;
                };
                /**
                 * Checks if name is included in current files names
                 * @param {string} name Name of file
                 * @returns {boolean} if found or not found
                 */
                function fileNameIncluded(name) {
                    var checkName = name.toLowerCase() + '/';
                    for (var i = 0; i < files.length; i++) {
                        var nameMod = files[i].getFileName().toLowerCase() + '/';
                        // Check for name as directory existent.
                        if (nameMod.indexOf(checkName) === 0 || checkName.indexOf(nameMod) === 0) {
                            return true;
                        }
                    }
                    return false;
                }
                /**
                 * Checks if changing file name results in two blovkly files
                 * @param {string} oldname The old file name
                 * @param {string} newname The new file name
                 * @returns {boolean} if results two two blovkly files
                 */
                function twoBlockly(oldname, newname) {
                    if (VPLUtil.isBlockly(oldname)) {
                        return false;
                    }
                    if (VPLUtil.isBlockly(newname)) {
                        for (var i = 0; i < files.length; i++) {
                            if (VPLUtil.isBlockly(files[i].getFileName())) {
                                return true;
                            }
                        }
                    }
                    return false;
                }

                this.restrictedPaste = restrictedPaste;
                this.dropHandler = dropHandler;
                this.dragoverHandler = dragoverHandler;
                this.readOnly = readOnly;
                this.restrictedEdit = restrictedEdit;
                this.adjustTabsTitles = adjustTabsTitles;
                this.minNumberOfFiles = minNumberOfFiles;
                this.scrollBarWidth = scrollBarWidth;
                var localClipboard = "";
                this.setClipboard = function(t) {
                    localClipboard = t;
                };
                this.getClipboard = function() {
                    return localClipboard;
                };
                this.getTabPos = function(file) {
                    for (var i = 0; i < openFiles.length; i++) {
                            return i;
                        }
                    }
                    return openFiles.length;
                };
                    return options.theme;
                };
                    options.theme = theme;
                    for (var i = 0; i < files.length; i++) {
                        files[i].setTheme(theme);
                    }
                };
                this.addTab = function(fid) {
                    var hlink = '<a href="#vpl_file' + fid + '"></a>';
                    tabsUl.append('<li id="vpl_tab_name' + fid + '">' + hlink + '</li>');
                    tabs.append('<div id="vpl_file' + fid + '" class="vpl_ide_file"></div>');
                };
                this.removeTab = function(fid) {
                    tabsUl.find('#vpl_tab_name' + fid).remove();
                    tabs.find('#vpl_file' + fid).remove();
                };
                this.open = function(pos) {
                    var file;
                    if (typeof pos == 'object') {
                        file = pos;
                    } else {
                        file = files[pos];
                    }
                    if (file.isOpen()){
                        return;
                    }
                    var fid = file.getId();
                    self.addTab(fid);
                    openFiles.push(file);
                    menuButtons.setGetkeys(file.open());
                    tabs.tabs('refresh');
                    adjustTabsTitles(false);
                    VPLUtil.delay('updateFileList', self.updateFileList);
                    VPLUtil.delay('updateMenu', updateMenu);
                };
                this.close = function(file) {
                    if (!file.isOpen()) {
                        return;
                    }
                    var pos;
                    var fid = file.getId();
                    file.close();
                    self.removeTab(fid);
                    var ptab = self.getTabPos(file);
                    openFiles.splice(ptab, 1);
                    tabs.tabs('refresh');
                    adjustTabsTitles(false);
                    self.fileListVisible(true);
                    VPLUtil.delay('updateFileList', self.updateFileList);
                    VPLUtil.delay('adjustTabsTitles', adjustTabsTitles, false);
                    if (openFiles.length > ptab) {
                        pos = self.getFilePosById(openFiles[ptab].getId());
                        self.gotoFile(pos, 'c');
                        return;
                    }
                    if (ptab > 0) {
                        pos = self.getFilePosById(openFiles[ptab - 1].getId());
                        self.gotoFile(pos, 'c');
                        return;
                    }
                };
                this.isClosed = function(pos) {
                    return !files[pos].isOpen();
                };
                this.fileListVisible = function(b) {
                    if (b === fileListContainer.vplVisible){
                        VPLUtil.delay('fileListVisible', function() {
                            fileListContainer.vplVisible = true;
                            self.updateFileList();
                            fileListContainer.show();
                            autoResizeTab();
                        });
                        VPLUtil.delay('fileListVisible', function() {
                            fileListContainer.vplVisible = false;
                            fileListContainer.hide();
                            autoResizeTab();
                        });
                    }
                };
                this.isFileListVisible = function() {
                };
                this.fileListVisibleIfNeeded = function() {
                        return;
                    }
                    for (var i = 0; i < files.length; i++) {
                        if (!files[i].isOpen()) {
                            this.fileListVisible(true);
                            return;
                        }
                    }
                };
                this.setFontSize = function(size) {
                    options.fontSize = size;
                    for (var i = 0; i < files.length; i++) {
                        files[i].setFontSize(size);
                    }
                };
                this.getFontSize = function() {
                    return options.fontSize;
                };
                this.addFile = function(file, replace, ok, showError) {
                    if ((typeof file.name != 'string') || !VPLUtil.validPath(file.name)) {
                        showError(str('incorrect_file_name') + ' (' + file.name + ')');
                        return false;
                    }
                    if (replace !== true) {
                        replace = false;
                    }
                    var pos = this.fileNameExists(file.name);
                    if (pos != -1) {
                        if (replace) {
                            files[pos].setContent(file.contents);
                            self.setModified();
                            ok();
                            VPLUtil.delay('updateFileList', self.updateFileList);
                            return file;
                        } else {
                            showError(str('filenotadded', file.name));
                            return false;
                        }
                    }
                    if (fileNameIncluded(file.name) || twoBlockly('', file.name)) {
                        showError(str('filenotadded', file.name));
                        return false;
                    }
                    if (files.length >= maxNumberOfFiles) {
                        showError(str('maxfilesexceeded') + ' (' + maxNumberOfFiles + ')');
                        return false;
                    }
                    var fid = VPLUtil.getUniqueId();
                    var newfile = new VPLFile(fid, file.name, file.contents, this, vplIdeInstance, options.id);
                    if (file.encoding == 1) {
                        newfile.extendToBinary();
                    } else {
                        if (VPLUtil.isBlockly(file.name)) {
                            newfile.extendToBlockly();
                        } else {
                            newfile.extendToCodeEditor();
                        }
                    }
                    files.push(newfile);
                    self.setModified();
                    if (files.length > 5) {
                        self.fileListVisible(true);
                    }
                    ok();
                    return newfile;
                };
                this.renameFile = function(oldname, newname, showError) {
                    var pos = this.fileNameExists(oldname);
                    try {
                        if (pos == -1) {
                            throw new Error("Internal error: File name not found");
                        if (pos < minNumberOfFiles) {
                            throw new Error("Internal error: Renaming required filename");
                        if (files[pos].getFileName() == newname) {
                            return true; // Equals name file.
                        }
                        if (!VPLUtil.validPath(newname) ||
                               fileNameIncluded(newname) ||
                               twoBlockly(oldname, newname)) {
                            throw str('incorrect_file_name');
                        }
                        if (VPLUtil.isBinary(oldname) && VPLUtil.fileExtension(oldname) != VPLUtil.fileExtension(newname)) {
                            throw str('incorrect_file_name');
                        }
                        if (VPLUtil.isBlockly(oldname) != VPLUtil.isBlockly(newname)) {
                            throw str('incorrect_file_name');
                        }
                        files[pos].setFileName(newname);
                    } catch (e) {
                        showError(str('filenotrenamed', newname) + ': ' + e);
                        return false;
                    }
                    self.setModified();
                    adjustTabsTitles(false);
                    VPLUtil.delay('updateFileList', self.updateFileList);
                    return true;
                };
                this.deleteFile = function(name, ok, showError) {
                    var pos = this.fileNameExists(name);
                    if (pos == -1) {
                        showError(str('filenotdeleted', name));
                        return false;
                    }
                    if (pos < minNumberOfFiles) {
                        showError(str('filenotdeleted', name));
                        return false;
                    }
                    self.setModified();
                    self.close(files[pos]);
                    files.splice(pos, 1);
                    VPLUtil.delay('updateFileList', self.updateFileList);
                    return true;
                };
                this.currentFile = function() {
                    var id = tabs.tabs('option', 'active');
                    if (id in openFiles) {
                        var file = openFiles[id];
                        if (arguments.length === 0) {
                            return file;
                        }
                        var action = arguments[0];
                        if (typeof file[action] === 'function') {
                            var fun = file[action];
                            var args = Array.prototype.slice(arguments);
                            args.shift();
                            return fun.apply(file, args);
                        }
                    }
                    return false;
                };
                this.currentPos = function() {
                    return tabs.tabs('option', 'active');
                };
                this.getFileTab = function(id) {
                    for (var i = 0; i < openFiles.length; i++) {
                        if (openFiles[i].getId() == id) {
                            return i;
                        }
                    }
                    return -1;
                };
                this.getFilePosById = function(id) {
                    for (var i = 0; i < files.length; i++) {
                        if (files[i].getId() == id) {
                            return i;
                        }
                    }
                    return -1;
                };
                this.gotoFile = function(pos, l) {
                    var file = files[pos];
                    self.open(file);
                    tabs.tabs('option', 'active', self.getFileTab(file.getId()));
                    if (l !== 'c') {
                        file.gotoLine(parseInt(l, 10));
                    }
                    file.focus();
                };
                this.gotoFileLink = function(a) {
                    var tag = $(a);
                    var fpos = -1;
                    if (fname > '') {
                        fpos = self.getFilePosById(tag.data('fileid'));
                    }
                    if (fpos >= 0) {
                        var line = tag.data('line');
                            line = 'c';
                        }
                        self.gotoFile(fpos, line);
                };
                this.getFilesToSave = function() {
                    var ret = [];
                    for (var i = 0; i < files.length; i++) {
                        var file = {};
                        file.name = files[i].getFileName();
                        file.contents = files[i].getContent();
                        file.encoding = files[i].isBinary() ? 1 : 0;
                        ret.push(file);
                    }
                    return ret;
                };
                this.resetModified = function() {
                    modified = false;
                    for (var i = 0; i < files.length; i++) {
                        files[i].resetModified();
                    }
                    VPLUtil.delay('updateMenu', updateMenu);
                    VPLUtil.delay('updateFileList', self.updateFileList);
                };
                this.setModified = function() {
                    modified = true;
                    VPLUtil.delay('updateFileList', self.updateFileList);
                    VPLUtil.delay('updateMenu', updateMenu);
                };
                this.isModified = function() {
                    return modified;
                };
                this.length = function() {
                    return files.length;
                };
                this.clearAnnotations = function() {
                    for (var i = 0; i < files.length; i++) {
                        files[i].clearAnnotations();
                    }
                };
                this.getFile = function(i) {
                    return files[i];
                };
                this.getFiles = function() {
                    return files;
                };
                this.getDirectoryStructure = function() {
                    var structure = {
                    /**
                     * Adds a new file the structure of directories
                     * @param {int} i Index of file to add in the file array
                     */
                    function addFilePath(i) {
                        var file = files[i];
                        var fileName = file.getFileName();
                        var path = fileName.split("/");
                        var curdir = structure;
                        for (var p in path) {
                            if (path.hasOwnProperty(p)) {
                                var part = path[p];
                                if (p == path.length - 1) { // File.
                                    curdir.content[part] = {
                                        isDir: false,
                                        content: file,
                                        pos: i
                                    };
                                } else {
                                    if (!curdir.content[part]) { // New dir.
                                        curdir.content[part] = {
                                    // Descend Dir.
                                    curdir = curdir.content[part];
                    for (var i in files) {
                        if (files.hasOwnProperty(i)) {
                            addFilePath(i);
                        }
                    }
                    return structure;
                };
                this.generateFileList = function() {
                        return;
                    }
                    var dirIndent = '<span class="vpl_ide_dirindent"></span>';
                    /**
                     * Generates an array of string with the HTML code to represent the list of IDE files
                     * @param {Object} dir Current directory
                     * @param {int} indent Html code to indent subdirectories
                     * @param {Array} lines Output. Each line contains the HTML to represent an IDE file
                     */
                    function lister(dir, indent, lines) {
                        for (var name in dir.content) {
                            if (dir.content.hasOwnProperty(name)) {
                                var fd = dir.content[name];
                                if (fd.isDir) {
                                    lines.push(indent + VPLUtil.iconFolder() + VPLUtil.sanitizeText(name));
                                    lister(fd, indent + dirIndent, lines);
                                } else {
                                    var file = fd.content;
                                    var sname = VPLUtil.sanitizeText(name);
                                    var path = VPLUtil.sanitizeText(file.getFileName());
                                    if (file.isOpen()) {
                                        sname = '<b>' + sname + '</b>';
                                    }
                                    var attrs = 'href="#" data-fileid="' + file.getId() + '" title="' + path + '"';
                                    var line = '<a ' + attrs + '>' + sname + '</a>';
                                    if (file.isModified()) {
                                        line = VPLUtil.iconModified() + line;
                                    }
                                    if (fd.pos < minNumberOfFiles) {
                                        line = line + VPLUtil.iconRequired();
                                    }
                                }
                            }
                        }
                    }

                    var structure = self.getDirectoryStructure();
                    var lines = [];
                    var html = '';
                    for (var i = 0; i < lines.length; i++) {
                    }
                    fileListContent.html('<div>' + html + '</div>');
                };
                tabsUl.on('click', 'span.vpl_ide_closeicon', function() {
                    fileManager.close(fileManager.currentFile());
                });
                tabsUl.on('dblclick', 'span.vpl_ide_closeicon', menuButtons.getAction('delete'));
                tabsUl.on('dblclick', 'a', menuButtons.getAction('rename'));
                fileListContent.on('dblclick', 'a', menuButtons.getAction('rename'));

            }
            this.updateEvaluationNumber = function(res) {
                if (typeof res.nevaluations != 'undefined') {
                    var text = res.nevaluations;
                    if (typeof res.reductionbyevaluation != 'undefined'
                         && res.reductionbyevaluation > ''
                         && res.reductionbyevaluation != 0) {
                            text = text + '/' + res.freeevaluations;
                        }
                        text = text + ' -' + res.reductionbyevaluation;
                    }
                    menuButtons.setExtracontent('evaluate', text);
                }
            };
            this.lastResult = null;
            this.getTerminal = function() {
                return terminal;
            };
            this.setResultGrade = function(content, raw) {
                var name = 'grade';
                var titleclass = 'vpl_ide_accordion_t_' + name;
                var contentclass = 'vpl_ide_accordion_c_' + name;
                if (result.find('.' + contentclass).length == 0) {
                    result.append('<div class="' + titleclass + '"></div>');
                    result.append('<div class="' + contentclass + '"></div>');
                }
                if (typeof raw == 'undefined') {
                    return result.find('h4.' + titleclass).length > 0;
                }
                var titleTag = result.find('.' + titleclass);
                    titleTag.replaceWith('<h4 class="' + titleclass + '">' + content + '</h4>');
                    return true;
                } else {
                    titleTag.replaceWith('<div class="' + titleclass + '"></div>');
                    return false;
                }
            };
            this.setResultTab = function(name, content, raw) {
                var titleclass = 'vpl_ide_accordion_t_' + name;
                var contentclass = 'vpl_ide_accordion_c_' + name;
                if (result.find('.' + contentclass).length == 0) {
                    result.append('<div class="' + titleclass + '"></div>');
                    result.append('<div class="' + contentclass + '"></div>');
                }
                if (typeof raw == 'undefined') {
                    return result.find('h4.' + titleclass).length > 0;
                }
                var titleTag = result.find('.' + titleclass);
                var contentTag = result.find('.' + contentclass);
                var HTMLcontent = $('<div>' + content + '</div>');
                HTMLcontent.find('h4').replaceWith(function() {
                    return $("<h5>").append($(this).contents());
                });
                if (contentTag.html() == HTMLcontent.html()) {
                    return content > '';
                }
                    titleTag.replaceWith('<h4 class="' + titleclass + '">' + str(name) + '</h4>');
                    contentTag.replaceWith('<div class="ui-widget ' + contentclass + '">' + HTMLcontent.html() + '</div>');
                    return true;
                } else {
                    titleTag.replaceWith('<div class="' + titleclass + '"></div>');
                    contentTag.replaceWith('<div class="' + contentclass + '"></div>');
                    return false;
                }
            };
            this.setResult = function(res, go) {
                self.updateEvaluationNumber(res);
                var files = fileManager.getFiles();
                var fileNames = [];
                var i;
                for (i = 0; i < files.length; i++) {
                    fileNames[i] = files[i].getFileName();
                    files[i].clearAnnotations();
                }
                var show = false;
                var hasContent;
                var grade = VPLUtil.sanitizeText(res.grade);
                var gradeShow;
                var formated;
                gradeShow = self.setResultGrade(grade, res.grade);
                show = show || gradeShow;
                hasContent = self.setResultTab('variables', res.variables, res.variables);
                show = show || hasContent;
                formated = VPLUtil.processResult(res.compilation, fileNames, files, true, false);
                hasContent = self.setResultTab('compilation', formated, res.compilation);
                show = show || hasContent;
                formated = VPLUtil.processResult(res.evaluation, fileNames, files, false, false);
                hasContent = self.setResultTab('comments', formated, res.evaluation);
                show = show || hasContent;
                formated = VPLUtil.sanitizeText(res.execution);
                hasContent = self.setResultTab('execution', formated, res.execution);
                show = show || hasContent;
                hasContent = self.setResultTab('description', options.description, options.description);
                if (hasContent && typeof MathJax == 'object') { // MathJax workaround.
                    var math = result.find(".vpl_ide_accordion_c_description")[0];
                    MathJax.Hub.Queue(["Typeset", MathJax.Hub, math]);
                }
                show = show || hasContent;
                if ( show ) {
                    resultContainer.show();
                    resultContainer.vplVisible = true;
                    result.accordion("refresh");
                    result.accordion('option', 'active', gradeShow ? 1 : 0);
                    for (i = 0; i < files.length; i++) {
                        var anot = files[i].getAnnotations();
                        for (var j = 0; j < anot.length; j++) {
                            if (go || anot[j].type == 'error') {
                                fileManager.gotoFile(i, anot[j].row + 1);
                                break;
                            }
                        }
                    }
                } else {
                    resultContainer.hide();
                    resultContainer.vplVisible = false;
                }
                VPLUtil.delay('autoResizeTab', autoResizeTab);
            };

            result.accordion({
                animate: false,
            });
            result.on('click', 'a', function(event) {
                if (fileManager.gotoFileLink(event.currentTarget)) {
                    event.preventDefault();
                }
            resultContainer.vplVisible = false;
            resultContainer.hide();

            fileListContainer.addClass('ui-tabs ui-widget ui-widget-content ui-corner-all');
            fileList.text(str('filelist'));
            fileList.html(VPLUtil.iconFolder() + fileList.html());
            fileList.addClass("ui-widget-header ui-button-text-only ui-corner-all");
            fileListContent.addClass("ui-widget ui-corner-all");
            fileListContainer.width(2 * fileListContainer.vplMinWidth);
            fileListContainer.on('click', 'a', function(event) {
                event.preventDefault();
                fileManager.gotoFileLink(event.currentTarget);
            });
            fileListContainer.vplVisible = false;
            fileListContainer.hide();
            var tabsAir = false;
            /**
             * Returns separation space
             * @returns {int} size in pixels
             */
            function getTabsAir() {
                if (tabsAir === false) {
                    tabsAir = (tabs.outerWidth(true) - tabs.width()) / 2;
                }
                return tabsAir;
            }
            /**
             * Resize tab width
             * @param {Event} e Unused
             * @param {Object} ui UI object
             */
            function resizeTabWidth(e, ui) {
                var diffLeft = ui.position.left - ui.originalPosition.left;
                var maxWidth;
                if (diffLeft !== 0) {
                    maxWidth = tabs.width() + fileListContainer.width() - fileListContainer.vplMinWidth;
                    tabs.resizable('option', 'maxWidth', maxWidth);
                    fileListContainer.width(fileListContainer.vplOriginalWidth + diffLeft);
                } else {
                    var diff_width = ui.size.width - ui.originalSize.width;
                    resultContainer.width(resultContainer.vplOriginalWidth - diff_width);
                }
                fileManager.currentFile('adjustSize');
            }
            var resizableOptions = {
                containment: 'parent',
                resize: resizeTabWidth,
                start: function() {
                    $(window).off('resize', autoResizeTab);
                    tabs.resizable('option', 'minWidth', 100);
                    if (resultContainer.vplVisible) {
                        resultContainer.vplOriginalWidth = resultContainer.width();
                    if (fileListContainer.vplVisible) {
                        fileListContainer.vplOriginalWidth = fileListContainer.width();
                    resizeTabWidth(e, ui);
                    tabs.resizable('option', 'maxWidth', 100000);
                    tabs.resizable('option', 'minWidth', 0);
                    autoResizeTab();
                    $(window).on('resize', autoResizeTab);
                },
                handles : ""
            };
            tabs.resizable(resizableOptions);
            /**
             * Updates handles for internal IDE resize
             */
            function updateTabsHandles() {
                var handles = ['e', 'w', 'e', 'e, w'];
                var index = 0;
                index += fileListContainer.vplVisible ? 1 : 0;
                index += resultContainer.vplVisible ? 2 : 0;
                tabs.resizable('destroy');
                resizableOptions.handles = handles[index];
                resizableOptions.disable = index === 0;
                tabs.resizable(resizableOptions);
            }
            function resizeHeight() {
                var newHeight = (fullScreen ? 1 : 0.7) * ($(window).innerHeight() - menu.height() - getTabsAir());
                if (newHeight < 150) {
                    newHeight = 150;
                }
                var panelHeight = newHeight - 3 * getTabsAir();
                tabs.height(panelHeight);
                if (resultContainer.vplVisible) {
                    resultContainer.height(panelHeight + getTabsAir());
                    result.accordion('refresh');
                if (fileListContainer.vplVisible) {
                    fileListContent.height(panelHeight - (fileList.outerHeight() + getTabsAir()));
                    fileListContainer.height(panelHeight);
                }
            }
            adjustTabsTitles = function(center) {
                var newWidth = tabs.width();
                var tabsUlWidth = 0;
                tabsUl.width(100000);
                var last = tabsUl.children('li:visible').last();
                if (last.length) {
                    var parentScrollLeft = tabsUl.parent().scrollLeft();
                    tabsUlWidth = parentScrollLeft + last.position().left + last.width() + tabsAir;
                    tabsUl.width(tabsUlWidth);
                    var file = fileManager.currentFile();
                    if (file && center) {
                        var fileTab = $(file.getTabNameId());
                        var scroll = parentScrollLeft + fileTab.position().left;
                        scroll -= (newWidth - fileTab.outerWidth()) / 2;
                        if (scroll < 0) {
                            scroll = 0;
                        }
                        tabsUl.parent().finish().animate({
                        }, 'slow');
                    }
                }
                if (tabsUlWidth < newWidth) {
                    tabsUl.width('');
                }
            };
                var oldWidth = tabs.width();
                var newWidth = menu.width();
                var planb = false;
                updateTabsHandles();
                tr.width(menu.outerWidth());
                    var left = fileListContainer.outerWidth() + tabsAir;
                    oldWidth += left;
                    if (left >= 100) {
                        newWidth -= left;
                        tabs.css('left', left);
                    } else {
                        planb = true;
                    }
                } else {
                    tabs.css('left', 0);
                }