Merge pull request #78 from owncloud/2.0

Version 2.0
This commit is contained in:
Jan-Christoph Borchardt 2015-09-03 01:27:03 +02:00
Родитель 958f3d11fa 85c993c89b
Коммит 0f8ba1addf
5 изменённых файлов: 743 добавлений и 437 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -1,2 +1,4 @@
# Mac OS
.DS_Store
# Tests - auto-generated files
/tests/clover.xml

Просмотреть файл

@ -4,3 +4,39 @@ files_texteditor
A text editor app for ownCloud
To see which files are editable, see [here](https://github.com/owncloud/files_texteditor/blob/master/js/editor.js)
Preview apps
----
Apps can add side-by-side previews to the app for certain file types by using the preview api
```js
OCA.MYApp.Preview = function(){
...
}
OCA.MYApp.Preview.Prototype = {
/**
* Give the app the opportunity to load any resources it needs and prepare for rendering a preview
*/
init: function() {
...
},
/**
* @param {string} the text to create the preview for
* @param {jQuery} the jQuery element to render the preview in
*/
preview: function(text, previewElement) {
...
}
}
OCA.Files_Texteditor.registerPreviewPlugin('text/markdown', new OCA.MYApp.Preview());
```
For styling of the preview, the preview element will have the id `preview` and the className will be set to the mimetype of the file being eddited with any slash replaced by dashes.
e.g. when editing a markdown file the preview element can be styled using the `#preview.text-markdown` css query.

Просмотреть файл

@ -1 +1 @@
0.4
2.0

Просмотреть файл

@ -1,37 +1,192 @@
.ie8 #editor_container div{
#editor_container div{
/* override core style.css font-size:100% that messes up the editor in IE8 */
font-size: 12px !important;
font-size: 13px !important;
}
#editor_container #editor {
position: relative;
display: block;
#editor_controls {
background-color: white;
height: 44px;
position: absolute;
top: 0;
left: 0;
z-index: 20;
width: 100%;
z-index: 1000;
}
.ace-clouds .ace_marker-layer .ace_active-line {
background-color: #f8f8f8 !important;
}
#editor div.ace_gutter {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
opacity: .2;
background-color: transparent;
}
#editor {
display: block;
height: 100%;
width: 100%;
overflow: hidden;
}
#editor_wrap {
box-sizing: border-box;
padding-top: 46px;
position: absolute;
top: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
#editor_container{
position: absolute;
display: block;
top: 0;
left: 0;
padding-top: 44px;
height: 100%;
width: 100%;
-moz-box-sizing: border-box;
position: fixed;
top: 8%;
left: 15%;
width: 70%;
height: 88%;
z-index: 9999;
overflow: hidden;
background-color: #fff;
border-radius: 3px;
box-sizing: border-box;
}
#editor_save {
margin-left: 7px;
margin-right: 20px;
float: left;
#editor_container.hasPreview {
z-index: 1002;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: none;
border-radius: 0;
}
#editor_container.hasPreview #preview, #editor_container.hasPreview #preview_wrap {
display:block;
}
#editor_container.hasPreview #editor {
width: 50%;
}
#editor_container.hasPreview #editor_controls {
top: 45px;
}
#editor_container.hasPreview #editor_wrap {
padding-top: 91px; /** main navigation + controls */
}
#preview_wrap {
box-sizing: border-box;
height: 100%;
width: 50%;
right: 0;
position: absolute;
top: 0;
display: none;
padding-top: 91px; /** main navigation + controls */
}
#preview {
overflow: auto;
box-sizing: border-box;
padding: 15px;
display: none;
height: 100%;
width:100%;
color: #252525;
background-color: #f9f9f9;
}
@media(max-width: 768px) {
#editor_container {
width: 100%;
height: 100%;
top: 0;
left: 0;
border: none;
border-radius: 0;
}
#editor_controls {
top: 45px;
}
#editor_wrap {
padding-top: 91px; /** main navigation + controls */
}
#editor_container.hasPreview #preview {
display: none;
}
#editor_container.hasPreview #editor {
width: 100%;
}
}
#editor_close {
float: right;
margin-right: 9px;
width: 34px;
border: none;
background-color: transparent;
padding: 22px !important;
margin: 0 !important;
border-radius: 0;
right: 0;
opacity: 0.3;
}
#editor_close:hover {
background-color: #ddd;
}
#editorcontrols > button {
height: 30px !important;
margin-top: 7px;
}
#editor_controls small.filename {
float: left;
color: #555;
font-family: inherit;
font-size: 100%;
font-weight: bolder;
text-overflow: ellipsis;
padding: 12px 0 0 14px;
max-width: 218px;
overflow: hidden;
white-space: nowrap;
}
#editor_controls small.saving-message {
color: #ccc;
font-family: inherit;
font-size: 90%;
padding: 13px 0 13px 17px;
overflow: hidden;
display: inline-block;
padding: 13px 0 0 17px;
}
#editor_overlay{
position: fixed;
top: 0%;
left: 0%;
width: 100%;
height: 100%;
background-color: black;
z-index:1001;
-moz-opacity: 0.7;
opacity:.70;
filter: alpha(opacity=70);
}
small.unsaved-star {
position:absolute;
padding: 9px 0 0 4px;
display: inline-block;
float: left;
}

