Cleaned folder heirarchy
This commit is contained in:
594
app/assets/javascripts/app/controllers/editor.js
Normal file
594
app/assets/javascripts/app/controllers/editor.js
Normal file
@@ -0,0 +1,594 @@
|
||||
angular.module('app')
|
||||
.directive("editorSection", function($timeout, $sce){
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
save: "&",
|
||||
remove: "&",
|
||||
note: "=",
|
||||
updateTags: "&"
|
||||
},
|
||||
templateUrl: 'editor.html',
|
||||
replace: true,
|
||||
controller: 'EditorCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.note', function(note, oldNote){
|
||||
if(note) {
|
||||
ctrl.noteDidChange(note, oldNote);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, actionsManager, syncManager, modelManager, themeManager, componentManager, storageManager) {
|
||||
|
||||
this.componentManager = componentManager;
|
||||
this.componentStack = [];
|
||||
|
||||
$rootScope.$on("sync:taking-too-long", function(){
|
||||
this.syncTakingTooLong = true;
|
||||
}.bind(this));
|
||||
|
||||
$rootScope.$on("sync:completed", function(){
|
||||
this.syncTakingTooLong = false;
|
||||
}.bind(this));
|
||||
|
||||
$rootScope.$on("tag-changed", function(){
|
||||
this.loadTagsString();
|
||||
}.bind(this));
|
||||
|
||||
this.noteDidChange = function(note, oldNote) {
|
||||
this.setNote(note, oldNote);
|
||||
this.reloadComponentContext();
|
||||
}
|
||||
|
||||
this.setNote = function(note, oldNote) {
|
||||
this.showExtensions = false;
|
||||
this.showMenu = false;
|
||||
this.loadTagsString();
|
||||
|
||||
let onReady = () => {
|
||||
this.noteReady = true;
|
||||
$timeout(() => {
|
||||
this.loadPreferences();
|
||||
})
|
||||
}
|
||||
|
||||
let associatedEditor = this.editorForNote(note);
|
||||
if(associatedEditor && associatedEditor != this.selectedEditor) {
|
||||
// setting note to not ready will remove the editor from view in a flash,
|
||||
// so we only want to do this if switching between external editors
|
||||
this.noteReady = false;
|
||||
// switch after timeout, so that note data isnt posted to current editor
|
||||
$timeout(() => {
|
||||
this.selectedEditor = associatedEditor;
|
||||
onReady();
|
||||
})
|
||||
} else if(associatedEditor) {
|
||||
// Same editor as currently active
|
||||
onReady();
|
||||
} else {
|
||||
// No editor
|
||||
this.selectedEditor = null;
|
||||
onReady();
|
||||
}
|
||||
|
||||
if(note.safeText().length == 0 && note.dummy) {
|
||||
this.focusTitle(100);
|
||||
}
|
||||
|
||||
if(oldNote && oldNote != note) {
|
||||
if(oldNote.hasChanges) {
|
||||
this.save()(oldNote, null);
|
||||
} else if(oldNote.dummy) {
|
||||
this.remove()(oldNote);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.editorForNote = function(note) {
|
||||
let editors = componentManager.componentsForArea("editor-editor");
|
||||
for(var editor of editors) {
|
||||
if(editor.isActiveForItem(note)) {
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
|
||||
// No editor found for note. Use default editor, if note does not prefer system editor
|
||||
if(!note.getAppDataItem("prefersPlainEditor")) {
|
||||
return editors.filter((e) => {return e.isDefaultEditor()})[0];
|
||||
}
|
||||
}
|
||||
|
||||
this.onEditorMenuClick = function() {
|
||||
// App bar menu item click
|
||||
this.showEditorMenu = !this.showEditorMenu;
|
||||
this.showMenu = false;
|
||||
this.showExtensions = false;
|
||||
}
|
||||
|
||||
this.editorMenuOnSelect = function(component) {
|
||||
if(!component || component.area == "editor-editor") {
|
||||
// if plain editor or other editor
|
||||
this.showEditorMenu = false;
|
||||
var editor = component;
|
||||
if(this.selectedEditor && editor !== this.selectedEditor) {
|
||||
this.disassociateComponentWithCurrentNote(this.selectedEditor);
|
||||
}
|
||||
if(editor) {
|
||||
if(this.note.getAppDataItem("prefersPlainEditor") == true) {
|
||||
this.note.setAppDataItem("prefersPlainEditor", false);
|
||||
this.note.setDirty(true);
|
||||
}
|
||||
this.associateComponentWithCurrentNote(editor);
|
||||
} else {
|
||||
// Note prefers plain editor
|
||||
if(!this.note.getAppDataItem("prefersPlainEditor")) {
|
||||
this.note.setAppDataItem("prefersPlainEditor", true);
|
||||
this.note.setDirty(true);
|
||||
}
|
||||
$timeout(() => {
|
||||
this.reloadFont();
|
||||
})
|
||||
}
|
||||
|
||||
this.selectedEditor = editor;
|
||||
} else if(component.area == "editor-stack") {
|
||||
// If component stack item
|
||||
this.toggleStackComponentForCurrentItem(component);
|
||||
}
|
||||
|
||||
// Lots of dirtying can happen above, so we'll sync
|
||||
syncManager.sync();
|
||||
}.bind(this)
|
||||
|
||||
this.hasAvailableExtensions = function() {
|
||||
return actionsManager.extensionsInContextOfItem(this.note).length > 0;
|
||||
}
|
||||
|
||||
this.focusEditor = function(delay) {
|
||||
setTimeout(function(){
|
||||
var element = document.getElementById("note-text-editor");
|
||||
if(element) {
|
||||
element.focus();
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
|
||||
this.focusTitle = function(delay) {
|
||||
setTimeout(function(){
|
||||
document.getElementById("note-title-editor").focus();
|
||||
}, delay)
|
||||
}
|
||||
|
||||
this.clickedTextArea = function() {
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
var statusTimeout;
|
||||
|
||||
this.saveNote = function($event) {
|
||||
var note = this.note;
|
||||
note.dummy = false;
|
||||
this.save()(note, function(success){
|
||||
if(success) {
|
||||
if(statusTimeout) $timeout.cancel(statusTimeout);
|
||||
statusTimeout = $timeout(function(){
|
||||
this.saveError = false;
|
||||
this.syncTakingTooLong = false;
|
||||
this.showAllChangesSavedStatus();
|
||||
}.bind(this), 200)
|
||||
} else {
|
||||
if(statusTimeout) $timeout.cancel(statusTimeout);
|
||||
statusTimeout = $timeout(function(){
|
||||
this.saveError = true;
|
||||
this.syncTakingTooLong = false;
|
||||
this.showErrorStatus();
|
||||
}.bind(this), 200)
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.saveTitle = function($event) {
|
||||
$event.target.blur();
|
||||
this.saveNote($event);
|
||||
this.focusEditor();
|
||||
}
|
||||
|
||||
var saveTimeout;
|
||||
this.changesMade = function() {
|
||||
this.note.hasChanges = true;
|
||||
this.note.dummy = false;
|
||||
|
||||
if(saveTimeout) $timeout.cancel(saveTimeout);
|
||||
if(statusTimeout) $timeout.cancel(statusTimeout);
|
||||
saveTimeout = $timeout(function(){
|
||||
this.showSavingStatus();
|
||||
this.saveNote();
|
||||
}.bind(this), 275)
|
||||
}
|
||||
|
||||
this.showSavingStatus = function() {
|
||||
this.noteStatus = $sce.trustAsHtml("Saving...");
|
||||
}
|
||||
|
||||
this.showAllChangesSavedStatus = function() {
|
||||
var status = "All changes saved";
|
||||
if(authManager.offline()) {
|
||||
status += " (offline)";
|
||||
}
|
||||
this.noteStatus = $sce.trustAsHtml(status);
|
||||
}
|
||||
|
||||
this.showErrorStatus = function() {
|
||||
this.noteStatus = $sce.trustAsHtml("Error syncing<br>(changes saved offline)")
|
||||
}
|
||||
|
||||
this.contentChanged = function() {
|
||||
this.changesMade();
|
||||
}
|
||||
|
||||
this.nameChanged = function() {
|
||||
this.changesMade();
|
||||
}
|
||||
|
||||
this.onNameFocus = function() {
|
||||
this.editingName = true;
|
||||
}
|
||||
|
||||
this.onContentFocus = function() {
|
||||
$rootScope.$broadcast("editorFocused");
|
||||
}
|
||||
|
||||
this.onNameBlur = function() {
|
||||
this.editingName = false;
|
||||
}
|
||||
|
||||
this.toggleFullScreen = function() {
|
||||
this.fullscreen = !this.fullscreen;
|
||||
if(this.fullscreen) {
|
||||
this.focusEditor(0);
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedMenuItem = function($event) {
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
this.deleteNote = function() {
|
||||
let title = this.note.safeTitle().length ? `'${this.note.title}'` : "this note";
|
||||
if(confirm(`Are you sure you want to delete ${title}?`)) {
|
||||
this.remove()(this.note);
|
||||
this.showMenu = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.togglePin = function() {
|
||||
this.note.setAppDataItem("pinned", !this.note.pinned);
|
||||
this.note.setDirty(true);
|
||||
this.changesMade();
|
||||
}
|
||||
|
||||
this.toggleArchiveNote = function() {
|
||||
this.note.setAppDataItem("archived", !this.note.archived);
|
||||
this.note.setDirty(true);
|
||||
this.changesMade();
|
||||
$rootScope.$broadcast("noteArchived");
|
||||
}
|
||||
|
||||
this.clickedEditNote = function() {
|
||||
this.focusEditor(100);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Tags
|
||||
*/
|
||||
|
||||
this.loadTagsString = function() {
|
||||
var string = "";
|
||||
for(var tag of this.note.tags) {
|
||||
string += "#" + tag.title + " ";
|
||||
}
|
||||
this.tagsString = string;
|
||||
}
|
||||
|
||||
this.addTag = function(tag) {
|
||||
var tags = this.note.tags;
|
||||
var strings = tags.map(function(_tag){
|
||||
return _tag.title;
|
||||
})
|
||||
strings.push(tag.title);
|
||||
this.updateTags()(this.note, strings);
|
||||
this.loadTagsString();
|
||||
}
|
||||
|
||||
this.removeTag = function(tag) {
|
||||
var tags = this.note.tags;
|
||||
var strings = tags.map(function(_tag){
|
||||
return _tag.title;
|
||||
}).filter(function(_tag){
|
||||
return _tag !== tag.title;
|
||||
})
|
||||
this.updateTags()(this.note, strings);
|
||||
this.loadTagsString();
|
||||
}
|
||||
|
||||
this.updateTagsFromTagsString = function() {
|
||||
var tags = this.tagsString.split("#");
|
||||
tags = _.filter(tags, function(tag){
|
||||
return tag.length > 0;
|
||||
})
|
||||
tags = _.map(tags, function(tag){
|
||||
return tag.trim();
|
||||
})
|
||||
|
||||
this.note.dummy = false;
|
||||
this.updateTags()(this.note, tags);
|
||||
}
|
||||
|
||||
|
||||
/* Resizability */
|
||||
|
||||
this.resizeControl = {};
|
||||
|
||||
this.onPanelResizeFinish = function(width, left, isMaxWidth) {
|
||||
if(isMaxWidth) {
|
||||
authManager.setUserPrefValue("editorWidth", null);
|
||||
} else {
|
||||
if(width !== undefined && width !== null) {
|
||||
authManager.setUserPrefValue("editorWidth", width);
|
||||
}
|
||||
}
|
||||
|
||||
if(left !== undefined && left !== null) {
|
||||
authManager.setUserPrefValue("editorLeft", left);
|
||||
}
|
||||
authManager.syncUserPreferences();
|
||||
}
|
||||
|
||||
$rootScope.$on("user-preferences-changed", () => {
|
||||
this.loadPreferences();
|
||||
});
|
||||
|
||||
this.loadPreferences = function() {
|
||||
this.monospaceFont = authManager.getUserPrefValue("monospaceFont", "monospace");
|
||||
|
||||
if(!document.getElementById("editor-content")) {
|
||||
// Elements have not yet loaded due to ng-if around wrapper
|
||||
return;
|
||||
}
|
||||
|
||||
this.reloadFont();
|
||||
|
||||
let width = authManager.getUserPrefValue("editorWidth", null);
|
||||
if(width !== null) {
|
||||
this.resizeControl.setWidth(width);
|
||||
}
|
||||
|
||||
let left = authManager.getUserPrefValue("editorLeft", null);
|
||||
if(left !== null) {
|
||||
this.resizeControl.setLeft(left);
|
||||
}
|
||||
}
|
||||
|
||||
this.reloadFont = function() {
|
||||
var editable = document.getElementById("note-text-editor");
|
||||
|
||||
if(!editable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.monospaceFont) {
|
||||
if(isMacApplication()) {
|
||||
editable.style.fontFamily = "Menlo, Consolas, 'DejaVu Sans Mono', monospace";
|
||||
} else {
|
||||
editable.style.fontFamily = "monospace";
|
||||
}
|
||||
} else {
|
||||
editable.style.fontFamily = "inherit";
|
||||
}
|
||||
}
|
||||
|
||||
this.toggleKey = function(key) {
|
||||
this[key] = !this[key];
|
||||
authManager.setUserPrefValue(key, this[key], true);
|
||||
this.reloadFont();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Components
|
||||
*/
|
||||
|
||||
componentManager.registerHandler({identifier: "editor", areas: ["note-tags", "editor-stack", "editor-editor"], activationHandler: function(component){
|
||||
|
||||
if(component.area === "note-tags") {
|
||||
// Autocomplete Tags
|
||||
this.tagsComponent = component.active ? component : null;
|
||||
} else if(component.area == "editor-stack") {
|
||||
// Stack
|
||||
if(component.active) {
|
||||
if(!_.find(this.componentStack, component)) {
|
||||
this.componentStack.push(component);
|
||||
}
|
||||
} else {
|
||||
_.pull(this.componentStack, component);
|
||||
}
|
||||
} else {
|
||||
// Editor
|
||||
if(component.active && this.note && (component.isActiveForItem(this.note) || component.isDefaultEditor())) {
|
||||
this.selectedEditor = component;
|
||||
} else {
|
||||
this.selectedEditor = null;
|
||||
}
|
||||
}
|
||||
}.bind(this), contextRequestHandler: function(component){
|
||||
return this.note;
|
||||
}.bind(this), actionHandler: function(component, action, data){
|
||||
if(action === "set-size") {
|
||||
var setSize = function(element, size) {
|
||||
var widthString = typeof size.width === 'string' ? size.width : `${data.width}px`;
|
||||
var heightString = typeof size.height === 'string' ? size.height : `${data.height}px`;
|
||||
element.setAttribute("style", `width:${widthString}; height:${heightString}; `);
|
||||
}
|
||||
|
||||
if(data.type == "container") {
|
||||
if(component.area == "note-tags") {
|
||||
var container = document.getElementById("note-tags-component-container");
|
||||
setSize(container, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if(action === "associate-item") {
|
||||
if(data.item.content_type == "Tag") {
|
||||
var tag = modelManager.findItem(data.item.uuid);
|
||||
this.addTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
else if(action === "deassociate-item") {
|
||||
var tag = modelManager.findItem(data.item.uuid);
|
||||
this.removeTag(tag);
|
||||
}
|
||||
|
||||
else if(action === "save-items" || action === "save-success" || action == "save-error") {
|
||||
if(data.items.map((item) => {return item.uuid}).includes(this.note.uuid)) {
|
||||
|
||||
if(action == "save-items") {
|
||||
if(this.componentSaveTimeout) $timeout.cancel(this.componentSaveTimeout);
|
||||
this.componentSaveTimeout = $timeout(this.showSavingStatus.bind(this), 10);
|
||||
}
|
||||
|
||||
else {
|
||||
if(this.componentStatusTimeout) $timeout.cancel(this.componentStatusTimeout);
|
||||
if(action == "save-success") {
|
||||
this.componentStatusTimeout = $timeout(this.showAllChangesSavedStatus.bind(this), 400);
|
||||
} else {
|
||||
this.componentStatusTimeout = $timeout(this.showErrorStatus.bind(this), 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this)});
|
||||
|
||||
this.reloadComponentContext = function() {
|
||||
componentManager.contextItemDidChangeInArea("note-tags");
|
||||
componentManager.contextItemDidChangeInArea("editor-stack");
|
||||
componentManager.contextItemDidChangeInArea("editor-editor");
|
||||
|
||||
var stack = componentManager.componentsForArea("editor-stack");
|
||||
for(var component of stack) {
|
||||
var activeForItem = component.isActiveForItem(this.note);
|
||||
if(activeForItem) {
|
||||
if(!component.active) {
|
||||
componentManager.activateComponent(component);
|
||||
}
|
||||
} else {
|
||||
if(component.active) {
|
||||
componentManager.deactivateComponent(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.toggleStackComponentForCurrentItem = function(component) {
|
||||
if(component.isActiveForItem(this.note)) {
|
||||
componentManager.deactivateComponent(component);
|
||||
this.disassociateComponentWithCurrentNote(component);
|
||||
} else {
|
||||
componentManager.activateComponent(component);
|
||||
componentManager.contextItemDidChangeInArea("editor-stack");
|
||||
this.associateComponentWithCurrentNote(component);
|
||||
}
|
||||
}
|
||||
|
||||
this.disassociateComponentWithCurrentNote = function(component) {
|
||||
component.associatedItemIds = component.associatedItemIds.filter((id) => {return id !== this.note.uuid});
|
||||
|
||||
// Only disassociative components should modify the disassociatedItemIds
|
||||
if(!component.isAssociative() && !component.disassociatedItemIds.includes(this.note.uuid)) {
|
||||
component.disassociatedItemIds.push(this.note.uuid);
|
||||
}
|
||||
|
||||
component.setDirty(true);
|
||||
}
|
||||
|
||||
this.associateComponentWithCurrentNote = function(component) {
|
||||
component.disassociatedItemIds = component.disassociatedItemIds.filter((id) => {return id !== this.note.uuid});
|
||||
|
||||
if(component.isAssociative() && !component.associatedItemIds.includes(this.note.uuid)) {
|
||||
component.associatedItemIds.push(this.note.uuid);
|
||||
}
|
||||
|
||||
component.setDirty(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Editor Customization
|
||||
*/
|
||||
|
||||
this.onSystemEditorLoad = function() {
|
||||
if(this.loadedTabListener) {
|
||||
return;
|
||||
}
|
||||
this.loadedTabListener = true;
|
||||
/**
|
||||
* 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 parent = this;
|
||||
var handleTab = function (event) {
|
||||
if (!event.shiftKey && event.which == 9) {
|
||||
event.preventDefault();
|
||||
|
||||
// Using document.execCommand gives us undo support
|
||||
if(!document.execCommand("insertText", false, "\t")) {
|
||||
// document.execCommand works great on Chrome/Safari but not Firefox
|
||||
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;
|
||||
}
|
||||
|
||||
parent.note.text = this.value;
|
||||
parent.changesMade();
|
||||
}
|
||||
}
|
||||
|
||||
var element = document.getElementById("note-text-editor");
|
||||
element.addEventListener('keydown', handleTab);
|
||||
|
||||
angular.element(element).on('$destroy', function(){
|
||||
window.removeEventListener('keydown', handleTab);
|
||||
this.loadedTabListener = false;
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
154
app/assets/javascripts/app/controllers/footer.js
Normal file
154
app/assets/javascripts/app/controllers/footer.js
Normal file
@@ -0,0 +1,154 @@
|
||||
angular.module('app')
|
||||
.directive("footer", function(authManager){
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {},
|
||||
templateUrl: 'footer.html',
|
||||
replace: true,
|
||||
controller: 'FooterCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$on("sync:updated_token", function(){
|
||||
ctrl.syncUpdated();
|
||||
ctrl.findErrors();
|
||||
ctrl.updateOfflineStatus();
|
||||
})
|
||||
scope.$on("sync:error", function(){
|
||||
ctrl.findErrors();
|
||||
ctrl.updateOfflineStatus();
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager,
|
||||
syncManager, storageManager, passcodeManager, componentManager, singletonManager, packageManager) {
|
||||
|
||||
this.getUser = function() {
|
||||
return authManager.user;
|
||||
}
|
||||
|
||||
this.updateOfflineStatus = function() {
|
||||
this.offline = authManager.offline();
|
||||
}
|
||||
this.updateOfflineStatus();
|
||||
|
||||
if(this.offline && !passcodeManager.hasPasscode()) {
|
||||
this.showAccountMenu = true;
|
||||
}
|
||||
|
||||
this.findErrors = function() {
|
||||
this.error = syncManager.syncStatus.error;
|
||||
}
|
||||
this.findErrors();
|
||||
|
||||
this.onAuthSuccess = function() {
|
||||
this.showAccountMenu = false;
|
||||
}.bind(this)
|
||||
|
||||
this.closeAccountMenu = () => {
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
|
||||
this.accountMenuPressed = function() {
|
||||
this.showAccountMenu = !this.showAccountMenu;
|
||||
}
|
||||
|
||||
this.toggleExtensions = function() {
|
||||
this.showExtensionsMenu = !this.showExtensionsMenu;
|
||||
}
|
||||
|
||||
this.hasPasscode = function() {
|
||||
return passcodeManager.hasPasscode();
|
||||
}
|
||||
|
||||
this.lockApp = function() {
|
||||
$rootScope.lockApplication();
|
||||
}
|
||||
|
||||
this.refreshData = function() {
|
||||
this.isRefreshing = true;
|
||||
syncManager.sync(function(response){
|
||||
$timeout(function(){
|
||||
this.isRefreshing = false;
|
||||
}.bind(this), 200)
|
||||
if(response && response.error) {
|
||||
alert("There was an error syncing. Please try again. If all else fails, log out and log back in.");
|
||||
} else {
|
||||
this.syncUpdated();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.syncUpdated = function() {
|
||||
this.lastSyncDate = new Date();
|
||||
}
|
||||
|
||||
$rootScope.$on("new-update-available", function(version){
|
||||
$timeout(function(){
|
||||
// timeout calls apply() which is needed
|
||||
this.onNewUpdateAvailable();
|
||||
}.bind(this))
|
||||
}.bind(this))
|
||||
|
||||
this.onNewUpdateAvailable = function() {
|
||||
this.newUpdateAvailable = true;
|
||||
}
|
||||
|
||||
this.clickedNewUpdateAnnouncement = function() {
|
||||
this.newUpdateAvailable = false;
|
||||
alert("A new update is ready to install. Updates address performance and security issues, as well as bug fixes and feature enhancements. Simply quit Standard Notes and re-open it for the update to be applied.")
|
||||
}
|
||||
|
||||
|
||||
/* Rooms */
|
||||
|
||||
this.componentManager = componentManager;
|
||||
this.rooms = [];
|
||||
|
||||
modelManager.addItemSyncObserver("room-bar", "SN|Component", (allItems, validItems, deletedItems, source) => {
|
||||
var incomingRooms = allItems.filter((candidate) => {return candidate.area == "rooms"});
|
||||
this.rooms = _.uniq(this.rooms.concat(incomingRooms)).filter((candidate) => {return !candidate.deleted});
|
||||
});
|
||||
|
||||
componentManager.registerHandler({identifier: "roomBar", areas: ["rooms", "modal"], activationHandler: (component) => {
|
||||
if(component.active) {
|
||||
// Show room, if it was not activated manually (in the event of event from componentManager)
|
||||
if(component.area == "rooms" && !component.showRoom) {
|
||||
this.selectRoom(component);
|
||||
}
|
||||
$timeout(() => {
|
||||
var lastSize = component.getLastSize();
|
||||
if(lastSize) {
|
||||
componentManager.handleSetSizeEvent(component, lastSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, actionHandler: (component, action, data) => {
|
||||
if(action == "set-size") {
|
||||
component.setLastSize(data);
|
||||
}
|
||||
}});
|
||||
|
||||
this.selectRoom = function(room) {
|
||||
|
||||
// Allows us to send messages to component modal directive
|
||||
if(!room.directiveController) {
|
||||
room.directiveController = {onDismiss: () => {
|
||||
room.showRoom = false;
|
||||
}};
|
||||
}
|
||||
|
||||
// Make sure to call dismiss() before setting new showRoom value
|
||||
// This way the directive stays alive long enough to deactivate the associated component
|
||||
// (The directive's life is at the mercy of "ng-if" => "room.showRoom")
|
||||
if(room.showRoom) {
|
||||
room.directiveController.dismiss(() => {
|
||||
|
||||
});
|
||||
} else {
|
||||
room.showRoom = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
281
app/assets/javascripts/app/controllers/home.js
Normal file
281
app/assets/javascripts/app/controllers/home.js
Normal file
@@ -0,0 +1,281 @@
|
||||
angular.module('app')
|
||||
.controller('HomeCtrl', function ($scope, $location, $rootScope, $timeout, modelManager,
|
||||
dbManager, syncManager, authManager, themeManager, passcodeManager, storageManager, migrationManager) {
|
||||
|
||||
storageManager.initialize(passcodeManager.hasPasscode(), authManager.isEphemeralSession());
|
||||
|
||||
$scope.onUpdateAvailable = function(version) {
|
||||
$rootScope.$broadcast('new-update-available', version);
|
||||
}
|
||||
|
||||
/* Used to avoid circular dependencies where syncManager cannot be imported but rootScope can */
|
||||
$rootScope.sync = function() {
|
||||
syncManager.sync();
|
||||
}
|
||||
|
||||
$rootScope.lockApplication = function() {
|
||||
// Reloading wipes current objects from memory
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function load() {
|
||||
// pass keys to storageManager to decrypt storage
|
||||
storageManager.setKeys(passcodeManager.keys());
|
||||
|
||||
openDatabase();
|
||||
// Retrieve local data and begin sycing timer
|
||||
initiateSync();
|
||||
// Configure "All" psuedo-tag
|
||||
loadAllTag();
|
||||
// Configure "Archived" psuedo-tag
|
||||
loadArchivedTag();
|
||||
}
|
||||
|
||||
if(passcodeManager.isLocked()) {
|
||||
$scope.needsUnlock = true;
|
||||
} else {
|
||||
load();
|
||||
}
|
||||
|
||||
$scope.onSuccessfulUnlock = function() {
|
||||
$timeout(() => {
|
||||
$scope.needsUnlock = false;
|
||||
load();
|
||||
})
|
||||
}
|
||||
|
||||
function openDatabase() {
|
||||
dbManager.setLocked(false);
|
||||
dbManager.openDatabase(null, function() {
|
||||
// new database, delete syncToken so that items can be refetched entirely from server
|
||||
syncManager.clearSyncToken();
|
||||
syncManager.sync();
|
||||
})
|
||||
}
|
||||
|
||||
function initiateSync() {
|
||||
authManager.loadInitialData();
|
||||
syncManager.loadLocalItems(function(items) {
|
||||
$scope.allTag.didLoad = true;
|
||||
themeManager.activateInitialTheme();
|
||||
$scope.$apply();
|
||||
|
||||
$rootScope.$broadcast("initial-data-loaded");
|
||||
|
||||
syncManager.sync(null);
|
||||
// refresh every 30s
|
||||
setInterval(function () {
|
||||
syncManager.sync(null);
|
||||
}, 30000);
|
||||
});
|
||||
}
|
||||
|
||||
function loadAllTag() {
|
||||
var allTag = new Tag({all: true, title: "All"});
|
||||
allTag.needsLoad = true;
|
||||
$scope.allTag = allTag;
|
||||
$scope.tags = modelManager.tags;
|
||||
$scope.allTag.notes = modelManager.notes;
|
||||
}
|
||||
|
||||
function loadArchivedTag() {
|
||||
var archiveTag = new Tag({archiveTag: true, title: "Archived"});
|
||||
$scope.archiveTag = archiveTag;
|
||||
$scope.archiveTag.notes = modelManager.notes;
|
||||
}
|
||||
|
||||
/*
|
||||
Editor Callbacks
|
||||
*/
|
||||
|
||||
$scope.updateTagsForNote = function(note, stringTags) {
|
||||
var toRemove = [];
|
||||
for(var tag of note.tags) {
|
||||
if(stringTags.indexOf(tag.title) === -1) {
|
||||
// remove this tag
|
||||
toRemove.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
for(var tagToRemove of toRemove) {
|
||||
note.removeItemAsRelationship(tagToRemove);
|
||||
tagToRemove.removeItemAsRelationship(note);
|
||||
tagToRemove.setDirty(true);
|
||||
}
|
||||
|
||||
var tags = [];
|
||||
for(var tagString of stringTags) {
|
||||
var existingRelationship = _.find(note.tags, {title: tagString});
|
||||
if(!existingRelationship) {
|
||||
tags.push(modelManager.findOrCreateTagByTitle(tagString));
|
||||
}
|
||||
}
|
||||
|
||||
for(var tag of tags) {
|
||||
modelManager.createRelationshipBetweenItems(note, tag);
|
||||
}
|
||||
|
||||
note.setDirty(true);
|
||||
syncManager.sync();
|
||||
}
|
||||
|
||||
/*
|
||||
Tags Ctrl Callbacks
|
||||
*/
|
||||
|
||||
|
||||
$scope.tagsWillMakeSelection = function(tag) {
|
||||
|
||||
}
|
||||
|
||||
$scope.tagsSelectionMade = function(tag) {
|
||||
$scope.selectedTag = tag;
|
||||
|
||||
if($scope.selectedNote && $scope.selectedNote.dummy) {
|
||||
modelManager.removeItemLocally($scope.selectedNote);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.tagsAddNew = function(tag) {
|
||||
modelManager.addItem(tag);
|
||||
}
|
||||
|
||||
$scope.tagsSave = function(tag, callback) {
|
||||
if(!tag.title || tag.title.length == 0) {
|
||||
$scope.removeTag(tag);
|
||||
return;
|
||||
}
|
||||
tag.setDirty(true);
|
||||
syncManager.sync(callback);
|
||||
$rootScope.$broadcast("tag-changed");
|
||||
modelManager.resortTag(tag);
|
||||
}
|
||||
|
||||
/*
|
||||
Notes Ctrl Callbacks
|
||||
*/
|
||||
|
||||
$scope.removeTag = function(tag) {
|
||||
if(confirm("Are you sure you want to delete this tag? Note: deleting a tag will not delete its notes.")) {
|
||||
modelManager.setItemToBeDeleted(tag);
|
||||
// if no more notes, delete tag
|
||||
syncManager.sync(function(){
|
||||
// force scope tags to update on sub directives
|
||||
$scope.safeApply();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$scope.notesSelectionMade = function(note) {
|
||||
$scope.selectedNote = note;
|
||||
}
|
||||
|
||||
$scope.notesAddNew = function(note) {
|
||||
modelManager.addItem(note);
|
||||
|
||||
if(!$scope.selectedTag.all && !$scope.selectedTag.archiveTag) {
|
||||
modelManager.createRelationshipBetweenItems($scope.selectedTag, note);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Shared Callbacks
|
||||
*/
|
||||
|
||||
$scope.saveNote = function(note, callback) {
|
||||
note.setDirty(true);
|
||||
|
||||
syncManager.sync(function(response){
|
||||
if(response && response.error) {
|
||||
if(!$scope.didShowErrorAlert) {
|
||||
$scope.didShowErrorAlert = true;
|
||||
alert("There was an error saving your note. Please try again.");
|
||||
}
|
||||
if(callback) {
|
||||
callback(false);
|
||||
}
|
||||
} else {
|
||||
note.hasChanges = false;
|
||||
if(callback) {
|
||||
callback(true);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$scope.safeApply = function(fn) {
|
||||
var phase = this.$root.$$phase;
|
||||
if(phase == '$apply' || phase == '$digest')
|
||||
this.$eval(fn);
|
||||
else
|
||||
this.$apply(fn);
|
||||
};
|
||||
|
||||
$scope.notifyDelete = function() {
|
||||
$timeout(function() {
|
||||
$rootScope.$broadcast("noteDeleted");
|
||||
}.bind(this), 0);
|
||||
}
|
||||
|
||||
$scope.deleteNote = function(note) {
|
||||
|
||||
modelManager.setItemToBeDeleted(note);
|
||||
|
||||
if(note == $scope.selectedNote) {
|
||||
$scope.selectedNote = null;
|
||||
}
|
||||
|
||||
if(note.dummy) {
|
||||
modelManager.removeItemLocally(note);
|
||||
$scope.notifyDelete();
|
||||
return;
|
||||
}
|
||||
|
||||
syncManager.sync(function(){
|
||||
if(authManager.offline()) {
|
||||
// when deleting items while ofline, we need to explictly tell angular to refresh UI
|
||||
setTimeout(function () {
|
||||
$scope.notifyDelete();
|
||||
$scope.safeApply();
|
||||
}, 50);
|
||||
} else {
|
||||
$scope.notifyDelete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Handle Auto Sign In From URL
|
||||
|
||||
function urlParam(key) {
|
||||
return $location.search()[key];
|
||||
}
|
||||
|
||||
function autoSignInFromParams() {
|
||||
var server = urlParam("server");
|
||||
var email = urlParam("email");
|
||||
var pw = urlParam("pw");
|
||||
|
||||
if(!authManager.offline()) {
|
||||
// check if current account
|
||||
if(syncManager.serverURL === server && authManager.user.email === email) {
|
||||
// already signed in, return
|
||||
return;
|
||||
} else {
|
||||
// sign out
|
||||
syncManager.destroyLocalData(function(){
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
} else {
|
||||
authManager.login(server, email, pw, false, function(response){
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if(urlParam("server")) {
|
||||
autoSignInFromParams();
|
||||
}
|
||||
});
|
||||
30
app/assets/javascripts/app/controllers/lockScreen.js
Normal file
30
app/assets/javascripts/app/controllers/lockScreen.js
Normal file
@@ -0,0 +1,30 @@
|
||||
class LockScreen {
|
||||
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "lock-screen.html";
|
||||
this.scope = {
|
||||
onSuccess: "&",
|
||||
};
|
||||
}
|
||||
|
||||
controller($scope, passcodeManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {};
|
||||
|
||||
$scope.submitPasscodeForm = function() {
|
||||
passcodeManager.unlock($scope.formData.passcode, (success) => {
|
||||
if(!success) {
|
||||
alert("Invalid passcode. Please try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.onSuccess()();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('lockScreen', () => new LockScreen);
|
||||
253
app/assets/javascripts/app/controllers/notes.js
Normal file
253
app/assets/javascripts/app/controllers/notes.js
Normal file
@@ -0,0 +1,253 @@
|
||||
angular.module('app')
|
||||
.directive("notesSection", function(){
|
||||
return {
|
||||
scope: {
|
||||
addNew: "&",
|
||||
selectionMade: "&",
|
||||
tag: "="
|
||||
},
|
||||
|
||||
templateUrl: 'notes.html',
|
||||
replace: true,
|
||||
controller: 'NotesCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.tag', function(tag, oldTag){
|
||||
if(tag) {
|
||||
if(tag.needsLoad) {
|
||||
scope.$watch('ctrl.tag.didLoad', function(didLoad){
|
||||
if(didLoad) {
|
||||
tag.needsLoad = false;
|
||||
ctrl.tagDidChange(tag, oldTag);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ctrl.tagDidChange(tag, oldTag);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager) {
|
||||
|
||||
this.panelController = {};
|
||||
|
||||
$rootScope.$on("user-preferences-changed", () => {
|
||||
this.loadPreferences();
|
||||
});
|
||||
|
||||
this.loadPreferences = function() {
|
||||
this.sortBy = authManager.getUserPrefValue("sortBy", "created_at");
|
||||
this.sortDescending = this.sortBy != "title";
|
||||
|
||||
this.showArchived = authManager.getUserPrefValue("showArchived", false);
|
||||
this.hidePinned = authManager.getUserPrefValue("hidePinned", false);
|
||||
this.hideNotePreview = authManager.getUserPrefValue("hideNotePreview", false);
|
||||
this.hideDate = authManager.getUserPrefValue("hideDate", false);
|
||||
this.hideTags = authManager.getUserPrefValue("hideTags", false);
|
||||
|
||||
let width = authManager.getUserPrefValue("notesPanelWidth");
|
||||
if(width) {
|
||||
this.panelController.setWidth(width);
|
||||
}
|
||||
}
|
||||
|
||||
this.loadPreferences();
|
||||
|
||||
this.onPanelResize = function(newWidth) {
|
||||
authManager.setUserPrefValue("notesPanelWidth", newWidth);
|
||||
authManager.syncUserPreferences();
|
||||
}
|
||||
|
||||
angular.element(document).ready(() => {
|
||||
this.loadPreferences();
|
||||
});
|
||||
|
||||
$rootScope.$on("editorFocused", function(){
|
||||
this.showMenu = false;
|
||||
}.bind(this))
|
||||
|
||||
$rootScope.$on("noteDeleted", function() {
|
||||
$timeout(this.onNoteRemoval.bind(this));
|
||||
}.bind(this))
|
||||
|
||||
$rootScope.$on("noteArchived", function() {
|
||||
$timeout(this.onNoteRemoval.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
|
||||
// When a note is removed from the list
|
||||
this.onNoteRemoval = function() {
|
||||
let visibleNotes = this.visibleNotes();
|
||||
if(this.selectedIndex < visibleNotes.length) {
|
||||
this.selectNote(visibleNotes[this.selectedIndex]);
|
||||
} else {
|
||||
this.selectNote(visibleNotes[visibleNotes.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
this.DefaultNotesToDisplayValue = 20;
|
||||
|
||||
this.notesToDisplay = this.DefaultNotesToDisplayValue;
|
||||
this.paginate = function() {
|
||||
this.notesToDisplay += this.DefaultNotesToDisplayValue
|
||||
}
|
||||
|
||||
this.panelTitle = function() {
|
||||
if(this.noteFilter.text.length) {
|
||||
return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`;
|
||||
} else if(this.tag) {
|
||||
return `${this.tag.title} notes`;
|
||||
}
|
||||
}
|
||||
|
||||
this.optionsSubtitle = function() {
|
||||
var base = "";
|
||||
if(this.sortBy == "created_at") {
|
||||
base += " Date Added";
|
||||
} else if(this.sortBy == "updated_at") {
|
||||
base += " Date Modifed";
|
||||
} else if(this.sortBy == "title") {
|
||||
base += " Title";
|
||||
}
|
||||
|
||||
if(this.showArchived && (!this.tag || !this.tag.archiveTag)) {
|
||||
base += " | + Archived"
|
||||
}
|
||||
|
||||
if(this.hidePinned) {
|
||||
base += " | – Pinned"
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
this.toggleKey = function(key) {
|
||||
this[key] = !this[key];
|
||||
authManager.setUserPrefValue(key, this[key]);
|
||||
authManager.syncUserPreferences();
|
||||
}
|
||||
|
||||
this.tagDidChange = function(tag, oldTag) {
|
||||
var scrollable = document.getElementById("notes-scrollable");
|
||||
if(scrollable) {
|
||||
scrollable.scrollTop = 0;
|
||||
scrollable.scrollLeft = 0;
|
||||
}
|
||||
|
||||
this.notesToDisplay = this.DefaultNotesToDisplayValue;
|
||||
|
||||
this.showMenu = false;
|
||||
|
||||
if(this.selectedNote && this.selectedNote.dummy) {
|
||||
if(oldTag) {
|
||||
_.remove(oldTag.notes, this.selectedNote);
|
||||
}
|
||||
}
|
||||
|
||||
this.noteFilter.text = "";
|
||||
this.setNotes(tag.notes);
|
||||
}
|
||||
|
||||
this.setNotes = function(notes) {
|
||||
notes.forEach(function(note){
|
||||
note.visible = true;
|
||||
})
|
||||
|
||||
var createNew = notes.length == 0;
|
||||
this.selectFirstNote(createNew);
|
||||
}
|
||||
|
||||
this.visibleNotes = function() {
|
||||
return this.sortedNotes.filter(function(note){
|
||||
return note.visible;
|
||||
});
|
||||
}
|
||||
|
||||
this.selectFirstNote = function(createNew) {
|
||||
var visibleNotes = this.visibleNotes();
|
||||
|
||||
if(visibleNotes.length > 0) {
|
||||
this.selectNote(visibleNotes[0]);
|
||||
} else if(createNew) {
|
||||
this.createNewNote();
|
||||
}
|
||||
}
|
||||
|
||||
this.selectNote = function(note) {
|
||||
if(!note) { return; }
|
||||
this.selectedNote = note;
|
||||
note.conflict_of = null; // clear conflict
|
||||
this.selectionMade()(note);
|
||||
}
|
||||
|
||||
this.createNewNote = function() {
|
||||
var title = "New Note" + (this.tag.notes ? (" " + (this.tag.notes.length + 1)) : "");
|
||||
this.newNote = modelManager.createItem({content_type: "Note", dummy: true, text: ""});
|
||||
this.newNote.title = title;
|
||||
this.selectNote(this.newNote);
|
||||
this.addNew()(this.newNote);
|
||||
}
|
||||
|
||||
this.noteFilter = {text : ''};
|
||||
|
||||
this.filterNotes = function(note) {
|
||||
if(this.tag.archiveTag) {
|
||||
note.visible = note.archived;
|
||||
return note.visible;
|
||||
}
|
||||
|
||||
if((note.archived && !this.showArchived) || (note.pinned && this.hidePinned)) {
|
||||
note.visible = false;
|
||||
return note.visible;
|
||||
}
|
||||
|
||||
var filterText = this.noteFilter.text.toLowerCase();
|
||||
if(filterText.length == 0) {
|
||||
note.visible = true;
|
||||
} else {
|
||||
var words = filterText.split(" ");
|
||||
var matchesTitle = words.every(function(word) { return note.safeTitle().toLowerCase().indexOf(word) >= 0; });
|
||||
var matchesBody = words.every(function(word) { return note.safeText().toLowerCase().indexOf(word) >= 0; });
|
||||
note.visible = matchesTitle || matchesBody;
|
||||
}
|
||||
return note.visible;
|
||||
}.bind(this)
|
||||
|
||||
this.filterTextChanged = function() {
|
||||
$timeout(function(){
|
||||
if(!this.selectedNote.visible) {
|
||||
this.selectFirstNote(false);
|
||||
}
|
||||
}.bind(this), 100)
|
||||
}
|
||||
|
||||
this.selectedMenuItem = function($event) {
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
this.selectedSortByCreated = function() {
|
||||
this.setSortBy("created_at");
|
||||
this.sortDescending = true;
|
||||
}
|
||||
|
||||
this.selectedSortByUpdated = function() {
|
||||
this.setSortBy("updated_at");
|
||||
this.sortDescending = true;
|
||||
}
|
||||
|
||||
this.selectedSortByTitle = function() {
|
||||
this.setSortBy("title");
|
||||
this.sortDescending = false;
|
||||
}
|
||||
|
||||
this.setSortBy = function(type) {
|
||||
this.sortBy = type;
|
||||
authManager.setUserPrefValue("sortBy", this.sortBy);
|
||||
authManager.syncUserPreferences();
|
||||
}
|
||||
|
||||
});
|
||||
163
app/assets/javascripts/app/controllers/tags.js
Normal file
163
app/assets/javascripts/app/controllers/tags.js
Normal file
@@ -0,0 +1,163 @@
|
||||
angular.module('app')
|
||||
.directive("tagsSection", function(){
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
addNew: "&",
|
||||
selectionMade: "&",
|
||||
willSelect: "&",
|
||||
save: "&",
|
||||
tags: "=",
|
||||
allTag: "=",
|
||||
archiveTag: "=",
|
||||
updateNoteTag: "&",
|
||||
removeTag: "&"
|
||||
},
|
||||
templateUrl: 'tags.html',
|
||||
replace: true,
|
||||
controller: 'TagsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.tags', function(newTags){
|
||||
if(newTags) {
|
||||
ctrl.setTags(newTags);
|
||||
}
|
||||
});
|
||||
|
||||
scope.$watch('ctrl.allTag', function(allTag){
|
||||
if(allTag) {
|
||||
ctrl.setAllTag(allTag);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('TagsCtrl', function ($rootScope, modelManager, $timeout, componentManager, authManager) {
|
||||
|
||||
var initialLoad = true;
|
||||
|
||||
this.panelController = {};
|
||||
|
||||
$rootScope.$on("user-preferences-changed", () => {
|
||||
this.loadPreferences();
|
||||
});
|
||||
|
||||
this.loadPreferences = function() {
|
||||
let width = authManager.getUserPrefValue("tagsPanelWidth");
|
||||
if(width) {
|
||||
this.panelController.setWidth(width);
|
||||
}
|
||||
}
|
||||
|
||||
this.loadPreferences();
|
||||
|
||||
this.onPanelResize = function(newWidth) {
|
||||
authManager.setUserPrefValue("tagsPanelWidth", newWidth);
|
||||
authManager.syncUserPreferences();
|
||||
}
|
||||
|
||||
this.componentManager = componentManager;
|
||||
|
||||
componentManager.registerHandler({identifier: "tags", areas: ["tags-list"], activationHandler: function(component){
|
||||
this.component = component;
|
||||
|
||||
}.bind(this), contextRequestHandler: function(component){
|
||||
return null;
|
||||
}.bind(this), actionHandler: function(component, action, data){
|
||||
if(action === "select-item") {
|
||||
var tag = modelManager.findItem(data.item.uuid);
|
||||
if(tag) {
|
||||
this.selectTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
else if(action === "clear-selection") {
|
||||
this.selectTag(this.allTag);
|
||||
}
|
||||
}.bind(this)});
|
||||
|
||||
this.setAllTag = function(allTag) {
|
||||
this.selectTag(this.allTag);
|
||||
}
|
||||
|
||||
this.setTags = function(tags) {
|
||||
if(initialLoad) {
|
||||
initialLoad = false;
|
||||
this.selectTag(this.allTag);
|
||||
} else {
|
||||
if(tags && tags.length > 0) {
|
||||
this.selectTag(tags[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.selectTag = function(tag) {
|
||||
this.willSelect()(tag);
|
||||
this.selectedTag = tag;
|
||||
tag.conflict_of = null; // clear conflict
|
||||
this.selectionMade()(tag);
|
||||
}
|
||||
|
||||
this.clickedAddNewTag = function() {
|
||||
if(this.editingTag) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.newTag = modelManager.createItem({content_type: "Tag"});
|
||||
this.selectedTag = this.newTag;
|
||||
this.editingTag = this.newTag;
|
||||
this.addNew()(this.newTag);
|
||||
}
|
||||
|
||||
this.tagTitleDidChange = function(tag) {
|
||||
this.editingTag = tag;
|
||||
}
|
||||
|
||||
this.saveTag = function($event, tag) {
|
||||
this.editingTag = null;
|
||||
$event.target.blur();
|
||||
|
||||
if(!tag.title || tag.title.length == 0) {
|
||||
if(originalTagName) {
|
||||
tag.title = originalTagName;
|
||||
originalTagName = null;
|
||||
} else {
|
||||
// newly created tag without content
|
||||
modelManager.removeItemLocally(tag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.save()(tag, function(savedTag){
|
||||
this.selectTag(tag);
|
||||
this.newTag = null;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
function inputElementForTag(tag) {
|
||||
return document.getElementById("tag-" + tag.uuid);
|
||||
}
|
||||
|
||||
var originalTagName = "";
|
||||
this.selectedRenameTag = function($event, tag) {
|
||||
originalTagName = tag.title;
|
||||
this.editingTag = tag;
|
||||
$timeout(function(){
|
||||
inputElementForTag(tag).focus();
|
||||
})
|
||||
}
|
||||
|
||||
this.selectedDeleteTag = function(tag) {
|
||||
this.removeTag()(tag);
|
||||
}
|
||||
|
||||
this.noteCount = function(tag) {
|
||||
var validNotes = Note.filterDummyNotes(tag.notes).filter(function(note){
|
||||
return !note.archived;
|
||||
});
|
||||
return validNotes.length;
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user