Grunt -> Webpack, Haml -> Pug
This commit is contained in:
@@ -1,104 +1,160 @@
|
||||
'use strict';
|
||||
|
||||
var SN = SN || {};
|
||||
import angular from 'angular';
|
||||
import { configRoutes } from './routes';
|
||||
|
||||
angular.module('app', [
|
||||
'ngSanitize'
|
||||
])
|
||||
import {
|
||||
Home,
|
||||
TagsPanel,
|
||||
NotesPanel,
|
||||
EditorPanel,
|
||||
Footer,
|
||||
LockScreen
|
||||
} from './controllers';
|
||||
|
||||
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, " "));
|
||||
}
|
||||
import {
|
||||
autofocus,
|
||||
clickOutside,
|
||||
delayHide,
|
||||
elemReady,
|
||||
fileChange,
|
||||
infiniteScroll,
|
||||
lowercase,
|
||||
selectOnClick,
|
||||
snEnter
|
||||
} from './directives/functional';
|
||||
|
||||
function parametersFromURL(url) {
|
||||
url = url.split("?").slice(-1)[0];
|
||||
var obj = {};
|
||||
url.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
|
||||
obj[decodeURIComponent(key)] = decodeURIComponent(value);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
import {
|
||||
AccountMenu,
|
||||
ActionsMenu,
|
||||
ComponentModal,
|
||||
ComponentView,
|
||||
ConflictResolutionModal,
|
||||
EditorMenu,
|
||||
InputModal,
|
||||
MenuRow,
|
||||
PanelResizer,
|
||||
PasswordWizard,
|
||||
PermissionsModal,
|
||||
PrivilegesAuthModal,
|
||||
PrivilegesManagementModal,
|
||||
RevisionPreviewModal,
|
||||
SessionHistoryMenu,
|
||||
SyncResolutionMenu
|
||||
} from './directives/views';
|
||||
|
||||
function getPlatformString() {
|
||||
try {
|
||||
var platform = navigator.platform.toLowerCase();
|
||||
var trimmed = "";
|
||||
if(platform.indexOf("mac") !== -1) {
|
||||
trimmed = "mac";
|
||||
} else if(platform.indexOf("win") !== -1) {
|
||||
trimmed = "windows";
|
||||
} if(platform.indexOf("linux") !== -1) {
|
||||
trimmed = "linux";
|
||||
}
|
||||
import { appDate, appDateTime, trusted } from './filters';
|
||||
|
||||
return trimmed + (isDesktopApplication() ? "-desktop" : "-web");
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
import {
|
||||
ActionsManager,
|
||||
ArchiveManager,
|
||||
AuthManager,
|
||||
ComponentManager,
|
||||
DBManager,
|
||||
DesktopManager,
|
||||
HttpManager,
|
||||
KeyboardManager,
|
||||
MigrationManager,
|
||||
ModelManager,
|
||||
NativeExtManager,
|
||||
PasscodeManager,
|
||||
PrivilegesManager,
|
||||
SessionHistory,
|
||||
SingletonManager,
|
||||
StatusManager,
|
||||
StorageManager,
|
||||
SyncManager,
|
||||
ThemeManager,
|
||||
AlertManager
|
||||
} from './services';
|
||||
|
||||
function isDesktopApplication() {
|
||||
return window.isElectron;
|
||||
}
|
||||
angular.module('app', ['ngSanitize']);
|
||||
|
||||
/* Use with numbers and strings, not objects */
|
||||
Array.prototype.containsPrimitiveSubset = function(array) {
|
||||
return !array.some(val => this.indexOf(val) === -1);
|
||||
}
|
||||
// Config
|
||||
angular
|
||||
.module('app')
|
||||
.config(configRoutes)
|
||||
.constant('appVersion', __VERSION__);
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
|
||||
if (!Array.prototype.includes) {
|
||||
Object.defineProperty(Array.prototype, 'includes', {
|
||||
value: function(searchElement, fromIndex) {
|
||||
// Controllers
|
||||
angular
|
||||
.module('app')
|
||||
.directive('home', () => new Home())
|
||||
.directive('tagsPanel', () => new TagsPanel())
|
||||
.directive('notesPanel', () => new NotesPanel())
|
||||
.directive('editorPanel', () => new EditorPanel())
|
||||
.directive('footer', () => new Footer())
|
||||
.directive('lockScreen', () => new LockScreen());
|
||||
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
// Directives - Functional
|
||||
angular
|
||||
.module('app')
|
||||
.directive('snAutofocus', ['$timeout', autofocus])
|
||||
.directive('clickOutside', ['$document', clickOutside])
|
||||
.directive('delayHide', delayHide)
|
||||
.directive('elemReady', elemReady)
|
||||
.directive('fileChange', fileChange)
|
||||
.directive('infiniteScroll', [
|
||||
'$rootScope',
|
||||
'$window',
|
||||
'$timeout',
|
||||
infiniteScroll
|
||||
])
|
||||
.directive('lowercase', lowercase)
|
||||
.directive('selectOnClick', ['$window', selectOnClick])
|
||||
.directive('snEnter', snEnter);
|
||||
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
var o = Object(this);
|
||||
// Directives - Views
|
||||
angular
|
||||
.module('app')
|
||||
.directive('accountMenu', () => new AccountMenu())
|
||||
.directive('actionsMenu', () => new ActionsMenu())
|
||||
.directive('componentModal', () => new ComponentModal())
|
||||
.directive(
|
||||
'componentView',
|
||||
($rootScope, componentManager, desktopManager, $timeout) =>
|
||||
new ComponentView($rootScope, componentManager, desktopManager, $timeout)
|
||||
)
|
||||
.directive('conflictResolutionModal', () => new ConflictResolutionModal())
|
||||
.directive('editorMenu', () => new EditorMenu())
|
||||
.directive('inputModal', () => new InputModal())
|
||||
.directive('menuRow', () => new MenuRow())
|
||||
.directive('panelResizer', () => new PanelResizer())
|
||||
.directive('passwordWizard', () => new PasswordWizard())
|
||||
.directive('permissionsModal', () => new PermissionsModal())
|
||||
.directive('privilegesAuthModal', () => new PrivilegesAuthModal())
|
||||
.directive('privilegesManagementModal', () => new PrivilegesManagementModal())
|
||||
.directive('revisionPreviewModal', () => new RevisionPreviewModal())
|
||||
.directive('sessionHistoryMenu', () => new SessionHistoryMenu())
|
||||
.directive('syncResolutionMenu', () => new SyncResolutionMenu());
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
var len = o.length >>> 0;
|
||||
// Filters
|
||||
angular
|
||||
.module('app')
|
||||
.filter('appDate', appDate)
|
||||
.filter('appDateTime', appDateTime)
|
||||
.filter('trusted', ['$sce', trusted]);
|
||||
|
||||
// 3. If len is 0, return false.
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Let n be ? ToInteger(fromIndex).
|
||||
// (If fromIndex is undefined, this step produces the value 0.)
|
||||
var n = fromIndex | 0;
|
||||
|
||||
// 5. If n ≥ 0, then
|
||||
// a. Let k be n.
|
||||
// 6. Else n < 0,
|
||||
// a. Let k be len + n.
|
||||
// b. If k < 0, let k be 0.
|
||||
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
||||
|
||||
function sameValueZero(x, y) {
|
||||
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
|
||||
}
|
||||
|
||||
// 7. Repeat, while k < len
|
||||
while (k < len) {
|
||||
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
|
||||
// b. If SameValueZero(searchElement, elementK) is true, return true.
|
||||
if (sameValueZero(o[k], searchElement)) {
|
||||
return true;
|
||||
}
|
||||
// c. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 8. Return false
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
// Services
|
||||
angular
|
||||
.module('app')
|
||||
.service('actionsManager', ActionsManager)
|
||||
.service('archiveManager', ArchiveManager)
|
||||
.service('authManager', AuthManager)
|
||||
.service('componentManager', ComponentManager)
|
||||
.service('dbManager', DBManager)
|
||||
.service('desktopManager', DesktopManager)
|
||||
.service('httpManager', HttpManager)
|
||||
.service('keyboardManager', KeyboardManager)
|
||||
.service('migrationManager', MigrationManager)
|
||||
.service('modelManager', ModelManager)
|
||||
.service('nativeExtManager', NativeExtManager)
|
||||
.service('passcodeManager', PasscodeManager)
|
||||
.service('privilegesManager', PrivilegesManager)
|
||||
.service('sessionHistory', SessionHistory)
|
||||
.service('singletonManager', SingletonManager)
|
||||
.service('statusManager', StatusManager)
|
||||
.service('storageManager', StorageManager)
|
||||
.service('syncManager', SyncManager)
|
||||
.service('alertManager', AlertManager)
|
||||
.service('themeManager', ThemeManager);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
angular.module('app')
|
||||
|
||||
.constant('appVersion', '3.0.22')
|
||||
|
||||
;
|
||||
@@ -1,31 +1,51 @@
|
||||
angular.module('app')
|
||||
.directive("editorSection", function($timeout, $sce){
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
remove: "&",
|
||||
note: "=",
|
||||
updateTags: "&"
|
||||
},
|
||||
templateUrl: 'editor.html',
|
||||
replace: true,
|
||||
controller: 'EditorCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.note', (note, oldNote) => {
|
||||
if(note) {
|
||||
ctrl.noteDidChange(note, oldNote);
|
||||
}
|
||||
});
|
||||
import angular from 'angular';
|
||||
import { SFModelManager } from 'snjs';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { KeyboardManager } from '@/services/keyboardManager';
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import template from '%/editor.pug';
|
||||
|
||||
export class EditorPanel {
|
||||
constructor() {
|
||||
this.restrict = 'E';
|
||||
this.scope = {
|
||||
remove: '&',
|
||||
note: '=',
|
||||
updateTags: '&'
|
||||
};
|
||||
|
||||
this.template = template;
|
||||
this.replace = true;
|
||||
this.controllerAs = 'ctrl';
|
||||
this.bindToController = true;
|
||||
}
|
||||
|
||||
link(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.note', (note, oldNote) => {
|
||||
if (note) {
|
||||
ctrl.noteDidChange(note, oldNote);
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, actionsManager,
|
||||
syncManager, modelManager, themeManager, componentManager, storageManager, sessionHistory,
|
||||
privilegesManager, keyboardManager, desktopManager, alertManager) {
|
||||
});
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller(
|
||||
$timeout,
|
||||
authManager,
|
||||
$rootScope,
|
||||
actionsManager,
|
||||
syncManager,
|
||||
modelManager,
|
||||
themeManager,
|
||||
componentManager,
|
||||
storageManager,
|
||||
sessionHistory,
|
||||
privilegesManager,
|
||||
keyboardManager,
|
||||
desktopManager,
|
||||
alertManager
|
||||
) {
|
||||
this.spellcheck = true;
|
||||
this.componentManager = componentManager;
|
||||
this.componentStack = [];
|
||||
@@ -412,7 +432,7 @@ angular.module('app')
|
||||
|
||||
let title = this.note.safeTitle().length ? `'${this.note.title}'` : "this note";
|
||||
let text = permanently ? `Are you sure you want to permanently delete ${title}?`
|
||||
: `Are you sure you want to move ${title} to the trash?`
|
||||
: `Are you sure you want to move ${title} to the trash?`
|
||||
|
||||
alertManager.confirm({text, destructive: true, onConfirm: () => {
|
||||
if(permanently) {
|
||||
@@ -851,10 +871,10 @@ angular.module('app')
|
||||
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.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const editor = document.getElementById("note-text-editor");
|
||||
@@ -880,9 +900,9 @@ angular.module('app')
|
||||
var end = editor.selectionEnd;
|
||||
var spaces = " ";
|
||||
|
||||
// Insert 4 spaces
|
||||
// Insert 4 spaces
|
||||
editor.value = editor.value.substring(0, start)
|
||||
+ spaces + editor.value.substring(end);
|
||||
+ spaces + editor.value.substring(end);
|
||||
|
||||
// Place cursor 4 spaces away from where
|
||||
// the tab key was pressed
|
||||
@@ -897,11 +917,12 @@ angular.module('app')
|
||||
})
|
||||
|
||||
// This handles when the editor itself is destroyed, and not when our controller is destroyed.
|
||||
angular.element(editor).on('$destroy', function(){
|
||||
angular.element(editor).on('$destroy', () => {
|
||||
if(this.tabObserver) {
|
||||
keyboardManager.removeKeyObserver(this.tabObserver);
|
||||
this.loadedTabListener = false;
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,313 +1,328 @@
|
||||
angular.module('app')
|
||||
.directive("footer", function(authManager){
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {},
|
||||
templateUrl: 'footer.html',
|
||||
replace: true,
|
||||
controller: 'FooterCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import template from '%/footer.pug';
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$on("sync:completed", function(){
|
||||
ctrl.syncUpdated();
|
||||
ctrl.findErrors();
|
||||
ctrl.updateOfflineStatus();
|
||||
})
|
||||
scope.$on("sync:error", function(){
|
||||
ctrl.findErrors();
|
||||
ctrl.updateOfflineStatus();
|
||||
})
|
||||
}
|
||||
export class Footer {
|
||||
constructor() {
|
||||
this.restrict = 'E';
|
||||
this.scope = {};
|
||||
this.template = template;
|
||||
this.replace = true;
|
||||
this.controllerAs = 'ctrl';
|
||||
this.bindToController = true;
|
||||
}
|
||||
|
||||
link(scope, elem, attrs, ctrl) {
|
||||
scope.$on('sync:completed', function() {
|
||||
ctrl.syncUpdated();
|
||||
ctrl.findErrors();
|
||||
ctrl.updateOfflineStatus();
|
||||
});
|
||||
scope.$on('sync:error', function() {
|
||||
ctrl.findErrors();
|
||||
ctrl.updateOfflineStatus();
|
||||
});
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller(
|
||||
$rootScope,
|
||||
authManager,
|
||||
modelManager,
|
||||
$timeout,
|
||||
dbManager,
|
||||
syncManager,
|
||||
storageManager,
|
||||
passcodeManager,
|
||||
componentManager,
|
||||
singletonManager,
|
||||
nativeExtManager,
|
||||
privilegesManager,
|
||||
statusManager,
|
||||
alertManager
|
||||
) {
|
||||
authManager.checkForSecurityUpdate().then((available) => {
|
||||
this.securityUpdateAvailable = available;
|
||||
})
|
||||
|
||||
$rootScope.$on("security-update-status-changed", () => {
|
||||
this.securityUpdateAvailable = authManager.securityUpdateAvailable;
|
||||
})
|
||||
|
||||
statusManager.addStatusObserver((string) => {
|
||||
$timeout(() => {
|
||||
this.arbitraryStatusMessage = string;
|
||||
})
|
||||
})
|
||||
|
||||
$rootScope.$on("did-begin-local-backup", () => {
|
||||
$timeout(() => {
|
||||
this.backupStatus = statusManager.addStatusFromString("Saving local backup...");
|
||||
})
|
||||
});
|
||||
|
||||
$rootScope.$on("did-finish-local-backup", (event, data) => {
|
||||
$timeout(() => {
|
||||
if(data.success) {
|
||||
this.backupStatus = statusManager.replaceStatusWithString(this.backupStatus, "Successfully saved backup.");
|
||||
} else {
|
||||
this.backupStatus = statusManager.replaceStatusWithString(this.backupStatus, "Unable to save local backup.");
|
||||
}
|
||||
|
||||
$timeout(() => {
|
||||
this.backupStatus = statusManager.removeStatus(this.backupStatus);
|
||||
}, 2000)
|
||||
})
|
||||
});
|
||||
|
||||
this.openSecurityUpdate = function() {
|
||||
authManager.presentPasswordWizard("upgrade-security");
|
||||
}
|
||||
})
|
||||
.controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager,
|
||||
syncManager, storageManager, passcodeManager, componentManager, singletonManager, nativeExtManager,
|
||||
privilegesManager, statusManager, alertManager) {
|
||||
|
||||
authManager.checkForSecurityUpdate().then((available) => {
|
||||
this.securityUpdateAvailable = available;
|
||||
})
|
||||
$rootScope.$on("reload-ext-data", () => {
|
||||
this.reloadExtendedData();
|
||||
});
|
||||
|
||||
$rootScope.$on("security-update-status-changed", () => {
|
||||
this.securityUpdateAvailable = authManager.securityUpdateAvailable;
|
||||
})
|
||||
this.reloadExtendedData = () => {
|
||||
if(this.reloadInProgress) { return; }
|
||||
this.reloadInProgress = true;
|
||||
|
||||
statusManager.addStatusObserver((string) => {
|
||||
$timeout(() => {
|
||||
this.arbitraryStatusMessage = string;
|
||||
})
|
||||
})
|
||||
|
||||
$rootScope.$on("did-begin-local-backup", () => {
|
||||
$timeout(() => {
|
||||
this.backupStatus = statusManager.addStatusFromString("Saving local backup...");
|
||||
})
|
||||
});
|
||||
|
||||
$rootScope.$on("did-finish-local-backup", (event, data) => {
|
||||
$timeout(() => {
|
||||
if(data.success) {
|
||||
this.backupStatus = statusManager.replaceStatusWithString(this.backupStatus, "Successfully saved backup.");
|
||||
} else {
|
||||
this.backupStatus = statusManager.replaceStatusWithString(this.backupStatus, "Unable to save local backup.");
|
||||
}
|
||||
|
||||
$timeout(() => {
|
||||
this.backupStatus = statusManager.removeStatus(this.backupStatus);
|
||||
}, 2000)
|
||||
})
|
||||
});
|
||||
|
||||
this.openSecurityUpdate = function() {
|
||||
authManager.presentPasswordWizard("upgrade-security");
|
||||
// A reload occurs when the extensions manager window is opened. We can close it after a delay
|
||||
let extWindow = this.rooms.find((room) => {return room.package_info.identifier == nativeExtManager.extensionsManagerIdentifier});
|
||||
if(!extWindow) {
|
||||
this.queueExtReload = true; // try again when the ext is available
|
||||
this.reloadInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.$on("reload-ext-data", () => {
|
||||
this.reloadExtendedData();
|
||||
});
|
||||
|
||||
this.reloadExtendedData = () => {
|
||||
if(this.reloadInProgress) { return; }
|
||||
this.reloadInProgress = true;
|
||||
|
||||
// A reload occurs when the extensions manager window is opened. We can close it after a delay
|
||||
let extWindow = this.rooms.find((room) => {return room.package_info.identifier == nativeExtManager.extensionsManagerIdentifier});
|
||||
if(!extWindow) {
|
||||
this.queueExtReload = true; // try again when the ext is available
|
||||
this.reloadInProgress = false;
|
||||
return;
|
||||
}
|
||||
this.selectRoom(extWindow);
|
||||
|
||||
$timeout(() => {
|
||||
this.selectRoom(extWindow);
|
||||
this.reloadInProgress = false;
|
||||
$rootScope.$broadcast("ext-reload-complete");
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
$timeout(() => {
|
||||
this.selectRoom(extWindow);
|
||||
this.reloadInProgress = false;
|
||||
$rootScope.$broadcast("ext-reload-complete");
|
||||
}, 2000);
|
||||
}
|
||||
this.getUser = function() {
|
||||
return authManager.user;
|
||||
}
|
||||
|
||||
this.getUser = function() {
|
||||
return authManager.user;
|
||||
}
|
||||
|
||||
this.updateOfflineStatus = function() {
|
||||
this.offline = authManager.offline();
|
||||
}
|
||||
this.updateOfflineStatus();
|
||||
this.updateOfflineStatus = function() {
|
||||
this.offline = authManager.offline();
|
||||
}
|
||||
this.updateOfflineStatus();
|
||||
|
||||
|
||||
syncManager.addEventHandler((syncEvent, data) => {
|
||||
$timeout(() => {
|
||||
if(syncEvent == "local-data-loaded") {
|
||||
// If the user has no notes and is offline, show Account menu
|
||||
if(this.offline && modelManager.noteCount() == 0) {
|
||||
this.showAccountMenu = true;
|
||||
}
|
||||
} else if(syncEvent == "enter-out-of-sync") {
|
||||
this.outOfSync = true;
|
||||
} else if(syncEvent == "exit-out-of-sync") {
|
||||
this.outOfSync = false;
|
||||
syncManager.addEventHandler((syncEvent, data) => {
|
||||
$timeout(() => {
|
||||
if(syncEvent == "local-data-loaded") {
|
||||
// If the user has no notes and is offline, show Account menu
|
||||
if(this.offline && modelManager.noteCount() == 0) {
|
||||
this.showAccountMenu = true;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.findErrors = function() {
|
||||
this.error = syncManager.syncStatus.error;
|
||||
}
|
||||
this.findErrors();
|
||||
|
||||
this.onAuthSuccess = function() {
|
||||
this.showAccountMenu = false;
|
||||
}.bind(this)
|
||||
|
||||
this.accountMenuPressed = function() {
|
||||
this.showAccountMenu = !this.showAccountMenu;
|
||||
this.closeAllRooms();
|
||||
}
|
||||
|
||||
this.toggleSyncResolutionMenu = function() {
|
||||
this.showSyncResolution = !this.showSyncResolution;
|
||||
}.bind(this);
|
||||
|
||||
this.closeAccountMenu = () => {
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
|
||||
this.hasPasscode = function() {
|
||||
return passcodeManager.hasPasscode();
|
||||
}
|
||||
|
||||
this.lockApp = function() {
|
||||
$rootScope.lockApplication();
|
||||
}
|
||||
|
||||
this.refreshData = function() {
|
||||
this.isRefreshing = true;
|
||||
// Enable integrity checking for this force request
|
||||
syncManager.sync({force: true, performIntegrityCheck: true}).then((response) => {
|
||||
$timeout(function(){
|
||||
this.isRefreshing = false;
|
||||
}.bind(this), 200)
|
||||
if(response && response.error) {
|
||||
alertManager.alert({text: "There was an error syncing. Please try again. If all else fails, try signing out and signing back in."});
|
||||
} else {
|
||||
this.syncUpdated();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.syncUpdated = function() {
|
||||
this.lastSyncDate = new Date();
|
||||
}
|
||||
|
||||
$rootScope.$on("new-update-available", () => {
|
||||
$timeout(() => {
|
||||
this.onNewUpdateAvailable();
|
||||
})
|
||||
} else if(syncEvent == "enter-out-of-sync") {
|
||||
this.outOfSync = true;
|
||||
} else if(syncEvent == "exit-out-of-sync") {
|
||||
this.outOfSync = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.onNewUpdateAvailable = function() {
|
||||
this.newUpdateAvailable = true;
|
||||
}
|
||||
this.findErrors = function() {
|
||||
this.error = syncManager.syncStatus.error;
|
||||
}
|
||||
this.findErrors();
|
||||
|
||||
this.clickedNewUpdateAnnouncement = function() {
|
||||
this.newUpdateAvailable = false;
|
||||
alertManager.alert({text: "A new update is ready to install. Please use the top-level 'Updates' menu to manage installation."})
|
||||
}
|
||||
this.onAuthSuccess = function() {
|
||||
this.showAccountMenu = false;
|
||||
}.bind(this)
|
||||
|
||||
this.accountMenuPressed = function() {
|
||||
this.showAccountMenu = !this.showAccountMenu;
|
||||
this.closeAllRooms();
|
||||
}
|
||||
|
||||
/* Rooms */
|
||||
this.toggleSyncResolutionMenu = function() {
|
||||
this.showSyncResolution = !this.showSyncResolution;
|
||||
}.bind(this);
|
||||
|
||||
this.componentManager = componentManager;
|
||||
this.rooms = [];
|
||||
this.themesWithIcons = [];
|
||||
this.closeAccountMenu = () => {
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
|
||||
modelManager.addItemSyncObserver("room-bar", "SN|Component", (allItems, validItems, deletedItems, source) => {
|
||||
this.rooms = modelManager.components.filter((candidate) => {return candidate.area == "rooms" && !candidate.deleted});
|
||||
if(this.queueExtReload) {
|
||||
this.queueExtReload = false;
|
||||
this.reloadExtendedData();
|
||||
this.hasPasscode = function() {
|
||||
return passcodeManager.hasPasscode();
|
||||
}
|
||||
|
||||
this.lockApp = function() {
|
||||
$rootScope.lockApplication();
|
||||
}
|
||||
|
||||
this.refreshData = function() {
|
||||
this.isRefreshing = true;
|
||||
// Enable integrity checking for this force request
|
||||
syncManager.sync({force: true, performIntegrityCheck: true}).then((response) => {
|
||||
$timeout(function(){
|
||||
this.isRefreshing = false;
|
||||
}.bind(this), 200)
|
||||
if(response && response.error) {
|
||||
alertManager.alert({text: "There was an error syncing. Please try again. If all else fails, try signing out and signing back in."});
|
||||
} else {
|
||||
this.syncUpdated();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
modelManager.addItemSyncObserver("footer-bar-themes", "SN|Theme", (allItems, validItems, deletedItems, source) => {
|
||||
let themes = modelManager.validItemsForContentType("SN|Theme").filter((candidate) => {
|
||||
return !candidate.deleted && candidate.content.package_info && candidate.content.package_info.dock_icon;
|
||||
}).sort((a, b) => {
|
||||
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||
});
|
||||
this.syncUpdated = function() {
|
||||
this.lastSyncDate = new Date();
|
||||
}
|
||||
|
||||
let differ = themes.length != this.themesWithIcons.length;
|
||||
$rootScope.$on("new-update-available", () => {
|
||||
$timeout(() => {
|
||||
this.onNewUpdateAvailable();
|
||||
})
|
||||
})
|
||||
|
||||
this.themesWithIcons = themes;
|
||||
this.onNewUpdateAvailable = function() {
|
||||
this.newUpdateAvailable = true;
|
||||
}
|
||||
|
||||
if(differ) {
|
||||
this.reloadDockShortcuts();
|
||||
}
|
||||
this.clickedNewUpdateAnnouncement = function() {
|
||||
this.newUpdateAvailable = false;
|
||||
alertManager.alert({text: "A new update is ready to install. Please use the top-level 'Updates' menu to manage installation."})
|
||||
}
|
||||
|
||||
|
||||
/* Rooms */
|
||||
|
||||
this.componentManager = componentManager;
|
||||
this.rooms = [];
|
||||
this.themesWithIcons = [];
|
||||
|
||||
modelManager.addItemSyncObserver("room-bar", "SN|Component", (allItems, validItems, deletedItems, source) => {
|
||||
this.rooms = modelManager.components.filter((candidate) => {return candidate.area == "rooms" && !candidate.deleted});
|
||||
if(this.queueExtReload) {
|
||||
this.queueExtReload = false;
|
||||
this.reloadExtendedData();
|
||||
}
|
||||
});
|
||||
|
||||
modelManager.addItemSyncObserver("footer-bar-themes", "SN|Theme", (allItems, validItems, deletedItems, source) => {
|
||||
let themes = modelManager.validItemsForContentType("SN|Theme").filter((candidate) => {
|
||||
return !candidate.deleted && candidate.content.package_info && candidate.content.package_info.dock_icon;
|
||||
}).sort((a, b) => {
|
||||
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||
});
|
||||
|
||||
this.reloadDockShortcuts = function() {
|
||||
let shortcuts = [];
|
||||
for(var theme of this.themesWithIcons) {
|
||||
var name = theme.content.package_info.name;
|
||||
var icon = theme.content.package_info.dock_icon;
|
||||
if(!icon) {
|
||||
continue;
|
||||
}
|
||||
shortcuts.push({
|
||||
name: name,
|
||||
component: theme,
|
||||
icon: icon
|
||||
})
|
||||
let differ = themes.length != this.themesWithIcons.length;
|
||||
|
||||
this.themesWithIcons = themes;
|
||||
|
||||
if(differ) {
|
||||
this.reloadDockShortcuts();
|
||||
}
|
||||
});
|
||||
|
||||
this.reloadDockShortcuts = function() {
|
||||
let shortcuts = [];
|
||||
for(var theme of this.themesWithIcons) {
|
||||
var name = theme.content.package_info.name;
|
||||
var icon = theme.content.package_info.dock_icon;
|
||||
if(!icon) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.dockShortcuts = shortcuts.sort((a, b) => {
|
||||
// circles first, then images
|
||||
|
||||
var aType = a.icon.type;
|
||||
var bType = b.icon.type;
|
||||
|
||||
if(aType == bType) {
|
||||
return 0;
|
||||
} else if(aType == "circle" && bType == "svg") {
|
||||
return -1;
|
||||
} else if(bType == "circle" && aType == "svg") {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
shortcuts.push({
|
||||
name: name,
|
||||
component: theme,
|
||||
icon: icon
|
||||
})
|
||||
}
|
||||
|
||||
this.initSvgForShortcut = function(shortcut) {
|
||||
var id = "dock-svg-" + shortcut.component.uuid;
|
||||
var element = document.getElementById(id);
|
||||
var parser = new DOMParser();
|
||||
var svg = shortcut.component.content.package_info.dock_icon.source;
|
||||
var doc = parser.parseFromString(svg, "image/svg+xml");
|
||||
element.appendChild(doc.documentElement);
|
||||
}
|
||||
this.dockShortcuts = shortcuts.sort((a, b) => {
|
||||
// circles first, then images
|
||||
|
||||
this.selectShortcut = function(shortcut) {
|
||||
componentManager.toggleComponent(shortcut.component);
|
||||
}
|
||||
var aType = a.icon.type;
|
||||
var bType = b.icon.type;
|
||||
|
||||
componentManager.registerHandler({identifier: "roomBar", areas: ["rooms", "modal"], activationHandler: (component) => {
|
||||
// RIP: There used to be code here that checked if component.active was true, and if so, displayed the component.
|
||||
// However, we no longer want to persist active state for footer extensions. If you open Extensions on one computer,
|
||||
// it shouldn't open on another computer. Active state should only be persisted for persistent extensions, like Folders.
|
||||
}, actionHandler: (component, action, data) => {
|
||||
if(action == "set-size") {
|
||||
component.setLastSize(data);
|
||||
if(aType == bType) {
|
||||
return 0;
|
||||
} else if(aType == "circle" && bType == "svg") {
|
||||
return -1;
|
||||
} else if(bType == "circle" && aType == "svg") {
|
||||
return 1;
|
||||
}
|
||||
}, focusHandler: (component, focused) => {
|
||||
if(component.isEditor() && focused) {
|
||||
this.closeAllRooms();
|
||||
this.closeAccountMenu();
|
||||
}
|
||||
}});
|
||||
});
|
||||
}
|
||||
|
||||
$rootScope.$on("editorFocused", () => {
|
||||
this.initSvgForShortcut = function(shortcut) {
|
||||
var id = "dock-svg-" + shortcut.component.uuid;
|
||||
var element = document.getElementById(id);
|
||||
var parser = new DOMParser();
|
||||
var svg = shortcut.component.content.package_info.dock_icon.source;
|
||||
var doc = parser.parseFromString(svg, "image/svg+xml");
|
||||
element.appendChild(doc.documentElement);
|
||||
}
|
||||
|
||||
this.selectShortcut = function(shortcut) {
|
||||
componentManager.toggleComponent(shortcut.component);
|
||||
}
|
||||
|
||||
componentManager.registerHandler({identifier: "roomBar", areas: ["rooms", "modal"], activationHandler: (component) => {
|
||||
// RIP: There used to be code here that checked if component.active was true, and if so, displayed the component.
|
||||
// However, we no longer want to persist active state for footer extensions. If you open Extensions on one computer,
|
||||
// it shouldn't open on another computer. Active state should only be persisted for persistent extensions, like Folders.
|
||||
}, actionHandler: (component, action, data) => {
|
||||
if(action == "set-size") {
|
||||
component.setLastSize(data);
|
||||
}
|
||||
}, focusHandler: (component, focused) => {
|
||||
if(component.isEditor() && focused) {
|
||||
this.closeAllRooms();
|
||||
this.closeAccountMenu();
|
||||
})
|
||||
}
|
||||
}});
|
||||
|
||||
this.onRoomDismiss = function(room) {
|
||||
$rootScope.$on("editorFocused", () => {
|
||||
this.closeAllRooms();
|
||||
this.closeAccountMenu();
|
||||
})
|
||||
|
||||
this.onRoomDismiss = function(room) {
|
||||
room.showRoom = false;
|
||||
}
|
||||
|
||||
this.closeAllRooms = function() {
|
||||
for(var room of this.rooms) {
|
||||
room.showRoom = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.closeAllRooms = function() {
|
||||
for(var room of this.rooms) {
|
||||
room.showRoom = false;
|
||||
}
|
||||
this.selectRoom = async function(room) {
|
||||
let run = () => {
|
||||
$timeout(() => {
|
||||
room.showRoom = !room.showRoom;
|
||||
})
|
||||
}
|
||||
|
||||
this.selectRoom = async function(room) {
|
||||
let run = () => {
|
||||
$timeout(() => {
|
||||
room.showRoom = !room.showRoom;
|
||||
})
|
||||
}
|
||||
|
||||
if(!room.showRoom) {
|
||||
// About to show, check if has privileges
|
||||
if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageExtensions)) {
|
||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageExtensions, () => {
|
||||
run();
|
||||
});
|
||||
} else {
|
||||
if(!room.showRoom) {
|
||||
// About to show, check if has privileges
|
||||
if(await privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageExtensions)) {
|
||||
privilegesManager.presentPrivilegesModal(PrivilegesManager.ActionManageExtensions, () => {
|
||||
run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
this.clickOutsideAccountMenu = function() {
|
||||
if(privilegesManager.authenticationInProgress()) {
|
||||
return;
|
||||
}
|
||||
this.showAccountMenu = false;
|
||||
this.clickOutsideAccountMenu = function() {
|
||||
if(privilegesManager.authenticationInProgress()) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
this.showAccountMenu = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
angular.module('app')
|
||||
.controller('HomeCtrl', function ($scope, $location, $rootScope, $timeout, modelManager,
|
||||
dbManager, syncManager, authManager, themeManager, passcodeManager, storageManager, migrationManager,
|
||||
privilegesManager, statusManager, alertManager) {
|
||||
import _ from 'lodash';
|
||||
import { SFAuthManager } from 'snjs';
|
||||
import { getPlatformString } from '@/utils';
|
||||
import template from '%/home.pug';
|
||||
|
||||
export class Home {
|
||||
constructor() {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller(
|
||||
$scope,
|
||||
$location,
|
||||
$rootScope,
|
||||
$timeout,
|
||||
modelManager,
|
||||
dbManager,
|
||||
syncManager,
|
||||
authManager,
|
||||
themeManager,
|
||||
passcodeManager,
|
||||
storageManager,
|
||||
migrationManager,
|
||||
privilegesManager,
|
||||
statusManager,
|
||||
alertManager
|
||||
) {
|
||||
storageManager.initialize(passcodeManager.hasPasscode(), authManager.isEphemeralSession());
|
||||
|
||||
$scope.platform = getPlatformString();
|
||||
@@ -253,9 +276,9 @@ angular.module('app')
|
||||
$rootScope.safeApply = function(fn) {
|
||||
var phase = this.$root.$$phase;
|
||||
if(phase == '$apply' || phase == '$digest')
|
||||
this.$eval(fn);
|
||||
this.$eval(fn);
|
||||
else
|
||||
this.$apply(fn);
|
||||
this.$apply(fn);
|
||||
};
|
||||
|
||||
$rootScope.notifyDelete = function() {
|
||||
@@ -293,9 +316,9 @@ angular.module('app')
|
||||
}
|
||||
|
||||
/*
|
||||
Disable dragging and dropping of files into main SN interface.
|
||||
both 'dragover' and 'drop' are required to prevent dropping of files.
|
||||
This will not prevent extensions from receiving drop events.
|
||||
Disable dragging and dropping of files into main SN interface.
|
||||
both 'dragover' and 'drop' are required to prevent dropping of files.
|
||||
This will not prevent extensions from receiving drop events.
|
||||
*/
|
||||
window.addEventListener('dragover', (event) => {
|
||||
event.preventDefault();
|
||||
@@ -308,7 +331,7 @@ angular.module('app')
|
||||
|
||||
|
||||
/*
|
||||
Handle Auto Sign In From URL
|
||||
Handle Auto Sign In From URL
|
||||
*/
|
||||
|
||||
function urlParam(key) {
|
||||
@@ -341,4 +364,5 @@ angular.module('app')
|
||||
if(urlParam("server")) {
|
||||
autoSignInFromParams();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
6
app/assets/javascripts/app/controllers/index.js
Normal file
6
app/assets/javascripts/app/controllers/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export { EditorPanel } from './editor';
|
||||
export { Footer } from './footer';
|
||||
export { NotesPanel } from './notes';
|
||||
export { TagsPanel } from './tags';
|
||||
export { Home } from './home';
|
||||
export { LockScreen } from './lockScreen';
|
||||
@@ -1,16 +1,16 @@
|
||||
class LockScreen {
|
||||
import template from '%/lock-screen.pug';
|
||||
|
||||
export class LockScreen {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "lock-screen.html";
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
onSuccess: "&",
|
||||
};
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, passcodeManager, authManager, syncManager, storageManager, alertManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {};
|
||||
|
||||
this.visibilityObserver = passcodeManager.addVisibilityObserver((visible) => {
|
||||
@@ -53,5 +53,3 @@ class LockScreen {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('lockScreen', () => new LockScreen);
|
||||
|
||||
@@ -1,30 +1,45 @@
|
||||
angular.module('app')
|
||||
.directive("notesSection", function(){
|
||||
return {
|
||||
scope: {
|
||||
addNew: "&",
|
||||
selectionMade: "&",
|
||||
tag: "="
|
||||
},
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import { SFAuthManager } from 'snjs';
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import { KeyboardManager } from '@/services/keyboardManager';
|
||||
import template from '%/notes.pug';
|
||||
|
||||
templateUrl: 'notes.html',
|
||||
replace: true,
|
||||
controller: 'NotesCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
export class NotesPanel {
|
||||
constructor() {
|
||||
this.scope = {
|
||||
addNew: '&',
|
||||
selectionMade: '&',
|
||||
tag: '='
|
||||
};
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.tag', (tag, oldTag) => {
|
||||
if(tag) {
|
||||
ctrl.tagDidChange(tag, oldTag);
|
||||
}
|
||||
});
|
||||
this.template = template;
|
||||
this.replace = true;
|
||||
|
||||
this.controllerAs = 'ctrl';
|
||||
this.bindToController = true;
|
||||
}
|
||||
|
||||
link(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.tag', (tag, oldTag) => {
|
||||
if (tag) {
|
||||
ctrl.tagDidChange(tag, oldTag);
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager,
|
||||
syncManager, storageManager, desktopManager, privilegesManager, keyboardManager) {
|
||||
});
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller(
|
||||
authManager,
|
||||
$timeout,
|
||||
$rootScope,
|
||||
modelManager,
|
||||
syncManager,
|
||||
storageManager,
|
||||
desktopManager,
|
||||
privilegesManager,
|
||||
keyboardManager
|
||||
) {
|
||||
this.panelController = {};
|
||||
this.searchSubmitted = false;
|
||||
|
||||
@@ -671,5 +686,5 @@ angular.module('app')
|
||||
if(searchBar) {searchBar.focus()};
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
angular.module('app')
|
||||
.directive("tagsSection", function(){
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
addNew: "&",
|
||||
selectionMade: "&",
|
||||
save: "&",
|
||||
removeTag: "&"
|
||||
},
|
||||
templateUrl: 'tags.html',
|
||||
replace: true,
|
||||
controller: 'TagsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
}
|
||||
})
|
||||
.controller('TagsCtrl', function ($rootScope, modelManager, syncManager, $timeout, componentManager, authManager) {
|
||||
import { SNNote, SNSmartTag } from 'snjs';
|
||||
import template from '%/tags.pug';
|
||||
|
||||
export class TagsPanel {
|
||||
constructor() {
|
||||
this.restrict = 'E';
|
||||
this.scope = {
|
||||
addNew: '&',
|
||||
selectionMade: '&',
|
||||
save: '&',
|
||||
removeTag: '&'
|
||||
};
|
||||
this.template = template;
|
||||
this.replace = true;
|
||||
this.controllerAs = 'ctrl';
|
||||
this.bindToController = true;
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller(
|
||||
$rootScope,
|
||||
modelManager,
|
||||
syncManager,
|
||||
$timeout,
|
||||
componentManager,
|
||||
authManager
|
||||
) {
|
||||
// Wrap in timeout so that selectTag is defined
|
||||
$timeout(() => {
|
||||
this.smartTags = modelManager.getSmartTags();
|
||||
@@ -166,4 +175,5 @@ angular.module('app')
|
||||
this.removeTag()(tag);
|
||||
this.selectTag(this.smartTags[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive('snAutofocus', ['$timeout', function($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
shouldFocus: "="
|
||||
},
|
||||
link : function($scope, $element) {
|
||||
$timeout(function() {
|
||||
if($scope.shouldFocus) {
|
||||
$element[0].focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
/* @ngInject */
|
||||
export function autofocus($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
shouldFocus: '='
|
||||
},
|
||||
link: function($scope, $element) {
|
||||
$timeout(function() {
|
||||
if ($scope.shouldFocus) {
|
||||
$element[0].focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
angular.module('app').directive('clickOutside', ['$document', function($document) {
|
||||
/* @ngInject */
|
||||
export function clickOutside($document) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
replace: false,
|
||||
link: function($scope, $element, attrs) {
|
||||
|
||||
let didApplyClickOutside = false;
|
||||
var didApplyClickOutside = false;
|
||||
|
||||
$element.bind('click', function(e) {
|
||||
didApplyClickOutside = false;
|
||||
if(attrs.isOpen) {
|
||||
if (attrs.isOpen) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
$document.bind('click', function(event) {
|
||||
$document.bind('click', function() {
|
||||
// Ignore click if on SKAlert
|
||||
if(event.target.closest(".sk-modal")) {
|
||||
if (event.target.closest(".sk-modal")) {
|
||||
return;
|
||||
}
|
||||
if(!didApplyClickOutside) {
|
||||
|
||||
if (!didApplyClickOutside) {
|
||||
$scope.$apply(attrs.clickOutside);
|
||||
didApplyClickOutside = true;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,46 +1,44 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive('delayHide', function($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
show: '=',
|
||||
delay: '@'
|
||||
},
|
||||
link: function(scope, elem, attrs) {
|
||||
var showTimer;
|
||||
import angular from 'angular';
|
||||
|
||||
showElement(false);
|
||||
/* @ngInject */
|
||||
export function delayHide($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
show: '=',
|
||||
delay: '@'
|
||||
},
|
||||
link: function(scope, elem, attrs) {
|
||||
showElement(false);
|
||||
|
||||
//This is where all the magic happens!
|
||||
// Whenever the scope variable updates we simply
|
||||
// show if it evaluates to 'true' and hide if 'false'
|
||||
scope.$watch('show', function(newVal){
|
||||
newVal ? showSpinner() : hideSpinner();
|
||||
});
|
||||
// This is where all the magic happens!
|
||||
// Whenever the scope variable updates we simply
|
||||
// show if it evaluates to 'true' and hide if 'false'
|
||||
scope.$watch('show', function(newVal) {
|
||||
newVal ? showSpinner() : hideSpinner();
|
||||
});
|
||||
|
||||
function showSpinner() {
|
||||
if(scope.hidePromise) {
|
||||
$timeout.cancel(scope.hidePromise);
|
||||
scope.hidePromise = null;
|
||||
}
|
||||
showElement(true);
|
||||
}
|
||||
function showSpinner() {
|
||||
if (scope.hidePromise) {
|
||||
$timeout.cancel(scope.hidePromise);
|
||||
scope.hidePromise = null;
|
||||
}
|
||||
showElement(true);
|
||||
}
|
||||
|
||||
function hideSpinner() {
|
||||
scope.hidePromise = $timeout(showElement.bind(this, false), getDelay());
|
||||
}
|
||||
function hideSpinner() {
|
||||
scope.hidePromise = $timeout(showElement.bind(this, false), getDelay());
|
||||
}
|
||||
|
||||
function showElement(show) {
|
||||
show ? elem.css({display:''}) : elem.css({display:'none'});
|
||||
}
|
||||
function showElement(show) {
|
||||
show ? elem.css({ display: '' }) : elem.css({ display: 'none' });
|
||||
}
|
||||
|
||||
function getDelay() {
|
||||
var delay = parseInt(scope.delay);
|
||||
function getDelay() {
|
||||
var delay = parseInt(scope.delay);
|
||||
|
||||
return angular.isNumber(delay) ? delay : 200;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
});
|
||||
return angular.isNumber(delay) ? delay : 200;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive( 'elemReady', function( $parse ) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function( $scope, elem, attrs ) {
|
||||
elem.ready(function(){
|
||||
$scope.$apply(function(){
|
||||
var func = $parse(attrs.elemReady);
|
||||
func($scope);
|
||||
})
|
||||
})
|
||||
}
|
||||
/* @ngInject */
|
||||
export function elemReady($parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function($scope, elem, attrs) {
|
||||
elem.ready(function() {
|
||||
$scope.$apply(function() {
|
||||
var func = $parse(attrs.elemReady);
|
||||
func($scope);
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive('fileChange', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
handler: '&'
|
||||
},
|
||||
link: function (scope, element) {
|
||||
element.on('change', function (event) {
|
||||
scope.$apply(function(){
|
||||
scope.handler({files: event.target.files});
|
||||
/* @ngInject */
|
||||
export function fileChange() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
handler: '&'
|
||||
},
|
||||
link: function(scope, element) {
|
||||
element.on('change', function(event) {
|
||||
scope.$apply(function() {
|
||||
scope.handler({ files: event.target.files });
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export { autofocus } from './autofocus';
|
||||
export { clickOutside } from './click-outside';
|
||||
export { delayHide } from './delay-hide';
|
||||
export { elemReady } from './elemReady';
|
||||
export { fileChange } from './file-change';
|
||||
export { infiniteScroll } from './infiniteScroll';
|
||||
export { lowercase } from './lowercase';
|
||||
export { selectOnClick } from './selectOnClick';
|
||||
export { snEnter } from './snEnter';
|
||||
@@ -1,16 +1,18 @@
|
||||
angular.module('app').directive('infiniteScroll', [
|
||||
'$rootScope', '$window', '$timeout', function($rootScope, $window, $timeout) {
|
||||
/* @ngInject */
|
||||
export function infiniteScroll($rootScope, $window, $timeout) {
|
||||
return {
|
||||
link: function(scope, elem, attrs) {
|
||||
var offset = parseInt(attrs.threshold) || 0;
|
||||
var e = elem[0]
|
||||
var e = elem[0];
|
||||
|
||||
elem.on('scroll', function(){
|
||||
if(scope.$eval(attrs.canLoad) && e.scrollTop + e.offsetHeight >= e.scrollHeight - offset) {
|
||||
elem.on('scroll', function() {
|
||||
if (
|
||||
scope.$eval(attrs.canLoad) &&
|
||||
e.scrollTop + e.offsetHeight >= e.scrollHeight - offset
|
||||
) {
|
||||
scope.$apply(attrs.infiniteScroll);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive('lowercase', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, element, attrs, modelCtrl) {
|
||||
var lowercase = function(inputValue) {
|
||||
if (inputValue == undefined) inputValue = '';
|
||||
var lowercased = inputValue.toLowerCase();
|
||||
if (lowercased !== inputValue) {
|
||||
modelCtrl.$setViewValue(lowercased);
|
||||
modelCtrl.$render();
|
||||
}
|
||||
return lowercased;
|
||||
/* @ngInject */
|
||||
export function lowercase() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, element, attrs, modelCtrl) {
|
||||
var lowercase = function(inputValue) {
|
||||
if (inputValue === undefined) inputValue = '';
|
||||
var lowercased = inputValue.toLowerCase();
|
||||
if (lowercased !== inputValue) {
|
||||
modelCtrl.$setViewValue(lowercased);
|
||||
modelCtrl.$render();
|
||||
}
|
||||
modelCtrl.$parsers.push(lowercase);
|
||||
lowercase(scope[attrs.ngModel]);
|
||||
}
|
||||
};
|
||||
});
|
||||
return lowercased;
|
||||
};
|
||||
modelCtrl.$parsers.push(lowercase);
|
||||
lowercase(scope[attrs.ngModel]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive('selectOnClick', ['$window', function ($window) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function (scope, element, attrs) {
|
||||
element.on('focus', function () {
|
||||
if (!$window.getSelection().toString()) {
|
||||
// Required for mobile Safari
|
||||
this.setSelectionRange(0, this.value.length)
|
||||
}
|
||||
});
|
||||
/* @ngInject */
|
||||
export function selectOnClick($window) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
element.on('focus', function() {
|
||||
if (!$window.getSelection().toString()) {
|
||||
// Required for mobile Safari
|
||||
this.setSelectionRange(0, this.value.length);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
angular
|
||||
.module('app')
|
||||
.directive('snEnter', function() {
|
||||
/* @ngInject */
|
||||
export function snEnter() {
|
||||
return function(scope, element, attrs) {
|
||||
element.bind("keydown keypress", function(event) {
|
||||
if(event.which === 13) {
|
||||
scope.$apply(function(){
|
||||
scope.$eval(attrs.snEnter, {'event': event});
|
||||
element.bind('keydown keypress', function(event) {
|
||||
if (event.which === 13) {
|
||||
scope.$apply(function() {
|
||||
scope.$eval(attrs.snEnter, { event: event });
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,16 +1,34 @@
|
||||
class AccountMenu {
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import template from '%/directives/account-menu.pug';
|
||||
import { SNJS } from 'snjs';
|
||||
|
||||
export class AccountMenu {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/account-menu.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
"onSuccessfulAuth" : "&",
|
||||
"closeFunction" : "&"
|
||||
onSuccessfulAuth: '&',
|
||||
closeFunction: '&'
|
||||
};
|
||||
}
|
||||
|
||||
controller($scope, $rootScope, authManager, modelManager, syncManager, storageManager, dbManager, passcodeManager,
|
||||
$timeout, $compile, archiveManager, privilegesManager, appVersion, alertManager) {
|
||||
/* @ngInject */
|
||||
controller(
|
||||
$scope,
|
||||
$rootScope,
|
||||
authManager,
|
||||
modelManager,
|
||||
syncManager,
|
||||
storageManager,
|
||||
dbManager,
|
||||
passcodeManager,
|
||||
$timeout,
|
||||
$compile,
|
||||
archiveManager,
|
||||
privilegesManager,
|
||||
appVersion,
|
||||
alertManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.appVersion = "v" + (window.electronAppVersion || appVersion);
|
||||
@@ -506,8 +524,5 @@ class AccountMenu {
|
||||
$scope.isDesktopApplication = function() {
|
||||
return isDesktopApplication();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('accountMenu', () => new AccountMenu);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
class ActionsMenu {
|
||||
import template from '%/directives/actions-menu.pug';
|
||||
|
||||
export class ActionsMenu {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/actions-menu.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
item: "="
|
||||
item: '='
|
||||
};
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager, actionsManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.extensions = actionsManager.extensions.sort((a, b) => {
|
||||
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||
});
|
||||
@@ -81,7 +81,4 @@ class ActionsMenu {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('actionsMenu', () => new ActionsMenu);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
class ComponentModal {
|
||||
import template from '%/directives/component-modal.pug';
|
||||
|
||||
export class ComponentModal {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/component-modal.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
show: "=",
|
||||
component: "=",
|
||||
callback: "=",
|
||||
onDismiss: "&"
|
||||
show: '=',
|
||||
component: '=',
|
||||
callback: '=',
|
||||
onDismiss: '&'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,9 +16,8 @@ class ComponentModal {
|
||||
$scope.el = el;
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, $timeout, componentManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.dismiss = function(callback) {
|
||||
$scope.el.remove();
|
||||
$scope.$destroy();
|
||||
@@ -25,7 +25,4 @@ class ComponentModal {
|
||||
callback && callback();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('componentModal', () => new ComponentModal);
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
class ComponentView {
|
||||
import template from '%/directives/component-view.pug';
|
||||
|
||||
constructor($rootScope, componentManager, desktopManager, $timeout, themeManager) {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/component-view.html";
|
||||
import { isDesktopApplication } from '../../utils';
|
||||
|
||||
export class ComponentView {
|
||||
constructor(
|
||||
$rootScope,
|
||||
componentManager,
|
||||
desktopManager,
|
||||
$timeout,
|
||||
themeManager
|
||||
) {
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
component: "=",
|
||||
onLoad: "=?",
|
||||
manualDealloc: "=?"
|
||||
component: '=',
|
||||
onLoad: '=?',
|
||||
manualDealloc: '=?'
|
||||
};
|
||||
|
||||
this.desktopManager = desktopManager;
|
||||
@@ -28,9 +37,15 @@ class ComponentView {
|
||||
});
|
||||
}
|
||||
|
||||
controller($scope, $rootScope, $timeout, componentManager, desktopManager, themeManager) {
|
||||
'ngInject';
|
||||
|
||||
/* @ngInject */
|
||||
controller(
|
||||
$scope,
|
||||
$rootScope,
|
||||
$timeout,
|
||||
componentManager,
|
||||
desktopManager,
|
||||
themeManager
|
||||
) {
|
||||
$scope.onVisibilityChange = function() {
|
||||
if(document.visibilityState == "hidden") {
|
||||
return;
|
||||
@@ -261,7 +276,4 @@ class ComponentView {
|
||||
$scope.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('componentView', ($rootScope, componentManager, desktopManager, $timeout) => new ComponentView($rootScope, componentManager, desktopManager, $timeout));
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
and allow the user to choose which to keep (or to keep both.)
|
||||
*/
|
||||
|
||||
class ConflictResolutionModal {
|
||||
import template from '%/directives/conflict-resolution-modal.pug';
|
||||
|
||||
export class ConflictResolutionModal {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/conflict-resolution-modal.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
item1: "=",
|
||||
item2: "=",
|
||||
callback: "="
|
||||
item1: '=',
|
||||
item2: '=',
|
||||
callback: '='
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,9 +23,8 @@ class ConflictResolutionModal {
|
||||
}
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager, syncManager, archiveManager, alertManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.createContentString = function(item) {
|
||||
return JSON.stringify(
|
||||
Object.assign({created_at: item.created_at, updated_at: item.updated_at}, item.content), null, 2
|
||||
@@ -70,8 +70,5 @@ class ConflictResolutionModal {
|
||||
$scope.applyCallback = function() {
|
||||
$scope.callback && $scope.callback();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('conflictResolutionModal', () => new ConflictResolutionModal);
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
class EditorMenu {
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import template from '%/directives/editor-menu.pug';
|
||||
|
||||
export class EditorMenu {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/editor-menu.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
callback: "&",
|
||||
selectedEditor: "=",
|
||||
currentItem: "="
|
||||
callback: '&',
|
||||
selectedEditor: '=',
|
||||
currentItem: '='
|
||||
};
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, componentManager, syncManager, modelManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {};
|
||||
|
||||
$scope.editors = componentManager.componentsForArea("editor-editor").sort((a, b) => {
|
||||
@@ -82,7 +83,4 @@ class EditorMenu {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('editorMenu', () => new EditorMenu);
|
||||
|
||||
16
app/assets/javascripts/app/directives/views/index.js
Normal file
16
app/assets/javascripts/app/directives/views/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export { AccountMenu } from './accountMenu';
|
||||
export { ActionsMenu } from './actionsMenu';
|
||||
export { ComponentModal } from './componentModal';
|
||||
export { ComponentView } from './componentView';
|
||||
export { ConflictResolutionModal } from './conflictResolutionModal';
|
||||
export { EditorMenu } from './editorMenu';
|
||||
export { InputModal } from './inputModal';
|
||||
export { MenuRow } from './menuRow';
|
||||
export { PanelResizer } from './panelResizer';
|
||||
export { PasswordWizard } from './passwordWizard';
|
||||
export { PermissionsModal } from './permissionsModal';
|
||||
export { PrivilegesAuthModal } from './privilegesAuthModal';
|
||||
export { PrivilegesManagementModal } from './privilegesManagementModal';
|
||||
export { RevisionPreviewModal } from './revisionPreviewModal';
|
||||
export { SessionHistoryMenu } from './sessionHistoryMenu';
|
||||
export { SyncResolutionMenu } from './syncResolutionMenu';
|
||||
@@ -1,14 +1,15 @@
|
||||
class InputModal {
|
||||
import template from '%/directives/input-modal.pug';
|
||||
|
||||
export class InputModal {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/input-modal.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
type: "=",
|
||||
title: "=",
|
||||
message: "=",
|
||||
placeholder: "=",
|
||||
callback: "&"
|
||||
type: '=',
|
||||
title: '=',
|
||||
message: '=',
|
||||
placeholder: '=',
|
||||
callback: '&'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,9 +17,8 @@ class InputModal {
|
||||
$scope.el = el;
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager, archiveManager, authManager, syncManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {};
|
||||
|
||||
$scope.dismiss = function() {
|
||||
@@ -30,9 +30,5 @@ class InputModal {
|
||||
$scope.callback()($scope.formData.input);
|
||||
$scope.dismiss();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('inputModal', () => new InputModal);
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
class MenuRow {
|
||||
import template from '%/directives/menu-row.pug';
|
||||
|
||||
export class MenuRow {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.restrict = 'E';
|
||||
this.transclude = true;
|
||||
this.templateUrl = "directives/menu-row.html";
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
action: "&",
|
||||
circle: "=",
|
||||
circleAlign: "=",
|
||||
label: "=",
|
||||
subtitle: "=",
|
||||
hasButton: "=",
|
||||
buttonText: "=",
|
||||
buttonClass: "=",
|
||||
buttonAction: "&",
|
||||
spinnerClass: "=",
|
||||
subRows: "=",
|
||||
faded: "=",
|
||||
desc: "=",
|
||||
disabled: "=",
|
||||
stylekitClass: "="
|
||||
action: '&',
|
||||
circle: '=',
|
||||
circleAlign: '=',
|
||||
label: '=',
|
||||
subtitle: '=',
|
||||
hasButton: '=',
|
||||
buttonText: '=',
|
||||
buttonClass: '=',
|
||||
buttonAction: '&',
|
||||
spinnerClass: '=',
|
||||
subRows: '=',
|
||||
faded: '=',
|
||||
desc: '=',
|
||||
disabled: '=',
|
||||
stylekitClass: '='
|
||||
};
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, componentManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.onClick = function($event) {
|
||||
if($scope.disabled) {
|
||||
return;
|
||||
@@ -42,8 +42,5 @@ class MenuRow {
|
||||
$event.stopPropagation();
|
||||
$scope.buttonAction();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('menuRow', () => new MenuRow);
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
class PanelResizer {
|
||||
import angular from 'angular';
|
||||
import template from '%/directives/panel-resizer.pug';
|
||||
|
||||
export class PanelResizer {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/panel-resizer.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
index: "=",
|
||||
panelId: "=",
|
||||
onResize: "&",
|
||||
defaultWidth: "=",
|
||||
onResizeFinish: "&",
|
||||
control: "=",
|
||||
alwaysVisible: "=",
|
||||
minWidth: "=",
|
||||
property: "=",
|
||||
hoverable: "=",
|
||||
collapsable: "="
|
||||
index: '=',
|
||||
panelId: '=',
|
||||
onResize: '&',
|
||||
defaultWidth: '=',
|
||||
onResizeFinish: '&',
|
||||
control: '=',
|
||||
alwaysVisible: '=',
|
||||
minWidth: '=',
|
||||
property: '=',
|
||||
hoverable: '=',
|
||||
collapsable: '='
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,9 +40,8 @@ class PanelResizer {
|
||||
}
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, $element, modelManager, actionsManager, $timeout, $compile) {
|
||||
'ngInject';
|
||||
|
||||
let panel = document.getElementById($scope.panelId);
|
||||
if(!panel) {
|
||||
console.log("Panel not found for", $scope.panelId);
|
||||
@@ -288,8 +289,6 @@ class PanelResizer {
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('panelResizer', () => new PanelResizer);
|
||||
|
||||
/* via https://davidwalsh.name/javascript-debounce-function */
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
class PasswordWizard {
|
||||
import { SNJS } from 'snjs';
|
||||
import template from '%/directives/password-wizard.pug';
|
||||
|
||||
export class PasswordWizard {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/password-wizard.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
type: "="
|
||||
type: '='
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,8 +14,8 @@ class PasswordWizard {
|
||||
$scope.el = el;
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager, archiveManager, authManager, syncManager, $timeout, alertManager) {
|
||||
'ngInject';
|
||||
|
||||
window.onbeforeunload = (e) => {
|
||||
// Confirms with user to close tab before closing
|
||||
@@ -255,7 +257,4 @@ class PasswordWizard {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('passwordWizard', () => new PasswordWizard);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
class PermissionsModal {
|
||||
import template from '%/directives/permissions-modal.pug';
|
||||
|
||||
export class PermissionsModal {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/permissions-modal.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
show: "=",
|
||||
component: "=",
|
||||
permissionsString: "=",
|
||||
callback: "="
|
||||
show: '=',
|
||||
component: '=',
|
||||
permissionsString: '=',
|
||||
callback: '='
|
||||
};
|
||||
}
|
||||
|
||||
link($scope, el, attrs) {
|
||||
|
||||
$scope.dismiss = function() {
|
||||
el.remove();
|
||||
}
|
||||
@@ -28,11 +28,8 @@ class PermissionsModal {
|
||||
}
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager) {
|
||||
'ngInject';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('permissionsModal', () => new PermissionsModal);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
class PrivilegesAuthModal {
|
||||
import template from '%/directives/privileges-auth-modal.pug';
|
||||
|
||||
/* @ngInject */
|
||||
export class PrivilegesAuthModal {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/privileges-auth-modal.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
action: "=",
|
||||
onSuccess: "=",
|
||||
onCancel: "=",
|
||||
action: '=',
|
||||
onSuccess: '=',
|
||||
onCancel: '='
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,8 +87,5 @@ class PrivilegesAuthModal {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('privilegesAuthModal', () => new PrivilegesAuthModal);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
class PrivilegesManagementModal {
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import template from '%/directives/privileges-management-modal.pug';
|
||||
|
||||
export class PrivilegesManagementModal {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/privileges-management-modal.html";
|
||||
this.scope = {
|
||||
|
||||
};
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {};
|
||||
}
|
||||
|
||||
link($scope, el, attrs) {
|
||||
@@ -14,9 +14,8 @@ class PrivilegesManagementModal {
|
||||
}
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, privilegesManager, passcodeManager, authManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.dummy = {};
|
||||
|
||||
$scope.hasPasscode = passcodeManager.hasPasscode();
|
||||
@@ -84,5 +83,3 @@ class PrivilegesManagementModal {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('privilegesManagementModal', () => new PrivilegesManagementModal);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
class RevisionPreviewModal {
|
||||
import { SNJS, SNComponent, SFItem, SFModelManager } from 'snjs';
|
||||
import template from '%/directives/revision-preview-modal.pug';
|
||||
|
||||
export class RevisionPreviewModal {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/revision-preview-modal.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
uuid: "=",
|
||||
content: "="
|
||||
uuid: '=',
|
||||
content: '='
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,9 +15,8 @@ class RevisionPreviewModal {
|
||||
$scope.el = el;
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager, syncManager, componentManager, $timeout, alertManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.dismiss = function() {
|
||||
$scope.el.remove();
|
||||
$scope.$destroy();
|
||||
@@ -90,5 +91,3 @@ class RevisionPreviewModal {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('revisionPreviewModal', () => new RevisionPreviewModal);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
class SessionHistoryMenu {
|
||||
import template from '%/directives/session-history-menu.pug';
|
||||
|
||||
export class SessionHistoryMenu {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/session-history-menu.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
item: "="
|
||||
item: '='
|
||||
};
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager, sessionHistory, actionsManager, $timeout, alertManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.diskEnabled = sessionHistory.diskEnabled;
|
||||
$scope.autoOptimize = sessionHistory.autoOptimize;
|
||||
|
||||
@@ -85,9 +85,5 @@ class SessionHistoryMenu {
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').directive('sessionHistoryMenu', () => new SessionHistoryMenu);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
class SyncResolutionMenu {
|
||||
import template from '%/directives/sync-resolution-menu.pug';
|
||||
|
||||
export class SyncResolutionMenu {
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "directives/sync-resolution-menu.html";
|
||||
this.restrict = 'E';
|
||||
this.template = template;
|
||||
this.scope = {
|
||||
"closeFunction" : "&"
|
||||
closeFunction: '&'
|
||||
};
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
controller($scope, modelManager, syncManager, archiveManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.status = {};
|
||||
|
||||
$scope.close = function() {
|
||||
@@ -42,5 +42,3 @@ class SyncResolutionMenu {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').directive('syncResolutionMenu', () => new SyncResolutionMenu);
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
// reuse
|
||||
var locale, formatter;
|
||||
|
||||
angular.module('app')
|
||||
.filter('appDate', function ($filter) {
|
||||
return function (input) {
|
||||
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy', 'UTC') : '';
|
||||
};
|
||||
})
|
||||
.filter('appDateTime', function ($filter) {
|
||||
return function (input) {
|
||||
if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
|
||||
if (!formatter) {
|
||||
locale = (navigator.languages && navigator.languages.length) ? navigator.languages[0] : navigator.language;
|
||||
formatter = new Intl.DateTimeFormat(locale, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
}
|
||||
return formatter.format(input);
|
||||
} else {
|
||||
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy h:mm a') : '';
|
||||
}
|
||||
/* @ngInject */
|
||||
export function appDate($filter) {
|
||||
return function(input) {
|
||||
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy', 'UTC') : '';
|
||||
};
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
export function appDateTime($filter) {
|
||||
return function(input) {
|
||||
if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
|
||||
if (!formatter) {
|
||||
locale =
|
||||
navigator.languages && navigator.languages.length
|
||||
? navigator.languages[0]
|
||||
: navigator.language;
|
||||
formatter = new Intl.DateTimeFormat(locale, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
});
|
||||
return formatter.format(input);
|
||||
} else {
|
||||
return input ? $filter('date')(new Date(input), 'MM/dd/yyyy h:mm a') : '';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
2
app/assets/javascripts/app/filters/index.js
Normal file
2
app/assets/javascripts/app/filters/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { appDate, appDateTime } from './appDate';
|
||||
export { trusted } from './trusted';
|
||||
@@ -1,5 +1,6 @@
|
||||
angular.module('app').filter('trusted', ['$sce', function ($sce) {
|
||||
return function(url) {
|
||||
return $sce.trustAsResourceUrl(url);
|
||||
};
|
||||
}]);
|
||||
/* @ngInject */
|
||||
export function trusted($sce) {
|
||||
return function(url) {
|
||||
return $sce.trustAsResourceUrl(url);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
class NoteHistoryEntry extends SFItemHistoryEntry {
|
||||
import { SFItemHistoryEntry } from 'snjs';
|
||||
|
||||
export class NoteHistoryEntry extends SFItemHistoryEntry {
|
||||
|
||||
previewTitle() {
|
||||
return this.item.updated_at.toLocaleString();
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
angular.module('app')
|
||||
.config(function ($locationProvider) {
|
||||
import { isDesktopApplication } from './utils';
|
||||
|
||||
if(!isDesktopApplication()) {
|
||||
if (window.history && window.history.pushState) {
|
||||
$locationProvider.html5Mode({
|
||||
enabled: true,
|
||||
requireBase: false
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$locationProvider.html5Mode(false);
|
||||
/* @ngInject */
|
||||
export function configRoutes($locationProvider) {
|
||||
if (!isDesktopApplication()) {
|
||||
if (window.history && window.history.pushState) {
|
||||
$locationProvider.html5Mode({
|
||||
enabled: true,
|
||||
requireBase: false
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$locationProvider.html5Mode(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
class ActionsManager {
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import { Action, SFModelManager, SFItemParams, SNJS } from 'snjs';
|
||||
|
||||
constructor(httpManager, modelManager, authManager, syncManager, $rootScope, $compile, $timeout, alertManager) {
|
||||
export class ActionsManager {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
httpManager,
|
||||
modelManager,
|
||||
authManager,
|
||||
syncManager,
|
||||
$rootScope,
|
||||
$compile,
|
||||
$timeout,
|
||||
alertManager
|
||||
) {
|
||||
this.httpManager = httpManager;
|
||||
this.modelManager = modelManager;
|
||||
this.authManager = authManager;
|
||||
@@ -226,7 +240,4 @@ class ActionsManager {
|
||||
var el = this.$compile( "<input-modal type='type' message='message' title='title' callback='callback'></input-modal>" )(scope);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('actionsManager', ActionsManager);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
class AlertManager extends SFAlertManager {
|
||||
import { SFAlertManager } from 'snjs';
|
||||
import { SKAlert } from 'sn-stylekit';
|
||||
|
||||
export class AlertManager extends SFAlertManager {
|
||||
/* @ngInject */
|
||||
constructor($timeout) {
|
||||
super();
|
||||
this.$timeout = $timeout;
|
||||
@@ -15,7 +18,7 @@ class AlertManager extends SFAlertManager {
|
||||
resolve(true);
|
||||
}}
|
||||
]
|
||||
let alert = new Stylekit.SKAlert({title, text, buttons});
|
||||
let alert = new SKAlert({title, text, buttons});
|
||||
alert.present();
|
||||
})
|
||||
}
|
||||
@@ -37,11 +40,8 @@ class AlertManager extends SFAlertManager {
|
||||
}},
|
||||
];
|
||||
|
||||
let alert = new Stylekit.SKAlert({title, text, buttons});
|
||||
let alert = new SKAlert({title, text, buttons});
|
||||
alert.present();
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('alertManager', AlertManager);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class ArchiveManager {
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
|
||||
export class ArchiveManager {
|
||||
/* @ngInject */
|
||||
constructor(passcodeManager, authManager, modelManager, privilegesManager) {
|
||||
this.passcodeManager = passcodeManager;
|
||||
this.authManager = authManager;
|
||||
@@ -156,8 +158,4 @@ class ArchiveManager {
|
||||
link.click();
|
||||
link.remove();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('archiveManager', ArchiveManager);
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
class AuthManager extends SFAuthManager {
|
||||
import angular from 'angular';
|
||||
import { StorageManager } from './storageManager';
|
||||
import { SNJS, SFItem, SFPredicate, SFAuthManager } from 'snjs';
|
||||
|
||||
constructor(modelManager, singletonManager, storageManager, dbManager, httpManager, $rootScope, $timeout, $compile) {
|
||||
export class AuthManager extends SFAuthManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
modelManager,
|
||||
singletonManager,
|
||||
storageManager,
|
||||
dbManager,
|
||||
httpManager,
|
||||
$rootScope,
|
||||
$timeout,
|
||||
$compile
|
||||
) {
|
||||
super(storageManager, httpManager, null, $timeout);
|
||||
this.$rootScope = $rootScope;
|
||||
this.$compile = $compile;
|
||||
@@ -176,5 +189,3 @@ class AuthManager extends SFAuthManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('authManager', AuthManager);
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
class ComponentManager extends SNComponentManager {
|
||||
constructor(modelManager, syncManager, desktopManager, nativeExtManager, $rootScope, $timeout, $compile) {
|
||||
import angular from 'angular';
|
||||
import { SNComponentManager, SFAlertManager } from 'snjs';
|
||||
import { isDesktopApplication, getPlatformString } from '@/utils';
|
||||
|
||||
export class ComponentManager extends SNComponentManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
modelManager,
|
||||
syncManager,
|
||||
desktopManager,
|
||||
nativeExtManager,
|
||||
$rootScope,
|
||||
$timeout,
|
||||
$compile
|
||||
) {
|
||||
super({
|
||||
modelManager,
|
||||
syncManager,
|
||||
@@ -35,5 +48,3 @@ class ComponentManager extends SNComponentManager {
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('componentManager', ComponentManager);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
class DBManager {
|
||||
|
||||
export class DBManager {
|
||||
/* @ngInject */
|
||||
constructor(alertManager) {
|
||||
this.locked = true;
|
||||
this.alertManager;
|
||||
this.alertManager = alertManager;
|
||||
}
|
||||
|
||||
displayOfflineAlert() {
|
||||
@@ -179,5 +179,3 @@ class DBManager {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('dbManager', DBManager);
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
// An interface used by the Desktop app to interact with SN
|
||||
import _ from 'lodash';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { SFItemParams, SFModelManager } from 'snjs';
|
||||
|
||||
class DesktopManager {
|
||||
|
||||
constructor($rootScope, $timeout, modelManager, syncManager, authManager, passcodeManager) {
|
||||
export class DesktopManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$rootScope,
|
||||
$timeout,
|
||||
modelManager,
|
||||
syncManager,
|
||||
authManager,
|
||||
passcodeManager
|
||||
) {
|
||||
this.passcodeManager = passcodeManager;
|
||||
this.modelManager = modelManager;
|
||||
this.authManager = authManager;
|
||||
@@ -203,5 +213,3 @@ class DesktopManager {
|
||||
this.$rootScope.$broadcast("did-finish-local-backup", {success: success});
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('desktopManager', DesktopManager);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
class HttpManager extends SFHttpManager {
|
||||
import { SFHttpManager } from 'snjs';
|
||||
|
||||
export class HttpManager extends SFHttpManager {
|
||||
/* @ngInject */
|
||||
constructor(storageManager, $timeout) {
|
||||
// calling callbacks in a $timeout allows UI to update
|
||||
super($timeout);
|
||||
|
||||
this.setJWTRequestHandler(async () => {
|
||||
return storageManager.getItem("jwt");
|
||||
})
|
||||
return storageManager.getItem('jwt');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('httpManager', HttpManager);
|
||||
|
||||
20
app/assets/javascripts/app/services/index.js
Normal file
20
app/assets/javascripts/app/services/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export { ActionsManager } from './actionsManager';
|
||||
export { ArchiveManager } from './archiveManager';
|
||||
export { AuthManager } from './authManager';
|
||||
export { ComponentManager } from './componentManager';
|
||||
export { DBManager } from './dbManager';
|
||||
export { DesktopManager } from './desktopManager';
|
||||
export { HttpManager } from './httpManager';
|
||||
export { KeyboardManager } from './keyboardManager';
|
||||
export { MigrationManager } from './migrationManager';
|
||||
export { ModelManager } from './modelManager';
|
||||
export { NativeExtManager } from './nativeExtManager';
|
||||
export { PasscodeManager } from './passcodeManager';
|
||||
export { PrivilegesManager } from './privilegesManager';
|
||||
export { SessionHistory } from './sessionHistory';
|
||||
export { SingletonManager } from './singletonManager';
|
||||
export { StatusManager } from './statusManager';
|
||||
export { StorageManager } from './storageManager';
|
||||
export { SyncManager } from './syncManager';
|
||||
export { ThemeManager } from './themeManager';
|
||||
export { AlertManager } from './alertManager';
|
||||
@@ -1,4 +1,4 @@
|
||||
class KeyboardManager {
|
||||
export class KeyboardManager {
|
||||
|
||||
constructor() {
|
||||
this.observers = [];
|
||||
@@ -113,5 +113,3 @@ class KeyboardManager {
|
||||
this.observers.splice(this.observers.indexOf(observer), 1);
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('keyboardManager', KeyboardManager);
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
class MigrationManager extends SFMigrationManager {
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { SFMigrationManager } from 'snjs';
|
||||
import { ComponentManager } from '@/services/componentManager';
|
||||
|
||||
constructor($rootScope, modelManager, syncManager, componentManager, storageManager, statusManager, authManager, desktopManager) {
|
||||
export class MigrationManager extends SFMigrationManager {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
modelManager,
|
||||
syncManager,
|
||||
componentManager,
|
||||
storageManager,
|
||||
statusManager,
|
||||
authManager,
|
||||
desktopManager
|
||||
) {
|
||||
super(modelManager, syncManager, storageManager, authManager);
|
||||
this.componentManager = componentManager;
|
||||
this.statusManager = statusManager;
|
||||
@@ -157,5 +170,3 @@ class MigrationManager extends SFMigrationManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('migrationManager', MigrationManager);
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
SFItem,
|
||||
SFModelManager,
|
||||
SFPrivileges,
|
||||
SFPredicate,
|
||||
SNNote,
|
||||
SNTag,
|
||||
SNSmartTag,
|
||||
SNExtension,
|
||||
SNEditor,
|
||||
SNTheme,
|
||||
SNComponent,
|
||||
SNServerExtension,
|
||||
SNMfa
|
||||
} from 'snjs';
|
||||
|
||||
SFModelManager.ContentTypeClassMapping = {
|
||||
"Note" : SNNote,
|
||||
"Tag" : SNTag,
|
||||
@@ -11,10 +28,8 @@ SFModelManager.ContentTypeClassMapping = {
|
||||
"SN|Privileges" : SFPrivileges
|
||||
};
|
||||
|
||||
SFItem.AppDomain = "org.standardnotes.sn";
|
||||
|
||||
class ModelManager extends SFModelManager {
|
||||
|
||||
export class ModelManager extends SFModelManager {
|
||||
/* @ngInject */
|
||||
constructor(storageManager, $timeout) {
|
||||
super($timeout);
|
||||
this.notes = [];
|
||||
@@ -177,7 +192,4 @@ class ModelManager extends SFModelManager {
|
||||
"SN|FileSafe|Integration": "FileSafe integration"
|
||||
}[contentType];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('modelManager', ModelManager);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/* A class for handling installation of system extensions */
|
||||
|
||||
class NativeExtManager {
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { SFPredicate } from 'snjs';
|
||||
|
||||
export class NativeExtManager {
|
||||
/* @ngInject */
|
||||
constructor(modelManager, syncManager, singletonManager) {
|
||||
this.modelManager = modelManager;
|
||||
this.syncManager = syncManager;
|
||||
@@ -180,5 +183,3 @@ class NativeExtManager {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('nativeExtManager', NativeExtManager);
|
||||
|
||||
@@ -1,282 +1,285 @@
|
||||
import _ from 'lodash';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { StorageManager } from './storageManager';
|
||||
import { SNJS } from 'snjs';
|
||||
|
||||
const MillisecondsPerSecond = 1000;
|
||||
|
||||
class PasscodeManager {
|
||||
export class PasscodeManager {
|
||||
/* @ngInject */
|
||||
constructor($rootScope, authManager, storageManager, syncManager) {
|
||||
this.authManager = authManager;
|
||||
this.storageManager = storageManager;
|
||||
this.syncManager = syncManager;
|
||||
this.$rootScope = $rootScope;
|
||||
|
||||
constructor($rootScope, authManager, storageManager, syncManager) {
|
||||
this.authManager = authManager;
|
||||
this.storageManager = storageManager;
|
||||
this.syncManager = syncManager;
|
||||
this.$rootScope = $rootScope;
|
||||
this._hasPasscode = this.storageManager.getItemSync("offlineParams", StorageManager.Fixed) != null;
|
||||
this._locked = this._hasPasscode;
|
||||
|
||||
this._hasPasscode = this.storageManager.getItemSync("offlineParams", StorageManager.Fixed) != null;
|
||||
this._locked = this._hasPasscode;
|
||||
this.visibilityObservers = [];
|
||||
this.passcodeChangeObservers = [];
|
||||
|
||||
this.visibilityObservers = [];
|
||||
this.passcodeChangeObservers = [];
|
||||
this.configureAutoLock();
|
||||
}
|
||||
|
||||
this.configureAutoLock();
|
||||
addPasscodeChangeObserver(callback) {
|
||||
this.passcodeChangeObservers.push(callback);
|
||||
}
|
||||
|
||||
lockApplication() {
|
||||
window.location.reload();
|
||||
this.cancelAutoLockTimer();
|
||||
}
|
||||
|
||||
isLocked() {
|
||||
return this._locked;
|
||||
}
|
||||
|
||||
hasPasscode() {
|
||||
return this._hasPasscode;
|
||||
}
|
||||
|
||||
keys() {
|
||||
return this._keys;
|
||||
}
|
||||
|
||||
addVisibilityObserver(callback) {
|
||||
this.visibilityObservers.push(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
removeVisibilityObserver(callback) {
|
||||
_.pull(this.visibilityObservers, callback);
|
||||
}
|
||||
|
||||
notifiyVisibilityObservers(visible) {
|
||||
for(let callback of this.visibilityObservers) {
|
||||
callback(visible);
|
||||
}
|
||||
}
|
||||
|
||||
addPasscodeChangeObserver(callback) {
|
||||
this.passcodeChangeObservers.push(callback);
|
||||
async setAutoLockInterval(interval) {
|
||||
return this.storageManager.setItem(PasscodeManager.AutoLockIntervalKey, JSON.stringify(interval), StorageManager.FixedEncrypted);
|
||||
}
|
||||
|
||||
async getAutoLockInterval() {
|
||||
let interval = await this.storageManager.getItem(PasscodeManager.AutoLockIntervalKey, StorageManager.FixedEncrypted);
|
||||
if(interval) {
|
||||
return JSON.parse(interval);
|
||||
} else {
|
||||
return PasscodeManager.AutoLockIntervalNone;
|
||||
}
|
||||
}
|
||||
|
||||
lockApplication() {
|
||||
window.location.reload();
|
||||
this.cancelAutoLockTimer();
|
||||
}
|
||||
|
||||
isLocked() {
|
||||
return this._locked;
|
||||
}
|
||||
|
||||
hasPasscode() {
|
||||
return this._hasPasscode;
|
||||
}
|
||||
|
||||
keys() {
|
||||
return this._keys;
|
||||
}
|
||||
|
||||
addVisibilityObserver(callback) {
|
||||
this.visibilityObservers.push(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
removeVisibilityObserver(callback) {
|
||||
_.pull(this.visibilityObservers, callback);
|
||||
}
|
||||
|
||||
notifiyVisibilityObservers(visible) {
|
||||
for(let callback of this.visibilityObservers) {
|
||||
callback(visible);
|
||||
}
|
||||
}
|
||||
|
||||
async setAutoLockInterval(interval) {
|
||||
return this.storageManager.setItem(PasscodeManager.AutoLockIntervalKey, JSON.stringify(interval), StorageManager.FixedEncrypted);
|
||||
}
|
||||
|
||||
async getAutoLockInterval() {
|
||||
let interval = await this.storageManager.getItem(PasscodeManager.AutoLockIntervalKey, StorageManager.FixedEncrypted);
|
||||
if(interval) {
|
||||
return JSON.parse(interval);
|
||||
passcodeAuthParams() {
|
||||
var authParams = JSON.parse(this.storageManager.getItemSync("offlineParams", StorageManager.Fixed));
|
||||
if(authParams && !authParams.version) {
|
||||
var keys = this.keys();
|
||||
if(keys && keys.ak) {
|
||||
// If there's no version stored, and there's an ak, it has to be 002. Newer versions would have their version stored in authParams.
|
||||
authParams.version = "002";
|
||||
} else {
|
||||
return PasscodeManager.AutoLockIntervalNone;
|
||||
authParams.version = "001";
|
||||
}
|
||||
}
|
||||
return authParams;
|
||||
}
|
||||
|
||||
passcodeAuthParams() {
|
||||
var authParams = JSON.parse(this.storageManager.getItemSync("offlineParams", StorageManager.Fixed));
|
||||
if(authParams && !authParams.version) {
|
||||
var keys = this.keys();
|
||||
if(keys && keys.ak) {
|
||||
// If there's no version stored, and there's an ak, it has to be 002. Newer versions would have their version stored in authParams.
|
||||
authParams.version = "002";
|
||||
} else {
|
||||
authParams.version = "001";
|
||||
}
|
||||
}
|
||||
return authParams;
|
||||
}
|
||||
|
||||
async verifyPasscode(passcode) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
var params = this.passcodeAuthParams();
|
||||
let keys = await SNJS.crypto.computeEncryptionKeysForUser(passcode, params);
|
||||
if(keys.pw !== params.hash) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unlock(passcode, callback) {
|
||||
async verifyPasscode(passcode) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
var params = this.passcodeAuthParams();
|
||||
SNJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||
if(keys.pw !== params.hash) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this._keys = keys;
|
||||
this._authParams = params;
|
||||
this.decryptLocalStorage(keys, params).then(() => {
|
||||
this._locked = false;
|
||||
callback(true);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
setPasscode(passcode, callback) {
|
||||
var uuid = SNJS.crypto.generateUUIDSync();
|
||||
|
||||
SNJS.crypto.generateInitialKeysAndAuthParamsForUser(uuid, passcode).then((results) => {
|
||||
let keys = results.keys;
|
||||
let authParams = results.authParams;
|
||||
|
||||
authParams.hash = keys.pw;
|
||||
this._keys = keys;
|
||||
this._hasPasscode = true;
|
||||
this._authParams = authParams;
|
||||
|
||||
// Encrypting will initially clear localStorage
|
||||
this.encryptLocalStorage(keys, authParams);
|
||||
|
||||
// After it's cleared, it's safe to write to it
|
||||
this.storageManager.setItem("offlineParams", JSON.stringify(authParams), StorageManager.Fixed);
|
||||
callback(true);
|
||||
|
||||
this.notifyObserversOfPasscodeChange();
|
||||
});
|
||||
}
|
||||
|
||||
changePasscode(newPasscode, callback) {
|
||||
this.setPasscode(newPasscode, callback);
|
||||
}
|
||||
|
||||
clearPasscode() {
|
||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.Fixed); // Transfer from Ephemeral
|
||||
this.storageManager.removeItem("offlineParams", StorageManager.Fixed);
|
||||
this._keys = null;
|
||||
this._hasPasscode = false;
|
||||
|
||||
this.notifyObserversOfPasscodeChange();
|
||||
}
|
||||
|
||||
notifyObserversOfPasscodeChange() {
|
||||
for(var observer of this.passcodeChangeObservers) {
|
||||
observer();
|
||||
}
|
||||
}
|
||||
|
||||
encryptLocalStorage(keys, authParams) {
|
||||
this.storageManager.setKeys(keys, authParams);
|
||||
// Switch to Ephemeral storage, wiping Fixed storage
|
||||
// Last argument is `force`, which we set to true because in the case of changing passcode
|
||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true);
|
||||
}
|
||||
|
||||
async decryptLocalStorage(keys, authParams) {
|
||||
this.storageManager.setKeys(keys, authParams);
|
||||
return this.storageManager.decryptStorage();
|
||||
}
|
||||
|
||||
configureAutoLock() {
|
||||
PasscodeManager.AutoLockPollFocusInterval = 1 * MillisecondsPerSecond;
|
||||
|
||||
PasscodeManager.AutoLockIntervalNone = 0;
|
||||
PasscodeManager.AutoLockIntervalImmediate = 1;
|
||||
PasscodeManager.AutoLockIntervalOneMinute = 60 * MillisecondsPerSecond;
|
||||
PasscodeManager.AutoLockIntervalFiveMinutes = 300 * MillisecondsPerSecond;
|
||||
PasscodeManager.AutoLockIntervalOneHour = 3600 * MillisecondsPerSecond;
|
||||
|
||||
PasscodeManager.AutoLockIntervalKey = "AutoLockIntervalKey";
|
||||
|
||||
if(isDesktopApplication()) {
|
||||
// desktop only
|
||||
this.$rootScope.$on("window-lost-focus", () => {
|
||||
this.documentVisibilityChanged(false);
|
||||
})
|
||||
this.$rootScope.$on("window-gained-focus", () => {
|
||||
this.documentVisibilityChanged(true);
|
||||
})
|
||||
let keys = await SNJS.crypto.computeEncryptionKeysForUser(passcode, params);
|
||||
if(keys.pw !== params.hash) {
|
||||
resolve(false);
|
||||
} else {
|
||||
// tab visibility listener, web only
|
||||
document.addEventListener('visibilitychange', (e) => {
|
||||
let visible = document.visibilityState == "visible";
|
||||
this.documentVisibilityChanged(visible);
|
||||
});
|
||||
|
||||
// verify document is in focus every so often as visibilitychange event is not triggered
|
||||
// on a typical window blur event but rather on tab changes
|
||||
this.pollFocusTimeout = setInterval(() => {
|
||||
let hasFocus = document.hasFocus();
|
||||
|
||||
if(hasFocus && this.lastFocusState == "hidden") {
|
||||
this.documentVisibilityChanged(true);
|
||||
} else if(!hasFocus && this.lastFocusState == "visible") {
|
||||
this.documentVisibilityChanged(false);
|
||||
}
|
||||
|
||||
// save this to compare against next time around
|
||||
this.lastFocusState = hasFocus ? "visible" : "hidden";
|
||||
}, PasscodeManager.AutoLockPollFocusInterval);
|
||||
resolve(true);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getAutoLockIntervalOptions() {
|
||||
return [
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalNone,
|
||||
label: "Off"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalImmediate,
|
||||
label: "Immediately"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalOneMinute,
|
||||
label: "1m"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalFiveMinutes,
|
||||
label: "5m"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalOneHour,
|
||||
label: "1h"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
documentVisibilityChanged(visible) {
|
||||
if(visible) {
|
||||
// check to see if lockAfterDate is not null, and if the application isn't locked.
|
||||
// if that's the case, it needs to be locked immediately.
|
||||
if(this.lockAfterDate && new Date() > this.lockAfterDate && !this.isLocked()) {
|
||||
this.lockApplication();
|
||||
} else {
|
||||
if(!this.isLocked()) {
|
||||
this.syncManager.sync();
|
||||
}
|
||||
}
|
||||
this.cancelAutoLockTimer();
|
||||
} else {
|
||||
this.beginAutoLockTimer();
|
||||
}
|
||||
|
||||
this.notifiyVisibilityObservers(visible);
|
||||
}
|
||||
|
||||
async beginAutoLockTimer() {
|
||||
var interval = await this.getAutoLockInterval();
|
||||
if(interval == PasscodeManager.AutoLockIntervalNone) {
|
||||
unlock(passcode, callback) {
|
||||
var params = this.passcodeAuthParams();
|
||||
SNJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||
if(keys.pw !== params.hash) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a timeout if possible, but if the computer is put to sleep, timeouts won't work.
|
||||
// Need to set a date as backup. this.lockAfterDate does not need to be persisted, as
|
||||
// living in memory seems sufficient. If memory is cleared, then the application will lock anyway.
|
||||
let addToNow = (seconds) => {
|
||||
let date = new Date();
|
||||
date.setSeconds(date.getSeconds() + seconds);
|
||||
return date;
|
||||
this._keys = keys;
|
||||
this._authParams = params;
|
||||
this.decryptLocalStorage(keys, params).then(() => {
|
||||
this._locked = false;
|
||||
callback(true);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
setPasscode(passcode, callback) {
|
||||
var uuid = SNJS.crypto.generateUUIDSync();
|
||||
|
||||
SNJS.crypto.generateInitialKeysAndAuthParamsForUser(uuid, passcode).then((results) => {
|
||||
let keys = results.keys;
|
||||
let authParams = results.authParams;
|
||||
|
||||
authParams.hash = keys.pw;
|
||||
this._keys = keys;
|
||||
this._hasPasscode = true;
|
||||
this._authParams = authParams;
|
||||
|
||||
// Encrypting will initially clear localStorage
|
||||
this.encryptLocalStorage(keys, authParams);
|
||||
|
||||
// After it's cleared, it's safe to write to it
|
||||
this.storageManager.setItem("offlineParams", JSON.stringify(authParams), StorageManager.Fixed);
|
||||
callback(true);
|
||||
|
||||
this.notifyObserversOfPasscodeChange();
|
||||
});
|
||||
}
|
||||
|
||||
changePasscode(newPasscode, callback) {
|
||||
this.setPasscode(newPasscode, callback);
|
||||
}
|
||||
|
||||
clearPasscode() {
|
||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.Fixed); // Transfer from Ephemeral
|
||||
this.storageManager.removeItem("offlineParams", StorageManager.Fixed);
|
||||
this._keys = null;
|
||||
this._hasPasscode = false;
|
||||
|
||||
this.notifyObserversOfPasscodeChange();
|
||||
}
|
||||
|
||||
notifyObserversOfPasscodeChange() {
|
||||
for(var observer of this.passcodeChangeObservers) {
|
||||
observer();
|
||||
}
|
||||
}
|
||||
|
||||
encryptLocalStorage(keys, authParams) {
|
||||
this.storageManager.setKeys(keys, authParams);
|
||||
// Switch to Ephemeral storage, wiping Fixed storage
|
||||
// Last argument is `force`, which we set to true because in the case of changing passcode
|
||||
this.storageManager.setItemsMode(this.authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true);
|
||||
}
|
||||
|
||||
async decryptLocalStorage(keys, authParams) {
|
||||
this.storageManager.setKeys(keys, authParams);
|
||||
return this.storageManager.decryptStorage();
|
||||
}
|
||||
|
||||
configureAutoLock() {
|
||||
PasscodeManager.AutoLockPollFocusInterval = 1 * MillisecondsPerSecond;
|
||||
|
||||
PasscodeManager.AutoLockIntervalNone = 0;
|
||||
PasscodeManager.AutoLockIntervalImmediate = 1;
|
||||
PasscodeManager.AutoLockIntervalOneMinute = 60 * MillisecondsPerSecond;
|
||||
PasscodeManager.AutoLockIntervalFiveMinutes = 300 * MillisecondsPerSecond;
|
||||
PasscodeManager.AutoLockIntervalOneHour = 3600 * MillisecondsPerSecond;
|
||||
|
||||
PasscodeManager.AutoLockIntervalKey = "AutoLockIntervalKey";
|
||||
|
||||
if(isDesktopApplication()) {
|
||||
// desktop only
|
||||
this.$rootScope.$on("window-lost-focus", () => {
|
||||
this.documentVisibilityChanged(false);
|
||||
})
|
||||
this.$rootScope.$on("window-gained-focus", () => {
|
||||
this.documentVisibilityChanged(true);
|
||||
})
|
||||
} else {
|
||||
// tab visibility listener, web only
|
||||
document.addEventListener('visibilitychange', (e) => {
|
||||
let visible = document.visibilityState == "visible";
|
||||
this.documentVisibilityChanged(visible);
|
||||
});
|
||||
|
||||
// verify document is in focus every so often as visibilitychange event is not triggered
|
||||
// on a typical window blur event but rather on tab changes
|
||||
this.pollFocusTimeout = setInterval(() => {
|
||||
let hasFocus = document.hasFocus();
|
||||
|
||||
if(hasFocus && this.lastFocusState == "hidden") {
|
||||
this.documentVisibilityChanged(true);
|
||||
} else if(!hasFocus && this.lastFocusState == "visible") {
|
||||
this.documentVisibilityChanged(false);
|
||||
}
|
||||
|
||||
// save this to compare against next time around
|
||||
this.lastFocusState = hasFocus ? "visible" : "hidden";
|
||||
}, PasscodeManager.AutoLockPollFocusInterval);
|
||||
}
|
||||
}
|
||||
|
||||
getAutoLockIntervalOptions() {
|
||||
return [
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalNone,
|
||||
label: "Off"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalImmediate,
|
||||
label: "Immediately"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalOneMinute,
|
||||
label: "1m"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalFiveMinutes,
|
||||
label: "5m"
|
||||
},
|
||||
{
|
||||
value: PasscodeManager.AutoLockIntervalOneHour,
|
||||
label: "1h"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.lockAfterDate = addToNow(interval / MillisecondsPerSecond);
|
||||
this.lockTimeout = setTimeout(() => {
|
||||
documentVisibilityChanged(visible) {
|
||||
if(visible) {
|
||||
// check to see if lockAfterDate is not null, and if the application isn't locked.
|
||||
// if that's the case, it needs to be locked immediately.
|
||||
if(this.lockAfterDate && new Date() > this.lockAfterDate && !this.isLocked()) {
|
||||
this.lockApplication();
|
||||
// We don't need to look at this anymore since we've succeeded with timeout lock
|
||||
this.lockAfterDate = null;
|
||||
}, interval);
|
||||
} else {
|
||||
if(!this.isLocked()) {
|
||||
this.syncManager.sync();
|
||||
}
|
||||
}
|
||||
this.cancelAutoLockTimer();
|
||||
} else {
|
||||
this.beginAutoLockTimer();
|
||||
}
|
||||
|
||||
cancelAutoLockTimer() {
|
||||
clearTimeout(this.lockTimeout);
|
||||
this.notifiyVisibilityObservers(visible);
|
||||
}
|
||||
|
||||
async beginAutoLockTimer() {
|
||||
var interval = await this.getAutoLockInterval();
|
||||
if(interval == PasscodeManager.AutoLockIntervalNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a timeout if possible, but if the computer is put to sleep, timeouts won't work.
|
||||
// Need to set a date as backup. this.lockAfterDate does not need to be persisted, as
|
||||
// living in memory seems sufficient. If memory is cleared, then the application will lock anyway.
|
||||
let addToNow = (seconds) => {
|
||||
let date = new Date();
|
||||
date.setSeconds(date.getSeconds() + seconds);
|
||||
return date;
|
||||
}
|
||||
|
||||
this.lockAfterDate = addToNow(interval / MillisecondsPerSecond);
|
||||
this.lockTimeout = setTimeout(() => {
|
||||
this.lockApplication();
|
||||
// We don't need to look at this anymore since we've succeeded with timeout lock
|
||||
this.lockAfterDate = null;
|
||||
}
|
||||
}
|
||||
}, interval);
|
||||
}
|
||||
|
||||
angular.module('app').service('passcodeManager', PasscodeManager);
|
||||
cancelAutoLockTimer() {
|
||||
clearTimeout(this.lockTimeout);
|
||||
this.lockAfterDate = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
class PrivilegesManager extends SFPrivilegesManager {
|
||||
import angular from 'angular';
|
||||
import { SFPrivilegesManager } from 'snjs';
|
||||
|
||||
constructor(passcodeManager, authManager, syncManager, singletonManager, modelManager, storageManager, $rootScope, $compile) {
|
||||
export class PrivilegesManager extends SFPrivilegesManager {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
passcodeManager,
|
||||
authManager,
|
||||
syncManager,
|
||||
singletonManager,
|
||||
modelManager,
|
||||
storageManager,
|
||||
$rootScope,
|
||||
$compile
|
||||
) {
|
||||
super(modelManager, syncManager, singletonManager);
|
||||
|
||||
this.$rootScope = $rootScope;
|
||||
@@ -63,7 +76,4 @@ class PrivilegesManager extends SFPrivilegesManager {
|
||||
authenticationInProgress() {
|
||||
return this.currentAuthenticationElement != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('privilegesManager', PrivilegesManager);
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
class SessionHistory extends SFSessionHistoryManager {
|
||||
|
||||
constructor(modelManager, storageManager, authManager, passcodeManager, $timeout) {
|
||||
import { NoteHistoryEntry } from '@/models/noteHistoryEntry';
|
||||
import { SFSessionHistoryManager , SFItemHistory } from 'snjs';
|
||||
|
||||
export class SessionHistory extends SFSessionHistoryManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
modelManager,
|
||||
storageManager,
|
||||
authManager,
|
||||
passcodeManager,
|
||||
$timeout
|
||||
) {
|
||||
SFItemHistory.HistoryEntryClassMapping = {
|
||||
"Note" : NoteHistoryEntry
|
||||
}
|
||||
@@ -25,8 +33,12 @@ class SessionHistory extends SFSessionHistoryManager {
|
||||
}
|
||||
|
||||
var contentTypes = ["Note"];
|
||||
super(modelManager, storageManager, keyRequestHandler, contentTypes, $timeout);
|
||||
super(
|
||||
modelManager,
|
||||
storageManager,
|
||||
keyRequestHandler,
|
||||
contentTypes,
|
||||
$timeout
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('sessionHistory', SessionHistory);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
class SingletonManager extends SFSingletonManager {
|
||||
import { SFSingletonManager } from 'snjs';
|
||||
|
||||
export class SingletonManager extends SFSingletonManager {
|
||||
// constructor needed for angularjs injection to work
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
/* @ngInject */
|
||||
constructor(modelManager, syncManager) {
|
||||
super(modelManager, syncManager);
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('singletonManager', SingletonManager);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class StatusManager {
|
||||
import _ from 'lodash';
|
||||
|
||||
export class StatusManager {
|
||||
constructor() {
|
||||
this.statuses = [];
|
||||
this.observers = [];
|
||||
@@ -60,8 +61,4 @@ class StatusManager {
|
||||
removeStatusObserver(callback) {
|
||||
_.pull(this.statuses, callback);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('statusManager', StatusManager);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
class MemoryStorage {
|
||||
import { SNJS, SNEncryptedStorage, SFStorageManager , SFItemParams } from 'snjs';
|
||||
|
||||
export class MemoryStorage {
|
||||
constructor() {
|
||||
this.memory = {};
|
||||
}
|
||||
@@ -36,8 +38,9 @@ class MemoryStorage {
|
||||
}
|
||||
}
|
||||
|
||||
class StorageManager extends SFStorageManager {
|
||||
export class StorageManager extends SFStorageManager {
|
||||
|
||||
/* @ngInject */
|
||||
constructor(dbManager, alertManager) {
|
||||
super();
|
||||
this.dbManager = dbManager;
|
||||
@@ -251,5 +254,3 @@ class StorageManager extends SFStorageManager {
|
||||
StorageManager.FixedEncrypted = "FixedEncrypted"; // encrypted memoryStorage + localStorage persistence
|
||||
StorageManager.Ephemeral = "Ephemeral"; // memoryStorage
|
||||
StorageManager.Fixed = "Fixed"; // localStorage
|
||||
|
||||
angular.module('app').service('storageManager', StorageManager);
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
class SyncManager extends SFSyncManager {
|
||||
import angular from 'angular';
|
||||
import { SFSyncManager } from 'snjs';
|
||||
|
||||
constructor(modelManager, storageManager, httpManager, $timeout, $interval, $compile, $rootScope) {
|
||||
export class SyncManager extends SFSyncManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
modelManager,
|
||||
storageManager,
|
||||
httpManager,
|
||||
$timeout,
|
||||
$interval,
|
||||
$compile,
|
||||
$rootScope
|
||||
) {
|
||||
super(modelManager, storageManager, httpManager, $timeout, $interval);
|
||||
this.$rootScope = $rootScope;
|
||||
this.$compile = $compile;
|
||||
@@ -21,7 +32,4 @@ class SyncManager extends SFSyncManager {
|
||||
var el = this.$compile( "<conflict-resolution-modal item1='item1' item2='item2' callback='callback' class='sk-modal'></conflict-resolution-modal>" )(scope);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app').service('syncManager', SyncManager);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
class ThemeManager {
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import { SNTheme, SFItemParams } from 'snjs';
|
||||
import { StorageManager } from './storageManager';
|
||||
|
||||
export class ThemeManager {
|
||||
/* @ngInject */
|
||||
constructor(componentManager, desktopManager, storageManager, passcodeManager, $rootScope) {
|
||||
this.componentManager = componentManager;
|
||||
this.storageManager = storageManager;
|
||||
@@ -128,5 +133,3 @@ class ThemeManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app').service('themeManager', ThemeManager);
|
||||
|
||||
104
app/assets/javascripts/app/utils.js
Normal file
104
app/assets/javascripts/app/utils.js
Normal file
@@ -0,0 +1,104 @@
|
||||
export function getParameterByName(name, url) {
|
||||
name = name.replace(/[[\]]/g, '\\$&');
|
||||
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
|
||||
var results = regex.exec(url);
|
||||
if (!results) return null;
|
||||
if (!results[2]) return '';
|
||||
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
export function parametersFromURL(url) {
|
||||
url = url.split('?').slice(-1)[0];
|
||||
var obj = {};
|
||||
url.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
|
||||
obj[decodeURIComponent(key)] = decodeURIComponent(value);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function getPlatformString() {
|
||||
try {
|
||||
var platform = navigator.platform.toLowerCase();
|
||||
var trimmed = '';
|
||||
if (platform.indexOf('mac') !== -1) {
|
||||
trimmed = 'mac';
|
||||
} else if (platform.indexOf('win') !== -1) {
|
||||
trimmed = 'windows';
|
||||
}
|
||||
if (platform.indexOf('linux') !== -1) {
|
||||
trimmed = 'linux';
|
||||
}
|
||||
|
||||
return trimmed + (isDesktopApplication() ? '-desktop' : '-web');
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function isDesktopApplication() {
|
||||
return window.isElectron;
|
||||
}
|
||||
|
||||
/* Use with numbers and strings, not objects */
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.containsPrimitiveSubset = function(array) {
|
||||
return !array.some(val => this.indexOf(val) === -1);
|
||||
};
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
|
||||
if (!Array.prototype.includes) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Object.defineProperty(Array.prototype, 'includes', {
|
||||
value: function(searchElement, fromIndex) {
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
var o = Object(this);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
var len = o.length >>> 0;
|
||||
|
||||
// 3. If len is 0, return false.
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Let n be ? ToInteger(fromIndex).
|
||||
// (If fromIndex is undefined, this step produces the value 0.)
|
||||
var n = fromIndex | 0;
|
||||
|
||||
// 5. If n ≥ 0, then
|
||||
// a. Let k be n.
|
||||
// 6. Else n < 0,
|
||||
// a. Let k be len + n.
|
||||
// b. If k < 0, let k be 0.
|
||||
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
||||
|
||||
function sameValueZero(x, y) {
|
||||
return (
|
||||
x === y ||
|
||||
(typeof x === 'number' &&
|
||||
typeof y === 'number' &&
|
||||
isNaN(x) &&
|
||||
isNaN(y))
|
||||
);
|
||||
}
|
||||
|
||||
// 7. Repeat, while k < len
|
||||
while (k < len) {
|
||||
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
|
||||
// b. If SameValueZero(searchElement, elementK) is true, return true.
|
||||
if (sameValueZero(o[k], searchElement)) {
|
||||
return true;
|
||||
}
|
||||
// c. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 8. Return false
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1 +1,22 @@
|
||||
//= require_tree ./app
|
||||
|
||||
// css
|
||||
import 'sn-stylekit/dist/stylekit.css';
|
||||
import '../stylesheets/main.css.scss';
|
||||
|
||||
// Vendor
|
||||
import 'angular';
|
||||
import '../../../vendor/assets/javascripts/angular-sanitize';
|
||||
import '../../../vendor/assets/javascripts/zip/deflate';
|
||||
import '../../../vendor/assets/javascripts/zip/inflate';
|
||||
import '../../../vendor/assets/javascripts/zip/zip';
|
||||
import '../../../vendor/assets/javascripts/zip/z-worker';
|
||||
|
||||
import { SFItem } from 'snjs';
|
||||
|
||||
// Set the app domain before starting the app
|
||||
SFItem.AppDomain = 'org.standardnotes.sn';
|
||||
|
||||
// entry point
|
||||
// eslint-disable-next-line import/first
|
||||
import './app/app';
|
||||
|
||||
Reference in New Issue
Block a user