Просмотреть файл

@ -1,442 +1,555 @@
function getFileExtension(file) {
var parts = file.split('.');
return parts[parts.length - 1];
}
/**
* ownCloud - Files_Texteditor
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Tom Needham <tom@owncloud.com>
* @copyright Tom Needham 2015
*/
function setSyntaxMode(ext) {
// Loads the syntax mode files and tells the editor
var filetype = new Array();
// add file extensions like this: filetype["extension"] = "filetype":
filetype["h"] = "c_cpp";
filetype["c"] = "c_cpp";
filetype["clj"] = "clojure";
filetype["coffee"] = "coffee"; // coffescript can be compiled to javascript
filetype["coldfusion"] = "cfc";
filetype["cnf"] = "text";
filetype["conf"] = "text";
filetype["cpp"] = "c_cpp";
filetype["cs"] = "csharp";
filetype["css"] = "css";
filetype["groovy"] = "groovy";
filetype["haxe"] = "hx";
filetype["htm"] = "html";
filetype["html"] = "html";
filetype["tt"] = "html";
filetype["java"] = "java";
filetype["js"] = "javascript";
filetype["jsm"] = "javascript";
filetype["json"] = "json";
filetype["latex"] = "latex";
filetype["tex"] = "latex";
filetype["less"] = "less";
filetype["ly"] = "latex";
filetype["ily"] = "latex";
filetype["lua"] = "lua";
filetype["markdown"] = "markdown";
filetype["md"] = "markdown";
filetype["mdown"] = "markdown";
filetype["mdwn"] = "markdown";
filetype["mkd"] = "markdown";
filetype["ml"] = "ocaml";
filetype["mli"] = "ocaml";
filetype["pl"] = "perl";
filetype["php"] = "php";
filetype["powershell"] = "ps1";
filetype["py"] = "python";
filetype["rb"] = "ruby";
filetype["scad"] = "scad"; // seems to be something like 3d model files printed with e.g. reprap
filetype["scala"] = "scala";
filetype["scss"] = "scss"; // "sassy css"
filetype["sh"] = "sh";
filetype["sql"] = "sql";
filetype["svg"] = "svg";
filetype["textile"] = "textile"; // related to markdown
filetype["xml"] = "xml";
filetype["yaml"] = "yaml";
filetype["yml"] = "yaml";
var Files_Texteditor = {
if (filetype[ext] != null) {
// Then it must be in the array, so load the custom syntax mode
// Set the syntax mode
OC.addScript('files_texteditor', 'vendor/ace/src-noconflict/mode-' + filetype[ext], function () {
var SyntaxMode = ace.require("ace/mode/" + filetype[ext]).Mode;
window.aceEditor.getSession().setMode(new SyntaxMode());
});
}
}
/**
* Holds the editor container
*/
$container: null,
function showControls(dir, filename, writeable) {
// Loads the control bar at the top.
OC.Breadcrumb.show(dir, filename, '#');
// Load the new toolbar.
var editorbarhtml = '<div id="editorcontrols" style="display: none;">';
if (writeable) {
editorbarhtml += '<button id="editor_save">' + t('files_texteditor', 'Save') + '</button>';
}
editorbarhtml += '<label for="editorseachval">' + t('files_texteditor', 'Search');
editorbarhtml += '</label><input type="text" name="editorsearchval" id="editorsearchval">';
editorbarhtml += '<button id="editor_close" class="icon-close svg"></button>';
editorbarhtml += '</div>';
/**
* Holds the editor element ID
*/
editor: 'editor',
$('#controls').append(editorbarhtml);
$('#editorcontrols').show();
if (!OC.Util.hasSVGSupport()) {
OC.Util.replaceSVG($('#editorcontrols'));
}
}
/**
* Stores info on the file being edited
*/
file: {
edited: false,
mtime: null,
dir: null,
name: null,
writeable: null,
mime: null
},
function bindControlEvents() {
$('#app-content').on('click', '#editor_save', doFileSave);
$('#app-content').on('click', '#editor_close', hideFileEditor);
$('#app-content').on('keyup', '#editorsearchval', doSearch);
$('#app-content').on('click', '#clearsearchbtn', resetSearch);
$('#app-content').on('click', '#nextsearchbtn', nextSearchResult);
}
/**
* Stored the saving state
*/
saving: false,
// returns true or false if the editor is in view or not
function editorIsShown() {
return is_editor_shown;
}
/**
* Current files app context
*/
currentContext: null,
//resets the search
function resetSearch() {
$('#editorsearchval').val('');
$('#nextsearchbtn').remove();
$('#clearsearchbtn').remove();
window.aceEditor.gotoLine(0);
}
/**
* Stores the autosave timer
*/
saveTimer: null,
// moves the cursor to the next search resukt
function nextSearchResult() {
window.aceEditor.findNext();
}
// Performs the initial search
function doSearch() {
// check if search box empty?
if ($('#editorsearchval').val() == '') {
// Hide clear button
window.aceEditor.gotoLine(0);
$('#nextsearchbtn').remove();
$('#clearsearchbtn').remove();
} else {
// New search
// Reset cursor
window.aceEditor.gotoLine(0);
// Do search
window.aceEditor.find($('#editorsearchval').val(), {
backwards: false,
wrap: false,
caseSensitive: false,
wholeWord: false,
regExp: false
});
// Show next and clear buttons
// check if already there
if ($('#nextsearchbtn').length == 0) {
var nextbtnhtml = '<button id="nextsearchbtn">' + t('files_texteditor', 'Next') + '</button>';
var clearbtnhtml = '<button id="clearsearchbtn">' + t('files_texteditor', 'Clear') + '</button>';
$('#editorsearchval').after(nextbtnhtml).after(clearbtnhtml);
/**
* preview plugins by mimetype
*/
previewPlugins: {},
registerPreviewPlugin: function(mimeType, plugin) {
this.previewPlugins[mimeType] = plugin;
},
/**
* preview element
*/
preview: null,
previewPluginOnChange: null,
/**
* Save handler, triggered by the button, or keyboard
*/
_onSaveTrigger: function() {
// Don't save if not edited
if(!OCA.Files_Texteditor.file.edited) {
return;
}
// Don't try to save twice
if(OCA.Files_Texteditor.saving) {
return;
} else {
OCA.Files_Texteditor.saving = true;
OCA.Files_Texteditor.edited = false;
}
}
}
// Tries to save the file.
function doFileSave() {
if (editorIsShown()) {
// Changed contents?
if ($('#editor').attr('data-edited') == 'true') {
$('#editor').attr('data-edited', 'false');
$('#editor').attr('data-saving', 'true');
// Get file path
var path = $('#editor').attr('data-dir') + '/' + $('#editor').attr('data-filename');
// Get original mtime
var mtime = $('#editor').attr('data-mtime');
// Show saving spinner
$("#editor_save").die('click', doFileSave);
$('#save_result').remove();
$('#editor_save').text(t('files_texteditor', 'Saving...'));
// Get the data
var filecontents = window.aceEditor.getSession().getValue();
// Send the data
$.ajax({
type: 'PUT',
url: OC.generateUrl('/apps/files_texteditor/ajax/savefile'),
data: {
filecontents: filecontents,
path: path,
mtime: mtime
}
}).done(function(data) {
// Save OK
// Update mtime
$('#editor').attr('data-mtime', data.mtime);
$('#editor_save').text(t('files_texteditor', 'Save'));
// Update titles
if($('#editor').attr('data-edited') != 'true') {
$('.crumb.last a').text($('#editor').attr('data-filename'));
document.title = $('#editor').attr('data-filename') + ' - ownCloud';
}
$('#editor').attr('data-saving', 'false');
$('#editor_save').live('click', doFileSave);
}
).fail(function(jqXHR) {
$('#editor_save').text(t('files_texteditor', 'Save'));
$('#notification').text(JSON.parse(jqXHR.responseText).message);
$('#notification').fadeIn();
$('#editor').attr('data-edited', 'true');
$('#editor').attr('data-saving', 'false');
$('#editor_save').live('click', doFileSave);
// Set the saving status
$('#editor_controls small.saving-message')
.text(t('files_texteditor', 'saving...'))
.show();
// Send to server
OCA.Files_Texteditor.saveFile(
window.aceEditor.getSession().getValue(),
OCA.Files_Texteditor.file,
function(data){
newmtime = data.mtime;
// Yay
// TODO only reset edited value if not editing during saving
document.title = document.title.slice(2);
$('small.unsaved-star').css('display', 'none');
OCA.Files_Texteditor.file.mtime = newmtime;
OCA.Files_Texteditor.file.edited = false;
$('#editor_controls small.saving-message')
.text(t('files_texteditor', 'saved!'));
setTimeout(function() {
$('small.saving-message').fadeOut(200);
}, 2000);
},
function(message){
// Boo
$('small.saving-message').text(t('files_texteditor', 'failed!'));
setTimeout(function() {
$('small.saving-message').fadeOut(200);
}, 5000);
OCA.Files_Texteditor.edited = true;
}
);
OCA.Files_Texteditor.saving = false;
window.aceEditor.focus();
},
/**
* Handles on close button click
*/
_onCloseTrigger: function() {
// Hide or close?
if(!OCA.Files_Texteditor.file.edited) {
OCA.Files_Texteditor.closeEditor();
} else {
// Trick the autosave attempt into thinking we have no changes
OCA.Files_Texteditor.file.edited = false;
// Hide the editor
OCA.Files_Texteditor.hideEditor();
// Try to save
OCA.Files_Texteditor.saveFile(
window.aceEditor.getSession().getValue(),
OCA.Files_Texteditor.file,
function(data){
OC.Notification.showTemporary(t(
'files_texteditor',
'Saved'
)
);
// Remove the editor
OCA.Files_Texteditor.closeEditor();
},
function(message){
OC.Notification.showTemporary(t(
'files_texteditor',
'There was a problem saving your changes. Click to resume editing.'
)
);
$('#notification').data('reopeneditor', true).on(
'click',
OCA.Files_Texteditor._onReOpenTrigger
);
OCA.Files_Texteditor.file.edited = true;
}
);
}
}
giveEditorFocus();
};
},
// Gives the editor focus
function giveEditorFocus() {
window.aceEditor.focus();
};
// Loads the file editor. Accepts two parameters, dir and filename.
function showFileEditor(dir, filename) {
// Check if unsupported file format
if(FileActions.currentFile && FileActions.getCurrentMimeType() === 'text/rtf') {
// Download the file instead.
window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val());
} else {
if (!editorIsShown()) {
is_editor_shown = true;
// Delete any old editors
if ($('#notification').data('reopeneditor')) {
OC.Notification.hide();
}
$('#editor').remove();
// Loads the file editor and display it.
$('#app-content').append('<div id="editor_container"><div id="editor"></div></div>');
// bigger text for better readability
document.getElementById('editor').style.fontSize = '16px';
$.get(
OC.generateUrl('/apps/files_texteditor/ajax/loadfile'),
{
filename: filename,
dir: dir
}
).done(function (data) {
// Save mtime
$('#editor').attr('data-mtime', data.mtime);
$('#editor').attr('data-saving', 'false');
// Initialise the editor
if (window.FileList){
FileList.setViewerMode(true);
enableEditorUnsavedWarning(true);
$('#fileList').on('changeDirectory.texteditor', textEditorOnChangeDirectory);
}
// Show the control bar
showControls(dir, filename, data.writeable);
// Update document title
$('body').attr('old_title', document.title);
document.title = filename + ' - ownCloud';
$('#editor').text(data.filecontents);
$('#editor').attr('data-dir', dir);
$('#editor').attr('data-filename', filename);
$('#editor').attr('data-edited', 'false');
window.aceEditor = ace.edit("editor");
aceEditor.setShowPrintMargin(false);
aceEditor.getSession().setUseWrapMode(true);
if ( ! data.writeable ) {
aceEditor.setReadOnly(true);
}
if (data.mime && data.mime === 'text/html') {
setSyntaxMode('html');
} else {
setSyntaxMode(getFileExtension(filename));
}
OC.addScript('files_texteditor', 'vendor/ace/src-noconflict/theme-clouds', function () {
window.aceEditor.setTheme("ace/theme/clouds");
});
window.aceEditor.getSession().on('change', function () {
if ($('#editor').attr('data-edited') != 'true') {
$('#editor').attr('data-edited', 'true');
if($('#editor').attr('data-saving') != 'true') {
$('.crumb.last a').text($('.crumb.last a').text() + ' *');
document.title = $('#editor').attr('data-filename') + ' * - ownCloud';
}
}
});
// Add the ctrl+s event
window.aceEditor.commands.addCommand({
name: "save",
bindKey: {
win: "Ctrl-S",
mac: "Command-S",
sender: "editor"
},
exec: function () {
if($('#editor').attr('data-saving') == 'false'){
doFileSave();
}
}
});
giveEditorFocus();
}
).fail(function(jqXHR) {
// Failed to get the file.
OC.dialogs.alert(JSON.parse(jqXHR.responseText).message, t('files_texteditor', 'An error occurred!'));
hideFileEditor();
}
);
/**
* Handles the trigger or re open editor
*/
_onReOpenTrigger: function() {
if($('#notification').data('reopeneditor') == true) {
document.title = OCA.Files_Texteditor.file.name + ' - ' + document.title;
OCA.Files_Texteditor.$container.show();
}
}
}
},
function enableEditorUnsavedWarning(enable) {
$(window).unbind('beforeunload.texteditor');
if (enable) {
$(window).bind('beforeunload.texteditor', function () {
if ($('#editor').attr('data-edited') == 'true') {
return t('files_texteditor', 'There are unsaved changes in the text editor');
/**
* Handles the FileAction click event
*/
_onEditorTrigger: function(filename, context) {
OCA.Files_Texteditor.currentContext = context;
OCA.Files_Texteditor.file.name = filename;
OCA.Files_Texteditor.file.dir = context.dir;
OCA.Files_Texteditor.loadEditor(
OCA.Files_Texteditor.$container,
OCA.Files_Texteditor.file
);
if(!$('html').hasClass('ie8')) {
history.pushState({file:filename, dir:context.dir}, 'Editor', '#editor');
}
},
/**
* Handler for edits detected
*/
_onEdit: function () {
if(!OCA.Files_Texteditor.file.edited) {
OCA.Files_Texteditor.file.edited = true;
if(!OCA.Files_Texteditor.saving) {
OCA.Files_Texteditor._onUnsaved();
}
});
}
}
}
if (this.previewPluginOnChange) {
var text = window.aceEditor.getSession().getValue();
this.previewPluginOnChange(text, this.preview);
}
},
function textEditorOnChangeDirectory(ev){
// if the directory is changed, it is usually due to browser back
// navigation. In this case, simply close the editor
hideFileEditor();
}
/**
* Handler when unsaved work is detected
*/
_onUnsaved: function() {
document.title = '* '+document.title;
$('small.unsaved-star').css('display', 'inline-block');
},
// Fades out the editor.
function hideFileEditor() {
$('#fileList').off('changeDirectory.texteditor');
enableEditorUnsavedWarning(false);
if (window.FileList){
// reload the directory content with the updated file size + thumbnail
// and also the breadcrumb
window.FileList.reload();
}
var $editorWasEdited = $('#editor').attr('data-edited') == 'true';
/**
* Setup on page load
*/
initialize: function(container) {
// Don't load if not in the files app TODO: Fix for sharing
if(!$('#content.app-files').length) { return; }
this.$container = container;
this.registerFileActions();
this.oldTitle = document.title;
$.each(this.previewPlugins, function(mime, plugin) {
plugin.init();
});
},
// Fade out editor
if ($editorWasEdited) {
// Hide, not remove
$('#editorcontrols,#editor_container').hide();
} else {
$('#editor_container, #editorcontrols').remove();
}
/**
* Registers the file actions
*/
registerFileActions: function() {
var mimes = Array(
'text',
'application/xml',
'application/x-empty',
'application/x-php',
'application/javascript',
'application/x-pearl',
'application/x-text',
'application/yaml'
);
// Reset document title
if (typeof $('body').attr('old_title') !== 'undefined') {
document.title = $('body').attr('old_title');
}
FileList.setViewerMode(false);
$('#content table').show();
_self = this;
if ($editorWasEdited) {
OC.Notification.showTemporary(t('files_texteditor', 'There were unsaved changes, click here to go back'));
$('#notification').data('reopeneditor', true);
}
is_editor_shown = false;
}
// Reopens the last document
function reopenEditor() {
FileList.setViewerMode(true);
enableEditorUnsavedWarning(true);
$('#fileList').on('changeDirectory.texteditor', textEditorOnChangeDirectory);
$('#controls .last').not('#breadcrumb_file').removeClass('last');
$('#editor_container').show();
$('#editorcontrols').show();
OC.Breadcrumb.show($('#editor').attr('data-dir'), $('#editor').attr('data-filename') + ' *', '#');
document.title = $('#editor').attr('data-filename') + ' * - ownCloud';
is_editor_shown = true;
giveEditorFocus();
}
var is_editor_shown = false;
$(document).ready(function () {
if ($('#isPublic').val()){
// disable editor in public mode (not supported yet)
return;
}
if (typeof FileActions !== 'undefined') {
FileActions.register('text', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('text', 'Edit');
FileActions.register('application/xml', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('application/xml', 'Edit');
FileActions.register('application/x-empty', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('application/x-empty', 'Edit');
FileActions.register('inode/x-empty', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('inode/x-empty', 'Edit');
FileActions.register('application/x-php', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('application/x-php', 'Edit');
FileActions.register('application/javascript', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('application/javascript', 'Edit');
FileActions.register('application/x-perl', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('application/x-perl', 'Edit');
FileActions.register('application/x-tex', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('application/x-tex', 'Edit');
FileActions.register('application/yaml', 'Edit', OC.PERMISSION_READ, '', function (filename) {
showFileEditor($('#dir').val(), filename);
});
FileActions.setDefault('application/yaml', 'Edit');
}
//legacy search result customization
OC.search.customResults.Text = function (row, item) {
var text = item.link.substr(item.link.indexOf('download') + 8);
var a = row.find('td.result a');
a.data('file', text);
a.attr('href', '#');
a.click(function () {
text = decodeURIComponent(text);
var pos = text.lastIndexOf('/');
var file = text.substr(pos + 1);
var dir = text.substr(0, pos);
showFileEditor(dir, file);
});
};
// customize file results when we can edit them
OC.search.customResults.file = function (row, item) {
var validFile = /(text\/*|application\/xml)/;
if (validFile.test(item.mime_type)) {
var a = row.find('td.result a');
a.data('file', item.name);
a.attr('href', '#');
a.click(function () {
showFileEditor(OC.dirname(item.path), item.name);
$.each(mimes, function(key, value) {
OCA.Files.fileActions.registerAction({
name: 'Edit',
mime: value,
actionHandler: _self._onEditorTrigger,
permissions: OC.PERMISSION_READ
});
OCA.Files.fileActions.setDefault(value, 'Edit');
});
},
/**
* Actually fire up the editor in a container
*/
loadEditor: function(container, file) {
var _self = this;
// Insert the editor into the container
container.html(
'<div id="editor_overlay"></div>'
+'<div id="editor_container" class="icon-loading">'
+'<div id="editor_wrap"><div id="editor"></div>'
+'<div id="preview_wrap"><div id="preview"></div></div></div></div>');
$('#app-content').append(container);
// Get the file data
this.loadFile(
file.dir,
file.name,
function(file, data){
// Success!
// Sort the title
document.title = file.name + ' - ' + document.title;
// Load ace
$('#'+_self.editor).text(data);
// Configure ace
_self.configureACE(file);
// Show the controls
_self.loadControlBar(file, _self.currentContext);
window.aceEditor.getSession().on('change', _self.setupAutosave);
window.aceEditor.focus();
if (_self.previewPlugins[file.mime]){
_self.preview = container.find('#preview');
_self.preview.addClass(file.mime.replace('/','-'));
container.find('#editor_container').addClass('hasPreview');
_self.previewPluginOnChange = _.debounce(_self.previewPlugins[file.mime].preview, 200);
var text = window.aceEditor.getSession().getValue();
_self.previewPluginOnChange(text, _self.preview);
window.aceEditor.resize();
} else {
_self.previewPluginOnChange = null;
}
// IE8 support
if(!OC.Util.hasSVGSupport()){ //replace all svg images with png images for browser that dont support svg
OC.Util.replaceSVG();
}
},
function(message){
// Oh dear
OC.dialogs.alert(message, t('files_texteditor', 'An error occurred!'));
});
},
/**
* Load the editor control bar
*/
loadControlBar: function(file, context) {
var html =
'<small class="filename">'+escapeHTML(file.name)+'</small>'
+'<small class="unsaved-star" style="display: none">*</small>'
+'<small class="saving-message">'
+'</small>'
+'<button id="editor_close" class="icon-close svg"></button>';
var controlBar = $('<div id="editor_controls"></div>').html(html);
$('#editor_wrap').before(controlBar);
this.setFilenameMaxLength();
this.bindControlBar();
},
/**
* Removes the control bar
*/
unloadControlBar: function() {
$('#editor_controls').remove();
},
/**
* Set the max width of the filename to prevent wrapping
*/
setFilenameMaxLength: function() {
// Get the width of the control bar
var controlBar = $('#editor_controls').width();
// Get the width of all of the other controls
var controls = $('small.saving-message').outerWidth(true);
controls += $('small.unsaved-star').outerWidth(true);
controls += $('#editor_close').outerWidth(true);
// Set the max width
$('small.filename').css('max-width', controlBar-controls-28);
},
/**
* Binds the control events on the control bar
*/
bindControlBar: function() {
$('#editor_close').on('click', this._onCloseTrigger);
$(window).resize(OCA.Files_Texteditor.setFilenameMaxLength);
if(!$('html').hasClass('ie8')) {
window.onpopstate = function (e) {
OCA.Files_Texteditor._onCloseTrigger();
}
}
};
// Binds the file save and close editor events, and gotoline button
bindControlEvents();
$('#editor_container').remove();
$('#notification').click(function () {
if ($('#notification').data('reopeneditor')) {
reopenEditor();
OC.Notification.hide();
},
/**
* Configure the ACE editor
*/
configureACE: function(file) {
window.aceEditor = ace.edit(this.editor);
aceEditor.setShowPrintMargin(false);
aceEditor.getSession().setUseWrapMode(true);
if (!file.writeable) { aceEditor.setReadOnly(true); }
if (file.mime && file.mime === 'text/html') {
this.setEditorSyntaxMode('html');
} else {
// Set the syntax mode based on the file extension
this.setEditorSyntaxMode(
file.name.split('.')[file.name.split('.').length - 1]
);
}
});
// Set the theme
OC.addScript(
'files_texteditor',
'vendor/ace/src-noconflict/theme-clouds',
function () {
window.aceEditor.setTheme("ace/theme/clouds");
}
);
// Bind the edit event
window.aceEditor.getSession().on('change', this._onEdit.bind(this));
// Bind save trigger
window.aceEditor.commands.addCommand({
name: "save",
bindKey: {
win: "Ctrl-S",
mac: "Command-S",
sender: "editor"
},
exec: OCA.Files_Texteditor._onSaveTrigger
});
},
/**
* Sets the syntax highlighting for the editor based on the file extension
*/
setEditorSyntaxMode: function(extension) {
// Loads the syntax mode files and tells the editor
var filetype = new Array();
// add file extensions like this: filetype["extension"] = "filetype":
filetype["h"] = "c_cpp";
filetype["c"] = "c_cpp";
filetype["clj"] = "clojure";
filetype["coffee"] = "coffee"; // coffescript can be compiled to javascript
filetype["coldfusion"] = "cfc";
filetype["cpp"] = "c_cpp";
filetype["cs"] = "csharp";
filetype["css"] = "css";
filetype["groovy"] = "groovy";
filetype["haxe"] = "hx";
filetype["htm"] = "html";
filetype["html"] = "html";
filetype["tt"] = "html";
filetype["java"] = "java";
filetype["js"] = "javascript";
filetype["jsm"] = "javascript";
filetype["json"] = "json";
filetype["latex"] = "latex";
filetype["tex"] = "latex";
filetype["less"] = "less";
filetype["ly"] = "latex";
filetype["ily"] = "latex";
filetype["lua"] = "lua";
filetype["markdown"] = "markdown";
filetype["md"] = "markdown";
filetype["mdown"] = "markdown";
filetype["mdwn"] = "markdown";
filetype["mkd"] = "markdown";
filetype["ml"] = "ocaml";
filetype["mli"] = "ocaml";
filetype["pl"] = "perl";
filetype["php"] = "php";
filetype["powershell"] = "ps1";
filetype["py"] = "python";
filetype["rb"] = "ruby";
filetype["scad"] = "scad"; // seems to be something like 3d model files printed with e.g. reprap
filetype["scala"] = "scala";
filetype["scss"] = "scss"; // "sassy css"
filetype["sh"] = "sh";
filetype["sql"] = "sql";
filetype["svg"] = "svg";
filetype["textile"] = "textile"; // related to markdown
filetype["xml"] = "xml";
if (filetype[extension] != null) {
// Then it must be in the array, so load the custom syntax mode
// Set the syntax mode
OC.addScript(
'files_texteditor',
'vendor/ace/src-noconflict/mode-'+filetype[extension],
function () {
var SyntaxMode = ace.require("ace/mode/" + filetype[extension]).Mode;
window.aceEditor.getSession().setMode(new SyntaxMode());
}
);
}
},
/**
* Loads the data through AJAX
*/
loadFile: function(dir, filename, success, failure) {
var _self = this;
var data = $.getJSON(
OC.filePath('files_texteditor', 'ajax', 'loadfile.php'),
{file: filename, dir: dir},
function (result) {
if(result.status === 'success') {
// Call success callback
OCA.Files_Texteditor.file.writeable = result.data.writeable;
OCA.Files_Texteditor.file.mime = result.data.mime;
OCA.Files_Texteditor.file.mtime = result.data.mtime;
success(OCA.Files_Texteditor.file, result.data.filecontents);
} else {
// Call failure callback
failure(result.data.message);
}
});
$.get(
OC.generateUrl('/apps/files_texteditor/ajax/loadfile'),
{
filename: filename,
dir: dir
}
).done(function(data) {
// Call success callback
OCA.Files_Texteditor.file.writeable = data.writeable;
OCA.Files_Texteditor.file.mime = data.mime;
OCA.Files_Texteditor.file.mtime = data.mtime;
success(OCA.Files_Texteditor.file, data.filecontents);
}).fail(function(jqXHR) {
failure(JSON.parse(jqXHR.responseText).message);
});
},
/**
* Send the new file data back to the server
*/
saveFile: function(data, file, success, failure) {
// Send the post request
if(file.dir == '/') {
var path = file.dir + file.name;
} else {
var path = file.dir + '/' + file.name;
}
$.ajax({
type: 'PUT',
url: OC.generateUrl('/apps/files_texteditor/ajax/savefile'),
data: {
filecontents: data,
path: path,
mtime: file.mtime
}
})
.done(success)
.fail(function(jqXHR) {
failure(JSON.parse(jqXHR.responseText).message);
});
},
/**
* Close the editor for good
*/
closeEditor: function() {
this.$container.html('').show();
this.unloadControlBar();
document.title = this.oldTitle;
},
/**
* Hide the editor (unsaved changes)
*/
hideEditor: function() {
this.$container.hide();
document.title = this.oldTitle;
},
/**
* Configure the autosave timer
*/
setupAutosave: function() {
clearTimeout(this.saveTimer);
this.saveTimer = setTimeout(OCA.Files_Texteditor._onSaveTrigger, 3000);
}
}
OCA.Files_Texteditor = Files_Texteditor;
$(document).ready(function () {
$('#editor').remove();
OCA.Files_Texteditor.initialize($('<div id="app-content-texteditor"></div>'));
});