/* * Copyright (c) Codiad & Kent Safranski (codiad.com), distributed * as-is and without warranty under the MIT License. See * [root]/license.txt for more. This information must remain intact. */ (function(global, $) { var EditSession = require('ace/edit_session') .EditSession; var UndoManager = require('ace/undomanager') .UndoManager; var codiad = global.codiad; $(function() { codiad.active.init(); }); ////////////////////////////////////////////////////////////////// // // Active Files Component for Codiad // --------------------------------- // Track and manage EditSession instaces of files being edited. // ////////////////////////////////////////////////////////////////// codiad.active = { controller: 'components/active/controller.php', // Path to EditSession instance mapping sessions: {}, ////////////////////////////////////////////////////////////////// // // Check if a file is open. // // Parameters: // path - {String} // ////////////////////////////////////////////////////////////////// isOpen: function(path) { return !!this.sessions[path]; }, open: function(path, content, inBackground) { var _this = this; if (this.isOpen(path)) { this.focus(path); return; } var ext = codiad.filemanager.getExtension(path); var mode = codiad.editor.selectMode(ext); var _this = this; var fn = function() { var Mode = require('ace/mode/' + mode) .Mode; // TODO: Ask for user confirmation before recovering // And maybe show a diff var draft = _this.checkDraft(path); if (draft) { content = draft; codiad.message.success('Recovered unsaved content for : ' + path); } var session = new EditSession(content, new Mode()); session.setUndoManager(new UndoManager()); session.path = path; _this.sessions[path] = session; if (!inBackground) { codiad.editor.setSession(session); } _this.add(path, session); }; $.loadScript('components/editor/ace-editor/mode-' + mode + '.js', fn); }, init: function() { var _this = this; // Focus $('#active-files a') .live('click', function() { _this.focus($(this) .attr('data-path')); }); // Remove $('#active-files a>span') .live('click', function(e) { e.stopPropagation(); _this.remove($(this) .parent('a') .attr('data-path')); }); // Sortable $('#active-files') .sortable({ placeholder: 'active-sort-placeholder', tolerance: 'intersect', start: function(e, ui) { ui.placeholder.height(ui.item.height()); } }); // Open saved-state active files on load $.get(_this.controller + '?action=list', function(data) { var listResponse = codiad.jsend.parse(data); if (listResponse !== null) { $.each(listResponse, function(index, value) { codiad.filemanager.openFile(value); }); // Run resize command to fix render issues codiad.editor.resize(); } }); // Run resize on window resize $(window) .on('resize', function() { codiad.editor.resize(); }); // Prompt if a user tries to close window without saving all filess window.onbeforeunload = function(e) { if ($('#active-files a.changed') .length > 0) { var e = e || window.event; var errMsg = 'You have unsaved files.'; // For IE and Firefox prior to version 4 if (e) { e.returnValue = errMsg; } // For rest return errMsg; } }; }, ////////////////////////////////////////////////////////////////// // Drafts ////////////////////////////////////////////////////////////////// checkDraft: function(path) { var draft = localStorage.getItem(path); if (draft !== null) { return draft; } else { return false; } }, removeDraft: function(path) { localStorage.removeItem(path); }, ////////////////////////////////////////////////////////////////// // Get active editor path ////////////////////////////////////////////////////////////////// getPath: function() { try { return codiad.editor.getActive() .getSession() .path; } catch (e) { return null; } }, ////////////////////////////////////////////////////////////////// // Check if opened by another user ////////////////////////////////////////////////////////////////// check: function(path) { $.get(this.controller + '?action=check&path=' + path, function(data) { var checkResponse = codiad.jsend.parse(data); }); }, ////////////////////////////////////////////////////////////////// // Add newly opened file to list ////////////////////////////////////////////////////////////////// add: function(path, session) { var thumb = $('
' + path + '
'); session.thumb = thumb; $('#active-files') .append($('
  • ') .append(thumb)); $.get(this.controller + '?action=add&path=' + path); this.focus(path); // Mark draft as changed if (this.checkDraft(path)) { this.markChanged(path); } }, ////////////////////////////////////////////////////////////////// // Focus on opened file ////////////////////////////////////////////////////////////////// focus: function(path) { this.highlightEntry(path); var session = this.sessions[path]; codiad.editor.setSession(session); this.check(path); }, highlightEntry: function(path){ $('#active-files a') .removeClass('active'); this.sessions[path].thumb.addClass('active'); }, ////////////////////////////////////////////////////////////////// // Mark changed ////////////////////////////////////////////////////////////////// markChanged: function(path) { this.sessions[path].thumb.addClass('changed'); }, ////////////////////////////////////////////////////////////////// // Save active editor ////////////////////////////////////////////////////////////////// save: function(path) { var _this = this; if ((path && !this.isOpen(path)) || (!path && !codiad.editor.getActive())) { codiad.message.error('No Open Files to save'); return; } var session; if (path) session = this.sessions[path]; else session = codiad.editor.getActive() .getSession(); var content = session.getValue(); var path = session.path; codiad.filemanager.saveFile(path, content, { success: function() { session.thumb.removeClass('changed'); _this.removeDraft(path); } }); }, ////////////////////////////////////////////////////////////////// // Remove file ////////////////////////////////////////////////////////////////// remove: function(path) { if (!this.isOpen(path)) return; var session = this.sessions[path]; var closeFile = true; if (session.thumb.hasClass('changed')) { codiad.modal.load(450, 'components/active/dialog.php?action=confirm&path=' + path); closeFile = false; } if (closeFile) { this.close(path); } }, close: function(path) { var session = this.sessions[path]; session.thumb.parent('li') .remove(); var nextThumb = $('#active-files a[data-path]'); if (nextThumb.length == 0) { codiad.editor.exterminate(); } else { $(nextThumb[0]) .addClass('active'); var nextPath = nextThumb.attr('data-path'); var nextSession = this.sessions[nextPath]; codiad.editor.removeSession(session, nextSession); } delete this.sessions[path]; $.get(this.controller + '?action=remove&path=' + path); this.removeDraft(path); }, ////////////////////////////////////////////////////////////////// // Process rename ////////////////////////////////////////////////////////////////// rename: function(oldPath, newPath) { var switchSessions = function(oldPath, newPath) { var thumb = this.sessions[oldPath].thumb; thumb.attr('data-path', newPath); thumb.find('div') .text(newPath); this.sessions[newPath] = this.sessions[oldPath]; this.sessions[newPath].path = newPath; this.sessions[oldPath] = undefined; }; if (this.sessions[oldPath]) { // A file was renamed switchSessions.apply(this, [oldPath, newPath]); if (codiad.editor.getActive().session.path == oldPath) { codiad.editor.setActive(this.sessions[newPath]); } // Change Editor Mode var ext = codiad.filemanager.getExtension(newPath); var mode = codiad.editor.selectMode(ext); this.sessions[newPath].setMode("ace/mode/" + mode); } else { // A folder was renamed var newKey; for (var key in this.sessions) { newKey = key.replace(oldPath, newPath); if (newKey !== key) { switchSessions.apply(this, [key, newKey]); } } } $.get(this.controller + '?action=rename&old_path=' + oldPath + '&new_path=' + newPath); }, ////////////////////////////////////////////////////////////////// // Open in Browser ////////////////////////////////////////////////////////////////// openInBrowser: function() { var path = this.getPath(); if (path) { codiad.filemanager.openInBrowser(path); } else { codiad.message.error('No Open Files'); } }, ////////////////////////////////////////////////////////////////// // Get Selected Text ////////////////////////////////////////////////////////////////// getSelectedText: function() { var path = this.getPath(); var session = this.sessions[path]; if (path && this.isOpen(path)) { return session.getTextRange( codiad.editor.getActive() .getSelectionRange()); } else { codiad.message.error('No Open Files or Selected Text'); } }, ////////////////////////////////////////////////////////////////// // Insert Text ////////////////////////////////////////////////////////////////// insertText: function(val) { codiad.editor.getActive() .insert(val); }, ////////////////////////////////////////////////////////////////// // Goto Line ////////////////////////////////////////////////////////////////// gotoLine: function(line) { codiad.editor.getActive() .gotoLine(line, 0, true); }, ////////////////////////////////////////////////////////////////// // Move Up (Key Combo) ////////////////////////////////////////////////////////////////// move: function(dir) { var num = $('#active-files a') .length; if (num > 1) { if (dir == 'up') { // Move Up or rotate to bottom newActive = $('#active-files li a.active') .parent('li') .prev('li') .children('a') .attr('data-path'); if (!newActive) { newActive = $('#active-files li:last-child a') .attr('data-path'); } } else { // Move down or rotate to top newActive = $('#active-files li a.active') .parent('li') .next('li') .children('a') .attr('data-path'); if (!newActive) { newActive = $('#active-files li:first-child a') .attr('data-path'); } } this.focus(newActive); } } }; })(this, jQuery);