external editor support
This commit is contained in:
@@ -16,50 +16,9 @@ angular.module('app.frontend')
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
|
||||
/**
|
||||
* Insert 4 spaces when a tab key is pressed,
|
||||
* only used when inside of the text editor.
|
||||
* If the shift key is pressed first, this event is
|
||||
* not fired.
|
||||
*/
|
||||
var handleTab = function (event) {
|
||||
if (!event.shiftKey && event.which == 9) {
|
||||
event.preventDefault();
|
||||
var start = this.selectionStart;
|
||||
var end = this.selectionEnd;
|
||||
var spaces = " ";
|
||||
|
||||
// Insert 4 spaces
|
||||
this.value = this.value.substring(0, start)
|
||||
+ spaces + this.value.substring(end);
|
||||
|
||||
// Place cursor 4 spaces away from where
|
||||
// the tab key was pressed
|
||||
this.selectionStart = this.selectionEnd = start + 4;
|
||||
}
|
||||
}
|
||||
|
||||
var handler = function(event) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
switch (String.fromCharCode(event.which).toLowerCase()) {
|
||||
case 's':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
ctrl.saveNote(event);
|
||||
});
|
||||
break;
|
||||
case 'e':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
ctrl.clickedEditNote();
|
||||
})
|
||||
break;
|
||||
case 'm':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
ctrl.toggleMarkdown();
|
||||
})
|
||||
break;
|
||||
case 'o':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
@@ -70,14 +29,6 @@ angular.module('app.frontend')
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handler);
|
||||
var element = document.getElementById("note-text-editor");
|
||||
element.addEventListener('keydown', handleTab);
|
||||
|
||||
scope.$on('$destroy', function(){
|
||||
window.removeEventListener('keydown', handler);
|
||||
})
|
||||
|
||||
scope.$watch('ctrl.note', function(note, oldNote){
|
||||
if(note) {
|
||||
ctrl.setNote(note, oldNote);
|
||||
@@ -88,7 +39,21 @@ angular.module('app.frontend')
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('EditorCtrl', function ($sce, $timeout, authManager, markdownRenderer, $rootScope, extensionManager, syncManager) {
|
||||
.controller('EditorCtrl', function ($sce, $timeout, authManager, markdownRenderer, $rootScope, extensionManager, syncManager, modelManager) {
|
||||
|
||||
window.addEventListener("message", function(){
|
||||
console.log("App received message:", event);
|
||||
if(event.data.status) {
|
||||
this.postNoteToExternalEditor();
|
||||
} else {
|
||||
var id = event.data.id;
|
||||
var text = event.data.text;
|
||||
if(this.note.uuid == id) {
|
||||
this.note.text = text;
|
||||
this.changesMade();
|
||||
}
|
||||
}
|
||||
}.bind(this), false);
|
||||
|
||||
this.setNote = function(note, oldNote) {
|
||||
this.editorMode = 'edit';
|
||||
@@ -96,6 +61,13 @@ angular.module('app.frontend')
|
||||
this.showMenu = false;
|
||||
this.loadTagsString();
|
||||
|
||||
if(note.editorUrl) {
|
||||
this.customEditor = this.editorForUrl(note.editorUrl);
|
||||
this.postNoteToExternalEditor();
|
||||
} else {
|
||||
this.customEditor = null;
|
||||
}
|
||||
|
||||
if(note.safeText().length == 0 && note.dummy) {
|
||||
this.focusTitle(100);
|
||||
}
|
||||
@@ -109,6 +81,28 @@ angular.module('app.frontend')
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedEditor = function(editor) {
|
||||
this.showEditorMenu = false;
|
||||
if(editor.default) {
|
||||
this.customEditor = null;
|
||||
} else {
|
||||
this.customEditor = editor;
|
||||
}
|
||||
this.note.editorUrl = editor.url;
|
||||
}.bind(this)
|
||||
|
||||
this.editorForUrl = function(url) {
|
||||
var editors = modelManager.itemsForContentType("SN|Editor");
|
||||
return editors.filter(function(editor){return editor.url == url})[0];
|
||||
}
|
||||
|
||||
this.postNoteToExternalEditor = function() {
|
||||
var externalEditorElement = document.getElementById("editor-iframe");
|
||||
if(externalEditorElement) {
|
||||
externalEditorElement.contentWindow.postMessage({text: this.note.text, id: this.note.uuid}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
this.hasAvailableExtensions = function() {
|
||||
return extensionManager.extensionsInContextOfItem(this.note).length > 0;
|
||||
}
|
||||
|
||||
30
app/assets/javascripts/app/frontend/models/app/editor.js
Normal file
30
app/assets/javascripts/app/frontend/models/app/editor.js
Normal file
@@ -0,0 +1,30 @@
|
||||
class Editor extends Item {
|
||||
|
||||
constructor(json_obj) {
|
||||
super(json_obj);
|
||||
}
|
||||
|
||||
mapContentToLocalProperties(contentObject) {
|
||||
super.mapContentToLocalProperties(contentObject)
|
||||
this.url = contentObject.url;
|
||||
this.name = contentObject.name;
|
||||
}
|
||||
|
||||
structureParams() {
|
||||
var params = {
|
||||
url: this.url,
|
||||
name: this.name
|
||||
};
|
||||
|
||||
_.merge(params, super.structureParams());
|
||||
return params;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {uuid: this.uuid}
|
||||
}
|
||||
|
||||
get content_type() {
|
||||
return "SN|Editor";
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class ItemParams {
|
||||
}
|
||||
|
||||
paramsForLocalStorage() {
|
||||
this.additionalFields = ["updated_at", "dirty"];
|
||||
this.additionalFields = ["updated_at", "dirty", "editorUrl"];
|
||||
this.forExportFile = true;
|
||||
return this.__params();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
class EditorMenu {
|
||||
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "frontend/directives/editor-menu.html";
|
||||
this.scope = {
|
||||
callback: "&",
|
||||
selectedEditor: "="
|
||||
};
|
||||
}
|
||||
|
||||
controller($scope, modelManager, extensionManager, syncManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {};
|
||||
|
||||
let editorContentType = "SN|Editor";
|
||||
|
||||
let defaultEditor = {
|
||||
default: true,
|
||||
name: "Plain"
|
||||
}
|
||||
|
||||
$scope.sysEditors = [defaultEditor];
|
||||
$scope.editors = modelManager.itemsForContentType(editorContentType);
|
||||
|
||||
$scope.editorForUrl = function(url) {
|
||||
return $scope.editors.filter(function(editor){return editor.url == url})[0];
|
||||
}
|
||||
|
||||
$scope.selectEditor = function(editor) {
|
||||
$scope.callback()(editor);
|
||||
}
|
||||
|
||||
$scope.deleteEditor = function(editor) {
|
||||
if(confirm("Are you sure you want to delete this editor?")) {
|
||||
modelManager.setItemToBeDeleted(editor);
|
||||
syncManager.sync();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.submitNewEditorRequest = function() {
|
||||
var editor = createEditor($scope.formData.url);
|
||||
modelManager.addItem(editor);
|
||||
editor.setDirty(true);
|
||||
syncManager.sync();
|
||||
$scope.editors.push(editor);
|
||||
$scope.formData = {};
|
||||
}
|
||||
|
||||
function createEditor(url) {
|
||||
var name = getParameterByName("name", url);
|
||||
return modelManager.createItem({
|
||||
content_type: editorContentType,
|
||||
url: url,
|
||||
name: name
|
||||
})
|
||||
}
|
||||
|
||||
function getParameterByName(name, url) {
|
||||
name = name.replace(/[\[\]]/g, "\\$&");
|
||||
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
|
||||
results = regex.exec(url);
|
||||
if (!results) return null;
|
||||
if (!results[2]) return '';
|
||||
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app.frontend').directive('editorMenu', () => new EditorMenu);
|
||||
@@ -1,4 +1,3 @@
|
||||
// Start from filter
|
||||
angular.module('app.frontend').filter('startFrom', function() {
|
||||
return function(input, start) {
|
||||
return input.slice(start);
|
||||
|
||||
5
app/assets/javascripts/app/services/filters/trusted.js
Normal file
5
app/assets/javascripts/app/services/filters/trusted.js
Normal file
@@ -0,0 +1,5 @@
|
||||
angular.module('app.frontend').filter('trusted', ['$sce', function ($sce) {
|
||||
return function(url) {
|
||||
return $sce.trustAsResourceUrl(url);
|
||||
};
|
||||
}]);
|
||||
@@ -8,7 +8,7 @@ class ModelManager {
|
||||
this.itemChangeObservers = [];
|
||||
this.items = [];
|
||||
this._extensions = [];
|
||||
this.acceptableContentTypes = ["Note", "Tag", "Extension"];
|
||||
this.acceptableContentTypes = ["Note", "Tag", "Extension", "SN|Editor"];
|
||||
}
|
||||
|
||||
get allItems() {
|
||||
@@ -121,6 +121,8 @@ class ModelManager {
|
||||
item = new Tag(json_obj);
|
||||
} else if(json_obj.content_type == "Extension") {
|
||||
item = new Extension(json_obj);
|
||||
} else if(json_obj.content_type == "SN|Editor") {
|
||||
item = new Editor(json_obj);
|
||||
} else {
|
||||
item = new Item(json_obj);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user