Merge branch 'master' into after-delete-select-first
@@ -86,7 +86,6 @@ module.exports = function(grunt) {
|
||||
'vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.js',
|
||||
'vendor/assets/bower_components/lodash/dist/lodash.min.js',
|
||||
'vendor/assets/bower_components/restangular/dist/restangular.js',
|
||||
'vendor/assets/bower_components/marked/lib/marked.js',
|
||||
'vendor/assets/javascripts/crypto/*.js'
|
||||
],
|
||||
dest: 'vendor/assets/javascripts/lib.js',
|
||||
|
||||
@@ -15,87 +15,64 @@ angular.module('app.frontend')
|
||||
bindToController: true,
|
||||
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
|
||||
/**
|
||||
* Insert 4 spaces when a tab key is pressed,
|
||||
* only used when inside of the text editor.
|
||||
* If the shift key is pressed first, this event is
|
||||
* not fired.
|
||||
*/
|
||||
var handleTab = function (event) {
|
||||
if (!event.shiftKey && event.which == 9) {
|
||||
event.preventDefault();
|
||||
var start = this.selectionStart;
|
||||
var end = this.selectionEnd;
|
||||
var spaces = " ";
|
||||
|
||||
// Insert 4 spaces
|
||||
this.value = this.value.substring(0, start)
|
||||
+ spaces + this.value.substring(end);
|
||||
|
||||
// Place cursor 4 spaces away from where
|
||||
// the tab key was pressed
|
||||
this.selectionStart = this.selectionEnd = start + 4;
|
||||
}
|
||||
}
|
||||
|
||||
var handler = function(event) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
switch (String.fromCharCode(event.which).toLowerCase()) {
|
||||
case 's':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
ctrl.saveNote(event);
|
||||
});
|
||||
break;
|
||||
case 'e':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
ctrl.clickedEditNote();
|
||||
})
|
||||
break;
|
||||
case 'm':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
ctrl.toggleMarkdown();
|
||||
})
|
||||
break;
|
||||
case 'o':
|
||||
event.preventDefault();
|
||||
$timeout(function(){
|
||||
ctrl.toggleFullScreen();
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handler);
|
||||
var element = document.getElementById("note-text-editor");
|
||||
element.addEventListener('keydown', handleTab);
|
||||
|
||||
scope.$on('$destroy', function(){
|
||||
window.removeEventListener('keydown', handler);
|
||||
})
|
||||
|
||||
scope.$watch('ctrl.note', function(note, oldNote){
|
||||
if(note) {
|
||||
ctrl.setNote(note, oldNote);
|
||||
} else {
|
||||
ctrl.note = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.controller('EditorCtrl', function ($sce, $timeout, authManager, markdownRenderer, $rootScope, extensionManager, syncManager) {
|
||||
.controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, extensionManager, syncManager, modelManager) {
|
||||
|
||||
window.addEventListener("message", function(event){
|
||||
if(event.data.status) {
|
||||
this.postNoteToExternalEditor();
|
||||
} else {
|
||||
var id = event.data.id;
|
||||
var text = event.data.text;
|
||||
var data = event.data.data;
|
||||
|
||||
if(this.note.uuid === id) {
|
||||
this.note.text = text;
|
||||
if(data) {
|
||||
var changesMade = this.customEditor.setData(id, data);
|
||||
if(changesMade) {
|
||||
this.customEditor.setDirty(true);
|
||||
}
|
||||
}
|
||||
this.changesMade();
|
||||
}
|
||||
}
|
||||
}.bind(this), false);
|
||||
|
||||
this.setNote = function(note, oldNote) {
|
||||
this.editorMode = 'edit';
|
||||
var currentEditor = this.customEditor;
|
||||
this.customEditor = null;
|
||||
this.showExtensions = false;
|
||||
this.showMenu = false;
|
||||
this.loadTagsString();
|
||||
|
||||
var setEditor = function(editor) {
|
||||
this.customEditor = editor;
|
||||
this.postNoteToExternalEditor();
|
||||
}.bind(this)
|
||||
|
||||
var editor = this.editorForNote(note);
|
||||
if(editor) {
|
||||
if(currentEditor !== editor) {
|
||||
// switch after timeout, so that note data isnt posted to current editor
|
||||
$timeout(function(){
|
||||
setEditor(editor);
|
||||
}.bind(this));
|
||||
} else {
|
||||
// switch immediately
|
||||
setEditor(editor);
|
||||
}
|
||||
} else {
|
||||
this.customEditor = null;
|
||||
}
|
||||
|
||||
if(note.safeText().length == 0 && note.dummy) {
|
||||
this.focusTitle(100);
|
||||
}
|
||||
@@ -109,19 +86,50 @@ angular.module('app.frontend')
|
||||
}
|
||||
}
|
||||
|
||||
this.hasAvailableExtensions = function() {
|
||||
return extensionManager.extensionsInContextOfItem(this.note).length > 0;
|
||||
this.selectedEditor = function(editor) {
|
||||
this.showEditorMenu = false;
|
||||
|
||||
if(this.customEditor && editor !== this.customEditor) {
|
||||
this.customEditor.removeItemAsRelationship(this.note);
|
||||
this.customEditor.setDirty(true);
|
||||
}
|
||||
|
||||
if(editor.default) {
|
||||
this.customEditor = null;
|
||||
} else {
|
||||
this.customEditor = editor;
|
||||
this.customEditor.addItemAsRelationship(this.note);
|
||||
this.customEditor.setDirty(true);
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
this.editorForNote = function(note) {
|
||||
var editors = modelManager.itemsForContentType("SN|Editor");
|
||||
for(var editor of editors) {
|
||||
if(_.includes(editor.notes, note)) {
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
this.onPreviewDoubleClick = function() {
|
||||
this.editorMode = 'edit';
|
||||
this.focusEditor(100);
|
||||
this.postNoteToExternalEditor = function() {
|
||||
var externalEditorElement = document.getElementById("editor-iframe");
|
||||
if(externalEditorElement) {
|
||||
externalEditorElement.contentWindow.postMessage({text: this.note.text, data: this.customEditor.dataForKey(this.note.uuid), id: this.note.uuid}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
this.hasAvailableExtensions = function() {
|
||||
return extensionManager.extensionsInContextOfItem(this.note).length > 0;
|
||||
}
|
||||
|
||||
this.focusEditor = function(delay) {
|
||||
setTimeout(function(){
|
||||
var element = document.getElementById("note-text-editor");
|
||||
element.focus();
|
||||
if(element) {
|
||||
element.focus();
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
|
||||
@@ -135,10 +143,6 @@ angular.module('app.frontend')
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
this.renderedContent = function() {
|
||||
return markdownRenderer.renderHtml(markdownRenderer.renderedContentForText(this.note.safeText()));
|
||||
}
|
||||
|
||||
var statusTimeout;
|
||||
|
||||
this.saveNote = function($event) {
|
||||
@@ -198,7 +202,6 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
this.onContentFocus = function() {
|
||||
this.showSampler = false;
|
||||
$rootScope.$broadcast("editorFocused");
|
||||
}
|
||||
|
||||
@@ -209,12 +212,7 @@ angular.module('app.frontend')
|
||||
this.toggleFullScreen = function() {
|
||||
this.fullscreen = !this.fullscreen;
|
||||
if(this.fullscreen) {
|
||||
if(this.editorMode == 'edit') {
|
||||
// refocus
|
||||
this.focusEditor(0);
|
||||
}
|
||||
} else {
|
||||
|
||||
this.focusEditor(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,19 +220,6 @@ angular.module('app.frontend')
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
this.toggleMarkdown = function() {
|
||||
if(this.editorMode == 'preview') {
|
||||
this.editorMode = 'edit';
|
||||
this.focusEditor(0);
|
||||
} else {
|
||||
this.editorMode = 'preview';
|
||||
}
|
||||
}
|
||||
|
||||
this.clickedMenu = function() {
|
||||
this.showMenu = !this.showMenu;
|
||||
}
|
||||
|
||||
this.deleteNote = function() {
|
||||
if(confirm("Are you sure you want to delete this note?")) {
|
||||
this.remove()(this.note);
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
angular.module('app.frontend')
|
||||
.controller('HomeCtrl', function ($scope, $rootScope, $timeout, modelManager, syncManager, authManager) {
|
||||
$rootScope.bodyClass = "app-body-class";
|
||||
.controller('HomeCtrl', function ($scope, $stateParams, $rootScope, $timeout, modelManager, syncManager, authManager) {
|
||||
|
||||
function autoSignInFromParams() {
|
||||
if(!authManager.offline()) {
|
||||
// check if current account
|
||||
if(syncManager.serverURL == $stateParams.server && authManager.user.email == $stateParams.email) {
|
||||
// already signed in, return
|
||||
return;
|
||||
} else {
|
||||
// sign out
|
||||
syncManager.destroyLocalData(function(){
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
} else {
|
||||
authManager.login($stateParams.server, $stateParams.email, $stateParams.pw, function(response){
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if($stateParams.server && $stateParams.email) {
|
||||
autoSignInFromParams();
|
||||
}
|
||||
|
||||
syncManager.loadLocalItems(function(items) {
|
||||
$scope.allTag.didLoad = true;
|
||||
$scope.$apply();
|
||||
|
||||
syncManager.sync(null);
|
||||
@@ -12,7 +35,9 @@ angular.module('app.frontend')
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
$scope.allTag = new Tag({all: true});
|
||||
var allTag = new Tag({all: true});
|
||||
allTag.needsLoad = true;
|
||||
$scope.allTag = allTag;
|
||||
$scope.allTag.title = "All";
|
||||
$scope.tags = modelManager.tags;
|
||||
$scope.allTag.notes = modelManager.notes;
|
||||
@@ -31,6 +56,7 @@ angular.module('app.frontend')
|
||||
modelManager.createRelationshipBetweenItems(note, tag);
|
||||
}
|
||||
|
||||
note.setDirty(true);
|
||||
syncManager.sync();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,16 @@ angular.module('app.frontend')
|
||||
link:function(scope, elem, attrs, ctrl) {
|
||||
scope.$watch('ctrl.tag', function(tag, oldTag){
|
||||
if(tag) {
|
||||
ctrl.tagDidChange(tag, oldTag);
|
||||
if(tag.needsLoad) {
|
||||
scope.$watch('ctrl.tag.didLoad', function(didLoad){
|
||||
if(didLoad) {
|
||||
tag.needsLoad = false;
|
||||
ctrl.tagDidChange(tag, oldTag);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ctrl.tagDidChange(tag, oldTag);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -26,6 +35,8 @@ angular.module('app.frontend')
|
||||
})
|
||||
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager) {
|
||||
|
||||
this.sortBy = localStorage.getItem("sortBy") || "created_at";
|
||||
|
||||
$rootScope.$on("editorFocused", function(){
|
||||
this.showMenu = false;
|
||||
}.bind(this))
|
||||
@@ -34,8 +45,6 @@ angular.module('app.frontend')
|
||||
this.selectFirstNote(false);
|
||||
}.bind(this))
|
||||
|
||||
var isFirstLoad = true;
|
||||
|
||||
this.notesToDisplay = 20;
|
||||
this.paginate = function() {
|
||||
this.notesToDisplay += 20
|
||||
@@ -49,20 +58,16 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
this.noteFilter.text = "";
|
||||
this.setNotes(tag.notes);
|
||||
}
|
||||
|
||||
tag.notes.forEach(function(note){
|
||||
this.setNotes = function(notes) {
|
||||
notes.forEach(function(note){
|
||||
note.visible = true;
|
||||
})
|
||||
this.selectFirstNote(false);
|
||||
|
||||
if(isFirstLoad) {
|
||||
$timeout(function(){
|
||||
this.createNewNote();
|
||||
isFirstLoad = false;
|
||||
}.bind(this))
|
||||
} else if(tag.notes.length == 0) {
|
||||
this.createNewNote();
|
||||
}
|
||||
var createNew = notes.length == 0;
|
||||
this.selectFirstNote(createNew);
|
||||
}
|
||||
|
||||
this.selectedTagDelete = function() {
|
||||
@@ -71,7 +76,7 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
this.selectFirstNote = function(createNew) {
|
||||
var visibleNotes = this.tag.notes.filter(function(note){
|
||||
var visibleNotes = this.sortedNotes.filter(function(note){
|
||||
return note.visible;
|
||||
});
|
||||
|
||||
@@ -114,4 +119,22 @@ angular.module('app.frontend')
|
||||
}
|
||||
}.bind(this), 100)
|
||||
}
|
||||
|
||||
this.selectedMenuItem = function() {
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
this.selectedSortByCreated = function() {
|
||||
this.setSortBy("created_at");
|
||||
}
|
||||
|
||||
this.selectedSortByUpdated = function() {
|
||||
this.setSortBy("updated_at");
|
||||
}
|
||||
|
||||
this.setSortBy = function(type) {
|
||||
this.sortBy = type;
|
||||
localStorage.setItem("sortBy", type);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -37,6 +37,7 @@ class Item {
|
||||
|
||||
updateFromJSON(json) {
|
||||
_.merge(this, json);
|
||||
|
||||
if(this.created_at) {
|
||||
this.created_at = new Date(this.created_at);
|
||||
this.updated_at = new Date(this.updated_at);
|
||||
@@ -112,6 +113,10 @@ class Item {
|
||||
this.setDirty(true);
|
||||
}
|
||||
|
||||
locallyClearAllReferences() {
|
||||
|
||||
}
|
||||
|
||||
mergeMetadataFromItem(item) {
|
||||
_.merge(this, _.omit(item, ["content"]));
|
||||
}
|
||||
|
||||
89
app/assets/javascripts/app/frontend/models/app/editor.js
Normal file
@@ -0,0 +1,89 @@
|
||||
class Editor extends Item {
|
||||
|
||||
constructor(json_obj) {
|
||||
super(json_obj);
|
||||
if(!this.notes) {
|
||||
this.notes = [];
|
||||
}
|
||||
if(!this.data) {
|
||||
this.data = {};
|
||||
}
|
||||
}
|
||||
|
||||
mapContentToLocalProperties(contentObject) {
|
||||
super.mapContentToLocalProperties(contentObject)
|
||||
this.url = contentObject.url;
|
||||
this.name = contentObject.name;
|
||||
this.data = contentObject.data || {};
|
||||
}
|
||||
|
||||
structureParams() {
|
||||
var params = {
|
||||
url: this.url,
|
||||
name: this.name,
|
||||
data: this.data
|
||||
};
|
||||
|
||||
_.merge(params, super.structureParams());
|
||||
return params;
|
||||
}
|
||||
|
||||
referenceParams() {
|
||||
var references = _.map(this.notes, function(note){
|
||||
return {uuid: note.uuid, content_type: note.content_type};
|
||||
})
|
||||
|
||||
return references;
|
||||
}
|
||||
|
||||
addItemAsRelationship(item) {
|
||||
if(item.content_type == "Note") {
|
||||
if(!_.find(this.notes, item)) {
|
||||
this.notes.push(item);
|
||||
}
|
||||
}
|
||||
super.addItemAsRelationship(item);
|
||||
}
|
||||
|
||||
removeItemAsRelationship(item) {
|
||||
if(item.content_type == "Note") {
|
||||
_.pull(this.notes, item);
|
||||
}
|
||||
super.removeItemAsRelationship(item);
|
||||
}
|
||||
|
||||
removeAllRelationships() {
|
||||
super.removeAllRelationships();
|
||||
this.notes = [];
|
||||
}
|
||||
|
||||
locallyClearAllReferences() {
|
||||
super.locallyClearAllReferences();
|
||||
this.notes = [];
|
||||
}
|
||||
|
||||
allReferencedObjects() {
|
||||
return this.notes;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {uuid: this.uuid}
|
||||
}
|
||||
|
||||
get content_type() {
|
||||
return "SN|Editor";
|
||||
}
|
||||
|
||||
setData(key, value) {
|
||||
var dataHasChanged = JSON.stringify(this.data[key]) !== JSON.stringify(value);
|
||||
if(dataHasChanged) {
|
||||
this.data[key] = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
dataForKey(key) {
|
||||
return this.data[key] || {};
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,7 @@ class Extension extends Item {
|
||||
super.mapContentToLocalProperties(contentObject)
|
||||
this.name = contentObject.name;
|
||||
this.url = contentObject.url;
|
||||
this.supported_types = contentObject.supported_types;
|
||||
this.actions = contentObject.actions.map(function(action){
|
||||
return new Action(action);
|
||||
})
|
||||
@@ -99,7 +100,8 @@ class Extension extends Item {
|
||||
var params = {
|
||||
name: this.name,
|
||||
url: this.url,
|
||||
actions: this.actions
|
||||
actions: this.actions,
|
||||
supported_types: this.supported_types
|
||||
};
|
||||
|
||||
_.merge(params, super.structureParams());
|
||||
|
||||
@@ -56,6 +56,14 @@ class Note extends Item {
|
||||
this.tags = [];
|
||||
}
|
||||
|
||||
locallyClearAllReferences() {
|
||||
super.locallyClearAllReferences();
|
||||
this.tags.forEach(function(tag){
|
||||
_.pull(tag.notes, this);
|
||||
}.bind(this))
|
||||
this.tags = [];
|
||||
}
|
||||
|
||||
isBeingRemovedLocally() {
|
||||
this.tags.forEach(function(tag){
|
||||
_.pull(tag.notes, this);
|
||||
|
||||
@@ -55,6 +55,15 @@ class Tag extends Item {
|
||||
this.notes = [];
|
||||
}
|
||||
|
||||
locallyClearAllReferences() {
|
||||
super.locallyClearAllReferences();
|
||||
this.notes.forEach(function(note){
|
||||
_.pull(note.tags, this);
|
||||
}.bind(this))
|
||||
|
||||
this.notes = [];
|
||||
}
|
||||
|
||||
isBeingRemovedLocally() {
|
||||
this.notes.forEach(function(note){
|
||||
_.pull(note.tags, this);
|
||||
|
||||
@@ -7,7 +7,7 @@ angular.module('app.frontend')
|
||||
})
|
||||
|
||||
.state('home', {
|
||||
url: '/',
|
||||
url: '/?server&email&pw',
|
||||
parent: 'base',
|
||||
views: {
|
||||
'content@' : {
|
||||
|
||||
@@ -73,13 +73,12 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: password}, authParams), function(keys){
|
||||
var mk = keys.mk;
|
||||
var requestUrl = url + "/auth/sign_in";
|
||||
var request = Restangular.oneUrl(requestUrl, requestUrl);
|
||||
var params = {password: keys.pw, email: email};
|
||||
_.merge(request, params);
|
||||
request.post().then(function(response){
|
||||
this.handleAuthResponse(response, email, url, authParams, mk, keys.pw);
|
||||
this.handleAuthResponse(response, email, url, authParams, keys.mk, keys.pw);
|
||||
callback(response);
|
||||
}.bind(this))
|
||||
.catch(function(response){
|
||||
@@ -91,7 +90,9 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
this.handleAuthResponse = function(response, email, url, authParams, mk, pw) {
|
||||
localStorage.setItem("server", url);
|
||||
if(url) {
|
||||
localStorage.setItem("server", url);
|
||||
}
|
||||
localStorage.setItem("user", JSON.stringify(response.plain().user));
|
||||
localStorage.setItem("auth_params", JSON.stringify(_.omit(authParams, ["pw_nonce"])));
|
||||
localStorage.setItem("mk", mk);
|
||||
@@ -101,13 +102,12 @@ angular.module('app.frontend')
|
||||
|
||||
this.register = function(url, email, password, callback) {
|
||||
Neeto.crypto.generateInitialEncryptionKeysForUser({password: password, email: email}, function(keys, authParams){
|
||||
var mk = keys.mk;
|
||||
var requestUrl = url + "/auth";
|
||||
var request = Restangular.oneUrl(requestUrl, requestUrl);
|
||||
var params = _.merge({password: keys.pw, email: email}, authParams);
|
||||
_.merge(request, params);
|
||||
request.post().then(function(response){
|
||||
this.handleAuthResponse(response, email, url, authParams, mk, keys.pw);
|
||||
this.handleAuthResponse(response, email, url, authParams, keys.mk, keys.pw);
|
||||
callback(response);
|
||||
}.bind(this))
|
||||
.catch(function(response){
|
||||
@@ -117,55 +117,26 @@ angular.module('app.frontend')
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// this.changePassword = function(current_password, new_password) {
|
||||
// this.getAuthParamsForEmail(email, function(authParams){
|
||||
// if(!authParams) {
|
||||
// callback(null);
|
||||
// return;
|
||||
// }
|
||||
// Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: current_password, email: user.email}, authParams), function(currentKeys) {
|
||||
// Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: new_password, email: user.email}, authParams), function(newKeys){
|
||||
// var data = {};
|
||||
// data.current_password = currentKeys.pw;
|
||||
// data.password = newKeys.pw;
|
||||
// data.password_confirmation = newKeys.pw;
|
||||
//
|
||||
// var user = this.user;
|
||||
//
|
||||
// this._performPasswordChange(currentKeys, newKeys, function(response){
|
||||
// if(response && !response.error) {
|
||||
// // this.showNewPasswordForm = false;
|
||||
// // reencrypt data with new mk
|
||||
// this.reencryptAllItemsAndSave(user, newKeys.mk, currentKeys.mk, function(success){
|
||||
// if(success) {
|
||||
// this.setMk(newKeys.mk);
|
||||
// alert("Your password has been changed and your data re-encrypted.");
|
||||
// } else {
|
||||
// // rollback password
|
||||
// this._performPasswordChange(newKeys, currentKeys, function(response){
|
||||
// alert("There was an error changing your password. Your password has been rolled back.");
|
||||
// window.location.reload();
|
||||
// })
|
||||
// }
|
||||
// }.bind(this));
|
||||
// } else {
|
||||
// // this.showNewPasswordForm = false;
|
||||
// alert("There was an error changing your password. Please try again.");
|
||||
// }
|
||||
// }.bind(this))
|
||||
// }.bind(this));
|
||||
// }.bind(this));
|
||||
// }.bind(this));
|
||||
// }
|
||||
this.changePassword = function(email, new_password, callback) {
|
||||
Neeto.crypto.generateInitialEncryptionKeysForUser({password: new_password, email: email}, function(keys, authParams){
|
||||
var requestUrl = localStorage.getItem("server") + "/auth/change_pw";
|
||||
var request = Restangular.oneUrl(requestUrl, requestUrl);
|
||||
var params = _.merge({new_password: keys.pw}, authParams);
|
||||
_.merge(request, params);
|
||||
|
||||
this._performPasswordChange = function(url, email, current_keys, new_keys, callback) {
|
||||
var requestUrl = url + "/auth";
|
||||
var request = Restangular.oneUrl(requestUrl, requestUrl);
|
||||
var params = {password: new_keys.pw, password_confirmation: new_keys.pw, current_password: current_keys.pw, email: email};
|
||||
_.merge(request, params);
|
||||
request.patch().then(function(response){
|
||||
callback(response);
|
||||
})
|
||||
request.post().then(function(response){
|
||||
this.handleAuthResponse(response, email, null, authParams, keys.mk, keys.pw);
|
||||
callback(response.plain());
|
||||
}.bind(this))
|
||||
.catch(function(response){
|
||||
var error = response.data;
|
||||
if(!error) {
|
||||
error = {message: "Something went wrong while changing your password. Your password was not changed. Please try again."}
|
||||
}
|
||||
console.log("Change pw error", response);
|
||||
callback({error: error});
|
||||
})
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.staticifyObject = function(object) {
|
||||
|
||||
@@ -102,7 +102,6 @@ class DBManager {
|
||||
this.openDatabase((db) => {
|
||||
var request = db.transaction("items", "readwrite").objectStore("items").delete(item.uuid);
|
||||
request.onsuccess = function(event) {
|
||||
console.log("Successfully deleted item", item.uuid);
|
||||
if(callback) {
|
||||
callback(true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
angular
|
||||
.module('app.frontend')
|
||||
.directive('clickOutside', ['$document', function($document) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
replace: false,
|
||||
link : function($scope, $element, attrs) {
|
||||
|
||||
var didApplyClickOutside = false;
|
||||
|
||||
$element.bind('click', function(e) {
|
||||
didApplyClickOutside = false;
|
||||
if (attrs.isOpen) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
$document.bind('click', function() {
|
||||
if(!didApplyClickOutside) {
|
||||
$scope.$apply(attrs.clickOutside);
|
||||
didApplyClickOutside = true;
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}]);
|
||||
@@ -20,11 +20,15 @@ angular
|
||||
});
|
||||
|
||||
function showSpinner() {
|
||||
if(scope.hidePromise) {
|
||||
$timeout.cancel(scope.hidePromise);
|
||||
scope.hidePromise = null;
|
||||
}
|
||||
showElement(true);
|
||||
}
|
||||
|
||||
function hideSpinner() {
|
||||
$timeout(showElement.bind(this, false), getDelay());
|
||||
scope.hidePromise = $timeout(showElement.bind(this, false), getDelay());
|
||||
}
|
||||
|
||||
function showElement(show) {
|
||||
|
||||
@@ -6,19 +6,15 @@ class AccountMenu {
|
||||
this.scope = {};
|
||||
}
|
||||
|
||||
controller($scope, authManager, modelManager, syncManager, $timeout) {
|
||||
controller($scope, authManager, modelManager, syncManager, dbManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {url: syncManager.serverURL};
|
||||
$scope.formData = {mergeLocal: true, url: syncManager.serverURL};
|
||||
$scope.user = authManager.user;
|
||||
$scope.server = syncManager.serverURL;
|
||||
|
||||
$scope.syncStatus = syncManager.syncStatus;
|
||||
|
||||
$scope.changePasswordPressed = function() {
|
||||
$scope.showNewPasswordForm = !$scope.showNewPasswordForm;
|
||||
}
|
||||
|
||||
$scope.encryptionKey = function() {
|
||||
return syncManager.masterKey;
|
||||
}
|
||||
@@ -31,25 +27,61 @@ class AccountMenu {
|
||||
return `${$scope.server}/dashboard/?server=${$scope.server}&id=${$scope.user.email}&pw=${$scope.serverPassword()}`;
|
||||
}
|
||||
|
||||
$scope.newPasswordData = {};
|
||||
|
||||
$scope.showPasswordChangeForm = function() {
|
||||
$scope.newPasswordData.showForm = true;
|
||||
}
|
||||
|
||||
$scope.submitPasswordChange = function() {
|
||||
$scope.passwordChangeData.status = "Generating New Keys...";
|
||||
|
||||
$timeout(function(){
|
||||
if(data.password != data.password_confirmation) {
|
||||
alert("Your new password does not match its confirmation.");
|
||||
return;
|
||||
}
|
||||
if($scope.newPasswordData.newPassword != $scope.newPasswordData.newPasswordConfirmation) {
|
||||
alert("Your new password does not match its confirmation.");
|
||||
$scope.newPasswordData.status = null;
|
||||
return;
|
||||
}
|
||||
|
||||
authManager.changePassword($scope.passwordChangeData.current_password, $scope.passwordChangeData.new_password, function(response){
|
||||
var email = $scope.user.email;
|
||||
if(!email) {
|
||||
alert("We don't have your email stored. Please log out then log back in to fix this issue.");
|
||||
$scope.newPasswordData.status = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.newPasswordData.status = "Generating New Keys...";
|
||||
$scope.newPasswordData.showForm = false;
|
||||
|
||||
// perform a sync beforehand to pull in any last minutes changes before we change the encryption key (and thus cant decrypt new changes)
|
||||
syncManager.sync(function(response){
|
||||
authManager.changePassword(email, $scope.newPasswordData.newPassword, function(response){
|
||||
if(response.error) {
|
||||
alert("There was an error changing your password. Please try again.");
|
||||
$scope.newPasswordData.status = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// re-encrypt all items
|
||||
$scope.newPasswordData.status = "Re-encrypting all items with your new key...";
|
||||
|
||||
modelManager.setAllItemsDirty();
|
||||
syncManager.sync(function(response){
|
||||
if(response.error) {
|
||||
alert("There was an error re-encrypting your items. Your password was changed, but not all your items were properly re-encrypted and synced. You should try syncing again. If all else fails, you should restore your notes from backup.")
|
||||
return;
|
||||
}
|
||||
$scope.newPasswordData.status = "Successfully changed password and re-encrypted all items.";
|
||||
$timeout(function(){
|
||||
alert("Your password has been changed, and your items successfully re-encrypted and synced. You must sign out of all other signed in applications and sign in again, or else you may corrupt your data.")
|
||||
$scope.newPasswordData = {};
|
||||
}, 1000)
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
$scope.loginSubmitPressed = function() {
|
||||
$scope.formData.status = "Generating Login Keys...";
|
||||
console.log("logging in with url", $scope.formData.url);
|
||||
$timeout(function(){
|
||||
authManager.login($scope.formData.url, $scope.formData.email, $scope.formData.user_password, function(response){
|
||||
if(!response || response.error) {
|
||||
@@ -66,6 +98,11 @@ class AccountMenu {
|
||||
}
|
||||
|
||||
$scope.submitRegistrationForm = function() {
|
||||
var confirmation = prompt("Please confirm your password. Note that because your notes are encrypted using your password, Standard Notes does not have a password reset option. You cannot forget your password.")
|
||||
if(confirmation !== $scope.formData.user_password) {
|
||||
alert("The two passwords you entered do not match. Please try again.");
|
||||
return;
|
||||
}
|
||||
$scope.formData.status = "Generating Account Keys...";
|
||||
|
||||
$timeout(function(){
|
||||
@@ -81,10 +118,32 @@ class AccountMenu {
|
||||
})
|
||||
}
|
||||
|
||||
$scope.localNotesCount = function() {
|
||||
return modelManager.filteredNotes.length;
|
||||
}
|
||||
|
||||
$scope.mergeLocalChanged = function() {
|
||||
if(!$scope.formData.mergeLocal) {
|
||||
if(!confirm("Unchecking this option means any of the notes you have written while you were signed out will be deleted. Are you sure you want to discard these notes?")) {
|
||||
$scope.formData.mergeLocal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.onAuthSuccess = function() {
|
||||
syncManager.markAllItemsDirtyAndSaveOffline(function(){
|
||||
var block = function() {
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
|
||||
if($scope.formData.mergeLocal) {
|
||||
syncManager.markAllItemsDirtyAndSaveOffline(function(){
|
||||
block();
|
||||
})
|
||||
} else {
|
||||
dbManager.clearAllItems(function(){
|
||||
block();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$scope.destroyLocalData = function() {
|
||||
@@ -116,6 +175,8 @@ class AccountMenu {
|
||||
$scope.importData = null;
|
||||
if(!response) {
|
||||
alert("There was an error importing your data. Please try again.");
|
||||
} else {
|
||||
alert("Your data was successfully imported.")
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -149,12 +210,11 @@ class AccountMenu {
|
||||
}
|
||||
|
||||
$scope.importJSONData = function(data, password, callback) {
|
||||
console.log("Importing data", data);
|
||||
|
||||
var onDataReady = function() {
|
||||
var items = modelManager.mapResponseItemsToLocalModels(data.items);
|
||||
items.forEach(function(item){
|
||||
item.setDirty(true);
|
||||
item.deleted = false;
|
||||
item.markAllReferencesDirty();
|
||||
})
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ class ContextualExtensionsMenu {
|
||||
controller($scope, modelManager, extensionManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.renderData = {};
|
||||
|
||||
$scope.extensions = _.map(extensionManager.extensionsInContextOfItem($scope.item), function(ext){
|
||||
return _.cloneDeep(ext);
|
||||
});
|
||||
@@ -27,12 +29,30 @@ class ContextualExtensionsMenu {
|
||||
}
|
||||
|
||||
$scope.executeAction = function(action, extension) {
|
||||
if(action.verb == "nested") {
|
||||
action.showNestedActions = !action.showNestedActions;
|
||||
return;
|
||||
}
|
||||
action.running = true;
|
||||
extensionManager.executeAction(action, extension, $scope.item, function(response){
|
||||
action.running = false;
|
||||
$scope.handleActionResponse(action, response);
|
||||
})
|
||||
}
|
||||
|
||||
$scope.handleActionResponse = function(action, response) {
|
||||
switch (action.verb) {
|
||||
case "render": {
|
||||
var item = response.item;
|
||||
if(item.content_type == "Note") {
|
||||
$scope.renderData.title = item.title;
|
||||
$scope.renderData.text = item.text;
|
||||
$scope.renderData.showRenderModal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.accessTypeForExtension = function(extension) {
|
||||
return extensionManager.extensionUsesEncryptedData(extension) ? "encrypted" : "decrypted";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
class EditorMenu {
|
||||
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "frontend/directives/editor-menu.html";
|
||||
this.scope = {
|
||||
callback: "&",
|
||||
selectedEditor: "="
|
||||
};
|
||||
}
|
||||
|
||||
controller($scope, modelManager, extensionManager, syncManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {};
|
||||
|
||||
let editorContentType = "SN|Editor";
|
||||
|
||||
let defaultEditor = {
|
||||
default: true,
|
||||
name: "Plain"
|
||||
}
|
||||
|
||||
$scope.sysEditors = [defaultEditor];
|
||||
$scope.editors = modelManager.itemsForContentType(editorContentType);
|
||||
|
||||
$scope.editorForUrl = function(url) {
|
||||
return $scope.editors.filter(function(editor){return editor.url == url})[0];
|
||||
}
|
||||
|
||||
$scope.selectEditor = function(editor) {
|
||||
$scope.callback()(editor);
|
||||
}
|
||||
|
||||
$scope.deleteEditor = function(editor) {
|
||||
if(confirm("Are you sure you want to delete this editor?")) {
|
||||
modelManager.setItemToBeDeleted(editor);
|
||||
syncManager.sync();
|
||||
_.pull($scope.editors, editor);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.submitNewEditorRequest = function() {
|
||||
var editor = createEditor($scope.formData.url);
|
||||
modelManager.addItem(editor);
|
||||
editor.setDirty(true);
|
||||
syncManager.sync();
|
||||
$scope.editors.push(editor);
|
||||
$scope.formData = {};
|
||||
}
|
||||
|
||||
function createEditor(url) {
|
||||
var name = getParameterByName("name", url);
|
||||
return modelManager.createItem({
|
||||
content_type: editorContentType,
|
||||
url: url,
|
||||
name: name
|
||||
})
|
||||
}
|
||||
|
||||
function getParameterByName(name, url) {
|
||||
name = name.replace(/[\[\]]/g, "\\$&");
|
||||
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
|
||||
results = regex.exec(url);
|
||||
if (!results) return null;
|
||||
if (!results[2]) return '';
|
||||
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app.frontend').directive('editorMenu', () => new EditorMenu);
|
||||
@@ -21,7 +21,14 @@ class GlobalExtensionsMenu {
|
||||
if($scope.newExtensionData.url) {
|
||||
extensionManager.addExtension($scope.newExtensionData.url, function(response){
|
||||
if(!response) {
|
||||
alert("Unable to register this extension. Make sure the link is valid and try again.");
|
||||
if($scope.newExtensionData.url.indexOf("type=sf") != -1) {
|
||||
alert("Unable to register this extension. You are attempting to register a Standard File extension in Standard Notes. You should instead open your Standard File Dashboard and register this extension there.");
|
||||
} else if($scope.newExtensionData.url.indexOf("name=") != -1) {
|
||||
// user is mistakenly trying to register editor extension, most likely
|
||||
alert("Unable to register this extension. It looks like you may be trying to install an editor extension. To do that, click 'Editor' under the current note's title.");
|
||||
} else {
|
||||
alert("Unable to register this extension. Make sure the link is valid and try again.");
|
||||
}
|
||||
} else {
|
||||
$scope.newExtensionData.url = "";
|
||||
$scope.showNewExtensionForm = false;
|
||||
|
||||
@@ -28,7 +28,7 @@ class ExtensionManager {
|
||||
|
||||
extensionsInContextOfItem(item) {
|
||||
return this.extensions.filter(function(ext){
|
||||
return ext.actionsWithContextForItem(item).length > 0;
|
||||
return _.includes(ext.supported_types, item.content_type) || ext.actionsWithContextForItem(item).length > 0;
|
||||
})
|
||||
}
|
||||
|
||||
@@ -152,9 +152,29 @@ class ExtensionManager {
|
||||
case "get": {
|
||||
this.Restangular.oneUrl(action.url, action.url).get().then(function(response){
|
||||
action.error = false;
|
||||
var items = response.items;
|
||||
this.modelManager.mapResponseItemsToLocalModels(items);
|
||||
customCallback(items);
|
||||
var items = response.items || [response.item];
|
||||
EncryptionHelper.decryptMultipleItems(items, localStorage.getItem("mk"));
|
||||
items = this.modelManager.mapResponseItemsToLocalModels(items);
|
||||
for(var item of items) {
|
||||
item.setDirty(true);
|
||||
}
|
||||
this.syncManager.sync(null);
|
||||
customCallback({items: items});
|
||||
}.bind(this))
|
||||
.catch(function(response){
|
||||
action.error = true;
|
||||
customCallback(null);
|
||||
})
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "render": {
|
||||
this.Restangular.oneUrl(action.url, action.url).get().then(function(response){
|
||||
action.error = false;
|
||||
EncryptionHelper.decryptItem(response.item, localStorage.getItem("mk"));
|
||||
var item = this.modelManager.createItem(response.item);
|
||||
customCallback({item: item});
|
||||
}.bind(this))
|
||||
.catch(function(response){
|
||||
action.error = true;
|
||||
@@ -281,7 +301,11 @@ class ExtensionManager {
|
||||
}
|
||||
|
||||
outgoingParamsForItem(item, extension) {
|
||||
var itemParams = new ItemParams(item, this.syncManager.masterKey);
|
||||
var ek = this.syncManager.masterKey;
|
||||
if(!this.extensionUsesEncryptedData(extension)) {
|
||||
ek = null;
|
||||
}
|
||||
var itemParams = new ItemParams(item, ek);
|
||||
return itemParams.paramsForExtension();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Start from filter
|
||||
angular.module('app.frontend').filter('startFrom', function() {
|
||||
return function(input, start) {
|
||||
return input.slice(start);
|
||||
|
||||
5
app/assets/javascripts/app/services/filters/trusted.js
Normal file
@@ -0,0 +1,5 @@
|
||||
angular.module('app.frontend').filter('trusted', ['$sce', function ($sce) {
|
||||
return function(url) {
|
||||
return $sce.trustAsResourceUrl(url);
|
||||
};
|
||||
}]);
|
||||
@@ -60,11 +60,13 @@ class SNCrypto {
|
||||
}
|
||||
|
||||
base64(text) {
|
||||
return CryptoJS.enc.Utf8.parse(text).toString(CryptoJS.enc.Base64)
|
||||
// return CryptoJS.enc.Utf8.parse(text).toString(CryptoJS.enc.Base64)
|
||||
return window.btoa(text);
|
||||
}
|
||||
|
||||
base64Decode(base64String) {
|
||||
return CryptoJS.enc.Base64.parse(base64String).toString(CryptoJS.enc.Utf8)
|
||||
// return CryptoJS.enc.Base64.parse(base64String).toString(CryptoJS.enc.Utf8)
|
||||
return window.atob(base64String);
|
||||
}
|
||||
|
||||
sha256(text) {
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
class EncryptionHelper {
|
||||
|
||||
static encryptItem(item, key) {
|
||||
var item_key = null;
|
||||
if(item.enc_item_key) {
|
||||
// we reuse the key, but this is optional
|
||||
item_key = Neeto.crypto.decryptText(item.enc_item_key, key);
|
||||
} else {
|
||||
item_key = Neeto.crypto.generateRandomEncryptionKey();
|
||||
item.enc_item_key = Neeto.crypto.encryptText(item_key, key);
|
||||
}
|
||||
var item_key = Neeto.crypto.generateRandomEncryptionKey();
|
||||
item.enc_item_key = Neeto.crypto.encryptText(item_key, key);
|
||||
|
||||
var ek = Neeto.crypto.firstHalfOfKey(item_key);
|
||||
var ak = Neeto.crypto.secondHalfOfKey(item_key);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
angular.module('app.frontend')
|
||||
.service('markdownRenderer', function ($sce) {
|
||||
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
sanitize: true
|
||||
});
|
||||
|
||||
this.renderedContentForText = function(text) {
|
||||
if(!text || text.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return marked(text);
|
||||
}
|
||||
|
||||
this.renderHtml = function(html_code) {
|
||||
return $sce.trustAsHtml(html_code);
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
@@ -8,6 +8,7 @@ class ModelManager {
|
||||
this.itemChangeObservers = [];
|
||||
this.items = [];
|
||||
this._extensions = [];
|
||||
this.acceptableContentTypes = ["Note", "Tag", "Extension", "SN|Editor"];
|
||||
}
|
||||
|
||||
get allItems() {
|
||||
@@ -58,15 +59,17 @@ class ModelManager {
|
||||
}
|
||||
|
||||
mapResponseItemsToLocalModelsOmittingFields(items, omitFields) {
|
||||
var models = [];
|
||||
var models = [], processedObjects = [];
|
||||
|
||||
// first loop should add and process items
|
||||
for (var json_obj of items) {
|
||||
json_obj = _.omit(json_obj, omitFields || [])
|
||||
var item = this.findItem(json_obj["uuid"]);
|
||||
if(json_obj["deleted"] == true) {
|
||||
if(item) {
|
||||
this.removeItemLocally(item)
|
||||
}
|
||||
continue;
|
||||
if(json_obj["deleted"] == true || !_.includes(this.acceptableContentTypes, json_obj["content_type"])) {
|
||||
if(item) {
|
||||
this.removeItemLocally(item)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
_.omit(json_obj, omitFields);
|
||||
@@ -79,11 +82,16 @@ class ModelManager {
|
||||
|
||||
this.addItem(item);
|
||||
|
||||
if(json_obj.content) {
|
||||
this.resolveReferencesForItem(item);
|
||||
}
|
||||
|
||||
models.push(item);
|
||||
processedObjects.push(json_obj);
|
||||
}
|
||||
|
||||
// second loop should process references
|
||||
for (var index in processedObjects) {
|
||||
var json_obj = processedObjects[index];
|
||||
if(json_obj.content) {
|
||||
this.resolveReferencesForItem(models[index]);
|
||||
}
|
||||
}
|
||||
|
||||
this.notifySyncObserversOfModels(models);
|
||||
@@ -120,6 +128,8 @@ class ModelManager {
|
||||
item = new Tag(json_obj);
|
||||
} else if(json_obj.content_type == "Extension") {
|
||||
item = new Extension(json_obj);
|
||||
} else if(json_obj.content_type == "SN|Editor") {
|
||||
item = new Editor(json_obj);
|
||||
} else {
|
||||
item = new Item(json_obj);
|
||||
}
|
||||
@@ -144,9 +154,7 @@ class ModelManager {
|
||||
}
|
||||
} else if(item.content_type == "Note") {
|
||||
if(!_.find(this.notes, {uuid: item.uuid})) {
|
||||
this.notes.splice(_.sortedLastIndexBy(this.notes, item, function(item){
|
||||
return -item.created_at;
|
||||
}), 0, item);
|
||||
this.notes.unshift(item);
|
||||
}
|
||||
} else if(item.content_type == "Extension") {
|
||||
if(!_.find(this._extensions, {uuid: item.uuid})) {
|
||||
@@ -167,11 +175,13 @@ class ModelManager {
|
||||
}
|
||||
|
||||
resolveReferencesForItem(item) {
|
||||
item.locallyClearAllReferences();
|
||||
var contentObject = item.contentObject;
|
||||
if(!contentObject.references) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for(var reference of contentObject.references) {
|
||||
var referencedItem = this.findItem(reference.uuid);
|
||||
if(referencedItem) {
|
||||
@@ -225,6 +235,17 @@ class ModelManager {
|
||||
item.removeAllRelationships();
|
||||
}
|
||||
|
||||
/* Used when changing encryption key */
|
||||
setAllItemsDirty() {
|
||||
var relevantItems = this.allItems.filter(function(item){
|
||||
return _.includes(this.acceptableContentTypes, item.content_type);
|
||||
}.bind(this));
|
||||
|
||||
for(var item of relevantItems) {
|
||||
item.setDirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
removeItemLocally(item, callback) {
|
||||
_.pull(this.items, item);
|
||||
|
||||
|
||||
@@ -98,10 +98,37 @@ class SyncManager {
|
||||
return this._cursorToken;
|
||||
}
|
||||
|
||||
get queuedCallbacks() {
|
||||
if(!this._queuedCallbacks) {
|
||||
this._queuedCallbacks = [];
|
||||
}
|
||||
return this._queuedCallbacks;
|
||||
}
|
||||
|
||||
clearQueuedCallbacks() {
|
||||
this._queuedCallbacks = [];
|
||||
}
|
||||
|
||||
callQueuedCallbacksAndCurrent(currentCallback, response) {
|
||||
var allCallbacks = this.queuedCallbacks;
|
||||
if(currentCallback) {
|
||||
allCallbacks.push(currentCallback);
|
||||
}
|
||||
if(allCallbacks.length) {
|
||||
for(var eachCallback of allCallbacks) {
|
||||
eachCallback(response);
|
||||
}
|
||||
this.clearQueuedCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
sync(callback, options = {}) {
|
||||
|
||||
if(this.syncStatus.syncOpInProgress) {
|
||||
this.repeatOnCompletion = true;
|
||||
if(callback) {
|
||||
this.queuedCallbacks.push(callback);
|
||||
}
|
||||
console.log("Sync op in progress; returning.");
|
||||
return;
|
||||
}
|
||||
@@ -116,18 +143,17 @@ class SyncManager {
|
||||
return;
|
||||
}
|
||||
|
||||
var isContinuationSync = this.needsMoreSync;
|
||||
var isContinuationSync = this.syncStatus.needsMoreSync;
|
||||
|
||||
this.repeatOnCompletion = false;
|
||||
this.syncStatus.syncOpInProgress = true;
|
||||
|
||||
let submitLimit = 100;
|
||||
var subItems = allDirtyItems.slice(0, submitLimit);
|
||||
if(subItems.length < allDirtyItems.length) {
|
||||
// more items left to be synced, repeat
|
||||
this.needsMoreSync = true;
|
||||
this.syncStatus.needsMoreSync = true;
|
||||
} else {
|
||||
this.needsMoreSync = false;
|
||||
this.syncStatus.needsMoreSync = false;
|
||||
}
|
||||
|
||||
if(!isContinuationSync) {
|
||||
@@ -153,12 +179,14 @@ class SyncManager {
|
||||
this.$rootScope.$broadcast("sync:updated_token", this.syncToken);
|
||||
|
||||
var retrieved = this.handleItemsResponse(response.retrieved_items, null);
|
||||
|
||||
// merge only metadata for saved items
|
||||
// we write saved items to disk now because it clears their dirty status then saves
|
||||
// if we saved items before completion, we had have to save them as dirty and save them again on success as clean
|
||||
var omitFields = ["content", "auth_hash"];
|
||||
var saved = this.handleItemsResponse(response.saved_items, omitFields);
|
||||
|
||||
this.handleUnsavedItemsResponse(response.unsaved)
|
||||
|
||||
this.writeItemsToLocalStorage(saved, false, null);
|
||||
this.writeItemsToLocalStorage(retrieved, false, null);
|
||||
|
||||
@@ -169,14 +197,17 @@ class SyncManager {
|
||||
this.syncToken = response.sync_token;
|
||||
this.cursorToken = response.cursor_token;
|
||||
|
||||
if(this.cursorToken || this.repeatOnCompletion || this.needsMoreSync) {
|
||||
if(this.cursorToken || this.syncStatus.needsMoreSync) {
|
||||
setTimeout(function () {
|
||||
this.sync(callback, options);
|
||||
}.bind(this), 10); // wait 10ms to allow UI to update
|
||||
} else if(this.repeatOnCompletion) {
|
||||
this.repeatOnCompletion = false;
|
||||
setTimeout(function () {
|
||||
this.sync(callback, options);
|
||||
}.bind(this), 10); // wait 10ms to allow UI to update
|
||||
} else {
|
||||
if(callback) {
|
||||
callback(response);
|
||||
}
|
||||
this.callQueuedCallbacksAndCurrent(callback, response);
|
||||
}
|
||||
|
||||
}.bind(this))
|
||||
@@ -190,9 +221,7 @@ class SyncManager {
|
||||
|
||||
this.$rootScope.$broadcast("sync:error", error);
|
||||
|
||||
if(callback) {
|
||||
callback({error: "Sync error"});
|
||||
}
|
||||
this.callQueuedCallbacksAndCurrent(callback, {error: "Sync error"});
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
@@ -211,7 +240,7 @@ class SyncManager {
|
||||
var item = this.modelManager.findItem(itemResponse.uuid);
|
||||
var error = mapping.error;
|
||||
if(error.tag == "uuid_conflict") {
|
||||
// uuid conflicts can occur if a user attempts to import an old data archive with uuids form the old account into a new account
|
||||
// uuid conflicts can occur if a user attempts to import an old data archive with uuids from the old account into a new account
|
||||
this.modelManager.alternateUUIDForItem(item, handleNext);
|
||||
}
|
||||
++i;
|
||||
|
||||
@@ -72,45 +72,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.section-menu {
|
||||
padding-top: 0px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
padding-left: inherit;
|
||||
padding-right: inherit;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0px;
|
||||
background-color: #f1f1f1;
|
||||
color: $selected-text-color;
|
||||
height: 28px;
|
||||
cursor: default;
|
||||
|
||||
ol, ul {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&.dropdown-menu {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
li {
|
||||
text-align: left;
|
||||
|
||||
&.sep {
|
||||
margin: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
.dropdown-menu.contextual-menu {
|
||||
.extension {
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.ext-header {
|
||||
background-color: #ededed;
|
||||
border-bottom: 1px solid #d3d3d3;
|
||||
padding-top: 12px;
|
||||
padding-left: 10px;
|
||||
padding-bottom: 10px;
|
||||
position: relative;
|
||||
|
||||
> .name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
> .access {
|
||||
font-size: 12px;
|
||||
opacity: 0.5;
|
||||
font-weight: normal;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
> .loading {
|
||||
position: absolute;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
right: 10px;
|
||||
top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
list-style:none;
|
||||
padding-left:0;
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
|
||||
&.action {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid rgba(black, 0.1);
|
||||
background-color: rgba(white, 0.9);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(gray, 0.05);
|
||||
}
|
||||
|
||||
> .name {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
> .desc {
|
||||
font-weight: normal;
|
||||
opacity: 0.5;
|
||||
margin-top: 1px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
$heading-height: 75px;
|
||||
.editor {
|
||||
width: 60%;
|
||||
flex: 1 50%;
|
||||
min-width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: hidden;
|
||||
|
||||
&.fullscreen {
|
||||
width: 100%;
|
||||
@@ -12,253 +17,130 @@
|
||||
|
||||
.section-title-bar {
|
||||
border-bottom: none !important;
|
||||
|
||||
&.fullscreen {
|
||||
opacity: 0.0;
|
||||
-webkit-transition: all 300ms ease-in-out;
|
||||
-moz-transition: all 300ms ease-in-out;
|
||||
-ms-transition: all 300ms ease-in-out;
|
||||
-o-transition: all 300ms ease-in-out;
|
||||
transition: all 300ms ease-in-out;
|
||||
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
// z-index: -1;
|
||||
}
|
||||
height: $heading-height !important;
|
||||
}
|
||||
|
||||
$heading-height: 100px;
|
||||
.section-menu {
|
||||
flex: 1 0 28px;
|
||||
max-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-heading {
|
||||
.editor-heading {
|
||||
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
padding-top: 0px;
|
||||
background-color: white;
|
||||
|
||||
min-height: $heading-height;
|
||||
|
||||
padding-right: 10px;
|
||||
|
||||
&.fullscreen {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
> .title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding-top: 0px;
|
||||
background-color: white;
|
||||
|
||||
min-height: $heading-height;
|
||||
|
||||
width: 100%;
|
||||
padding-right: 10px;
|
||||
|
||||
> .title {
|
||||
font-size: 18px;
|
||||
> .input {
|
||||
float: left;
|
||||
text-overflow:ellipsis;
|
||||
width: 90%;
|
||||
font-weight: bold;
|
||||
padding-top: 0px;
|
||||
width: 100%;
|
||||
|
||||
> .input {
|
||||
float: left;
|
||||
text-overflow:ellipsis;
|
||||
width: 90%;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
|
||||
&:disabled {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.save-status {
|
||||
width: 20% !important;
|
||||
float: right;
|
||||
position: absolute;
|
||||
|
||||
right: 20px;
|
||||
font-size: 12px;
|
||||
text-transform: none;
|
||||
font-weight: normal;
|
||||
margin-top: 4px;
|
||||
width: 120px;
|
||||
text-align: right;
|
||||
color: rgba(black, 0.23);
|
||||
}
|
||||
|
||||
.tags {
|
||||
clear: left;
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
|
||||
.tags-input {
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fullscreen-ghost-bar {
|
||||
|
||||
position: absolute;
|
||||
width: 20%;
|
||||
height: 200px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
.editor-content {
|
||||
max-height: 100%;
|
||||
|
||||
height: 100%;
|
||||
clear: both;
|
||||
min-width: 0;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
overflow: auto;
|
||||
text-align: center; // centers children div horizontally
|
||||
z-index: 10;
|
||||
padding-top: $heading-height;
|
||||
|
||||
&.fullscreen {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.sampler-container {
|
||||
margin-top: 10px;
|
||||
padding: 15px;
|
||||
padding-top: 17px;
|
||||
font-size: 17px;
|
||||
// opacity: 0.5;
|
||||
}
|
||||
|
||||
.sampler {
|
||||
// opacity: 0.5;
|
||||
color: rgba(black, 0.3);
|
||||
}
|
||||
|
||||
.editable {
|
||||
font-family: monospace;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 15px;
|
||||
padding-top: 17px;
|
||||
font-size: 17px;
|
||||
resize: none;
|
||||
|
||||
&.fullscreen {
|
||||
padding: 85px 10%;
|
||||
max-width: 1200px;
|
||||
display: inline-block;;
|
||||
}
|
||||
}
|
||||
|
||||
.preview {
|
||||
// font-family: monospace;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
line-height: 23px;
|
||||
overflow-y: scroll;
|
||||
padding: 0px 15px;
|
||||
text-align: left;
|
||||
|
||||
&.fullscreen {
|
||||
padding: 85px 10%;
|
||||
max-width: 1200px;
|
||||
display: inline-block;;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.markdown {
|
||||
margin-left: 15px;
|
||||
float: right;
|
||||
text-align: right;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.full-screen-button {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
right: 20px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
list-style-position: inside;
|
||||
-webkit-margin-before: 1em;
|
||||
-webkit-margin-after: 1em;
|
||||
-webkit-margin-start: 0px;
|
||||
-webkit-margin-end: 0px;
|
||||
-webkit-padding-start: 0px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.nav-tabs {
|
||||
a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
a {
|
||||
background-color: transparent;
|
||||
|
||||
&:disabled {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.save-status {
|
||||
width: 20% !important;
|
||||
float: right;
|
||||
position: absolute;
|
||||
|
||||
right: 20px;
|
||||
font-size: 12px;
|
||||
text-transform: none;
|
||||
font-weight: normal;
|
||||
margin-top: 4px;
|
||||
width: 120px;
|
||||
text-align: right;
|
||||
color: rgba(black, 0.23);
|
||||
}
|
||||
|
||||
.tags {
|
||||
clear: left;
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
|
||||
.tags-input {
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editor-content {
|
||||
flex: 1;
|
||||
z-index: 10;
|
||||
overflow-y: hidden;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
background-color: white;
|
||||
|
||||
&.fullscreen {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
#editor-iframe {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.editable {
|
||||
font-family: monospace;
|
||||
flex: 1;
|
||||
overflow-y: scroll;
|
||||
width: 100%;
|
||||
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 15px;
|
||||
padding-top: 11px;
|
||||
font-size: 17px;
|
||||
resize: none;
|
||||
|
||||
&.fullscreen {
|
||||
padding: 85px 10%;
|
||||
max-width: 1200px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
ul {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-pills>li {
|
||||
float: left;
|
||||
}
|
||||
.nav>li {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-pills>li.active>a, .nav-pills>li.active>a:focus, .nav-pills>li.active>a:hover {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
}
|
||||
.nav-pills>li>a {
|
||||
border-radius: 4px;
|
||||
}
|
||||
.nav>li>a {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.nav-tabs>li {
|
||||
float: left;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.nav>li {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover {
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
.nav-tabs>li>a {
|
||||
margin-right: 2px;
|
||||
line-height: 1.42857143;
|
||||
border: 1px solid transparent;
|
||||
// border-radius: 4px 4px 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.nav>li>a {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
font-weight: bold;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
112
app/assets/stylesheets/app/_extensions.scss
Normal file
@@ -0,0 +1,112 @@
|
||||
.extension-render-modal {
|
||||
position: fixed;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 10000;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(gray, 0.3);
|
||||
|
||||
.content {
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
background-color: white;
|
||||
width: 700px;
|
||||
height: 500px;
|
||||
margin: auto;
|
||||
padding: 25px;
|
||||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-section-footer {
|
||||
background-color: #ededed;
|
||||
border-top: 1px solid #d3d3d3;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.menu-section-header {
|
||||
background-color: #ededed;
|
||||
border-bottom: 1px solid #d3d3d3;
|
||||
position: relative;
|
||||
padding-top: 12px;
|
||||
padding-left: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
|
||||
> .title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
> .subtitle {
|
||||
font-size: 12px;
|
||||
opacity: 0.5;
|
||||
font-weight: normal;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
> .loading {
|
||||
position: absolute;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
right: 10px;
|
||||
top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu.editor-menu {
|
||||
overflow-y: scroll;
|
||||
max-height: 85vh;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
list-style:none;
|
||||
padding-left:0;
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
|
||||
&.menu-item {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid rgba(black, 0.1);
|
||||
background-color: rgba(white, 0.9);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(gray, 0.05);
|
||||
}
|
||||
|
||||
&.nonactive {
|
||||
cursor: default;
|
||||
&:hover {
|
||||
background-color: rgba(white, 0.9) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-title {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.menu-item-subtitle {
|
||||
font-weight: normal;
|
||||
opacity: 0.5;
|
||||
margin-top: 1px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
src: font-url('ProximaNova/ProximaNova-Regular.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Regular.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Regular.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Regular.svg') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-style: italic;
|
||||
src: font-url('ProximaNova/ProximaNova-Regular-Italic.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Regular-Italic.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Regular-Italic.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Regular-Italic.svg') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-weight: 600;
|
||||
src: font-url('ProximaNova/ProximaNova-Semibold.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Semibold.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Semibold.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Semibold.svg') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
src: font-url('ProximaNova/ProximaNova-Semibold-Italic.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Semibold-Italic.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Semibold-Italic.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Semibold-Italic.svg') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-weight: bold;
|
||||
src: font-url('ProximaNova/ProximaNova-Bold.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Bold.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Bold.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Bold.svg') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
src: font-url('ProximaNova/ProximaNova-Bold-Italic.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Bold-Italic.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Bold-Italic.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Bold-Italic.svg') format('svg');
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-weight: 100;
|
||||
src: font-url('fonts/ProximaNova-Thin.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Thin.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Thin.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Thin.svg') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-weight: 100;
|
||||
font-style: italic;
|
||||
src: font-url('fonts/ProximaNova-Thin-Italic.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Thin-Italic.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Thin-Italic.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Thin-Italic.svg') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ProximaNova;
|
||||
font-weight: 900;
|
||||
src: font-url('ProximaNova/ProximaNova-Extrabold.eot');
|
||||
src: local('☺'),
|
||||
font-url('ProximaNova/ProximaNova-Extrabold.woff') format('woff'),
|
||||
font-url('ProximaNova/ProximaNova-Extrabold.ttf') format('truetype'),
|
||||
font-url('ProximaNova/ProximaNova-Extrabold.svg') format('svg');
|
||||
}
|
||||
@@ -1,141 +1,3 @@
|
||||
.pull-left {
|
||||
float: left !important;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 1px !important;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2px !important;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: 5px !important;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
|
||||
.mt-25 {
|
||||
margin-top: 25px !important;
|
||||
}
|
||||
|
||||
.mb-0 {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
|
||||
.mr-5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.faded {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.center-align {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.one-line-overflow {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.small-v-space {
|
||||
height: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.medium-v-space {
|
||||
height: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.large-v-space {
|
||||
height: 24px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.small-padding {
|
||||
padding: 5px !important;
|
||||
}
|
||||
|
||||
.medium-padding {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: 4px !important;
|
||||
}
|
||||
|
||||
.pb-6 {
|
||||
padding-bottom: 6px !important;
|
||||
}
|
||||
|
||||
.pb-10 {
|
||||
padding-bottom: 10px !important;
|
||||
}
|
||||
|
||||
.large-padding {
|
||||
padding: 22px !important;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: $blue-color;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.normal {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.fake-link {
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
@@ -146,6 +8,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.footer-bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
@@ -190,10 +57,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 14px !important;
|
||||
|
||||
@@ -29,8 +29,8 @@ $blue-color: #086dd6;
|
||||
}
|
||||
}
|
||||
|
||||
[ng\:cloak], [ng-cloak], .ng-cloak {
|
||||
display: none !important;
|
||||
.blue {
|
||||
color: $blue-color;
|
||||
}
|
||||
|
||||
html,
|
||||
@@ -45,6 +45,7 @@ body {
|
||||
height: 100%;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
background-color: $bg-color;
|
||||
}
|
||||
|
||||
.dark-button {
|
||||
@@ -72,19 +73,6 @@ a {
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
line-height: 1.45;
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
code {
|
||||
word-wrap: break-word;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
p {
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -93,35 +81,21 @@ p {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.app-body-class {
|
||||
height: 100%;
|
||||
background-color: $bg-color;
|
||||
min-width: 100px;
|
||||
min-width: 900px;
|
||||
}
|
||||
|
||||
$header-height: 25px;
|
||||
|
||||
.app-container {
|
||||
display: table;
|
||||
background-color: $bg-color;
|
||||
width: 100%;
|
||||
height: calc(100% - #{$header-height});
|
||||
padding-top: 0px;
|
||||
font-size: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
$section-header-height: 70px;
|
||||
|
||||
.app {
|
||||
height: 100%;
|
||||
// height: 100%;
|
||||
height: calc(100% - #{$header-height});
|
||||
width: 100%;
|
||||
display: table-row;
|
||||
display: flex;
|
||||
vertical-align: top;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.light-button {
|
||||
background-color: $bg-color;
|
||||
@@ -140,15 +114,8 @@ $section-header-height: 70px;
|
||||
|
||||
.section {
|
||||
padding-bottom: 0px;
|
||||
|
||||
display: block;
|
||||
height: 100%;
|
||||
max-height: calc(100vh - #{$header-height});
|
||||
float: left;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
min-width: 0;
|
||||
|
||||
font-size: 17px;
|
||||
|
||||
.scrollable {
|
||||
@@ -161,7 +128,6 @@ $section-header-height: 70px;
|
||||
max-height: 100%;
|
||||
background-color: white;
|
||||
position: relative;
|
||||
box-shadow: 0px 0px 2px rgba(gray, 0.3);
|
||||
}
|
||||
|
||||
.section-title-bar {
|
||||
@@ -198,3 +164,41 @@ $section-header-height: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-menu {
|
||||
width: 100%;
|
||||
padding-top: 0px;
|
||||
padding-left: 21px;
|
||||
padding-right: 21px;
|
||||
|
||||
background-color: #f1f1f1;
|
||||
color: $selected-text-color;
|
||||
height: 28px;
|
||||
cursor: default;
|
||||
|
||||
ol, ul {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&.dropdown-menu {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
li {
|
||||
text-align: left;
|
||||
|
||||
&.sep {
|
||||
margin: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,11 @@
|
||||
$screen-xs: 480px !default;
|
||||
//** Deprecated `$screen-xs-min` as of v3.2.0
|
||||
$screen-xs-min: $screen-xs !default;
|
||||
//** Deprecated `$screen-phone` as of v3.0.1
|
||||
$screen-phone: $screen-xs-min !default;
|
||||
|
||||
// Small screen / tablet
|
||||
//** Deprecated `$screen-sm` as of v3.0.1
|
||||
$screen-sm: 768px !default;
|
||||
$screen-sm-min: $screen-sm !default;
|
||||
//** Deprecated `$screen-tablet` as of v3.0.1
|
||||
$screen-tablet: $screen-sm-min !default;
|
||||
|
||||
// Medium screen / desktop
|
||||
//** Deprecated `$screen-md` as of v3.0.1
|
||||
@@ -67,111 +63,6 @@ $screen-md-max: ($screen-lg-min - 1) !default;
|
||||
|
||||
*:focus {outline:0;}
|
||||
|
||||
.navbar {
|
||||
min-height: 0px !important;
|
||||
background-color: white;
|
||||
height: 80px;
|
||||
margin-bottom: 0px;
|
||||
padding-top: 10px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.navbar {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: relative;
|
||||
min-height: 50px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-header {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.container > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-header, .container-fluid > .navbar-collapse {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand {
|
||||
// margin-left: -15px;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
float: left;
|
||||
padding: 15px 15px;
|
||||
font-size: 18px;
|
||||
line-height: 20px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-header, .container-fluid > .navbar-collapse {
|
||||
// margin-right: 0;
|
||||
// margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.container > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-header, .container-fluid > .navbar-collapse {
|
||||
// margin-right: -15px;
|
||||
// margin-left: -15px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-collapse.collapse {
|
||||
display: block !important;
|
||||
height: auto !important;
|
||||
padding-bottom: 0;
|
||||
overflow: visible !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.navbar-collapse {
|
||||
width: auto;
|
||||
border-top: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
overflow-x: visible;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
border-top: 1px solid transparent;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.navbar-right {
|
||||
float: right !important;
|
||||
// margin-right: -15px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.navbar-text {
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
.navbar-text {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.dropup, .dropdown {
|
||||
position: relative;
|
||||
}
|
||||
@@ -232,10 +123,6 @@ button:focus {outline:0;}
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.open > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
@@ -262,23 +149,6 @@ button:focus {outline:0;}
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// ul, menu, dir {
|
||||
// display: block;
|
||||
// list-style-type: disc;
|
||||
// -webkit-margin-before: 1em;
|
||||
// -webkit-margin-after: 1em;
|
||||
// -webkit-margin-start: 0px;
|
||||
// -webkit-margin-end: 0px;
|
||||
// -webkit-padding-start: 40px;
|
||||
// }
|
||||
|
||||
.dropdown-menu .divider {
|
||||
height: 1px;
|
||||
margin: 9px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
@@ -290,59 +160,35 @@ button:focus {outline:0;}
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.panel-top {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.panel-left {
|
||||
left: -50px;
|
||||
}
|
||||
|
||||
.panel-right {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.panel-centered {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857;
|
||||
color: #555555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
||||
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857;
|
||||
color: #555555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
|
||||
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
}
|
||||
|
||||
input, button, select, textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.has-feedback {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-tag {
|
||||
margin-bottom: 15px;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
@@ -352,63 +198,3 @@ input, button, select, textarea {
|
||||
margin-right: auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
background-color: transparent;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-link:hover, .btn-link:focus {
|
||||
color: #23527c;
|
||||
text-decoration: underline;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.animated {
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.animated-fast {
|
||||
-webkit-animation-duration: 0.5s;
|
||||
animation-duration: 0.5s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.fadeInDown {
|
||||
-webkit-animation-name: fadeInDown;
|
||||
animation-name: fadeInDown;
|
||||
}
|
||||
|
||||
.fadeIn {
|
||||
-webkit-animation-name: fadeIn;
|
||||
animation-name: fadeIn;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0,-100%,0);
|
||||
transform: translate3d(0,-100%,0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
.notes {
|
||||
width: 25%;
|
||||
border-left: 1px solid #dddddd;
|
||||
border-right: 1px solid #dddddd;
|
||||
|
||||
flex: 1 20%;
|
||||
max-width: 350px;
|
||||
min-width: 170px;
|
||||
|
||||
$notes-title-bar-height: 130px;
|
||||
|
||||
.notes-title-bar {
|
||||
|
||||
270
app/assets/stylesheets/app/_standard.scss
Normal file
@@ -0,0 +1,270 @@
|
||||
.pull-left {
|
||||
float: left !important;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 1px !important;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2px !important;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: 5px !important;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
.mt-15 {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
.mt-25 {
|
||||
margin-top: 25px !important;
|
||||
}
|
||||
|
||||
.mt-50 {
|
||||
margin-top: 50px !important;
|
||||
}
|
||||
|
||||
.mt-100 {
|
||||
margin-top: 100px !important;
|
||||
}
|
||||
|
||||
.mb-0 {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
|
||||
.mr-5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mr-10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mr-20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.pb-0 {
|
||||
padding-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.faded {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.center-align {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.one-line-overflow {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.small-v-space {
|
||||
height: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.medium-v-space {
|
||||
height: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.large-v-space {
|
||||
height: 24px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.small-padding {
|
||||
padding: 5px !important;
|
||||
}
|
||||
|
||||
.medium-padding {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: 4px !important;
|
||||
}
|
||||
|
||||
.pb-6 {
|
||||
padding-bottom: 6px !important;
|
||||
}
|
||||
|
||||
.pb-10 {
|
||||
padding-bottom: 10px !important;
|
||||
}
|
||||
|
||||
.large-padding {
|
||||
padding: 22px !important;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.normal {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.medium {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline-block !important;
|
||||
|
||||
&.top {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&.middle {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input.form-control {
|
||||
margin-bottom: 10px;
|
||||
border-radius: 0px;
|
||||
min-height: 39px;
|
||||
font-size: 14px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
|
||||
@mixin wide-button() {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
// min-width: 200px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&.black {
|
||||
@include wide-button();
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.white {
|
||||
@include wide-button();
|
||||
background-color: white;
|
||||
color: black;
|
||||
border: 1px solid rgba(gray, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.gray-bg {
|
||||
background-color: #f6f6f6;
|
||||
border: 1px solid #f2f2f2;
|
||||
}
|
||||
|
||||
.white-bg {
|
||||
background-color: white;
|
||||
border: 1px solid rgba(gray, 0.2);
|
||||
}
|
||||
|
||||
.col-container {
|
||||
// white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin col() {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.col-10 {
|
||||
width: 10%;
|
||||
@include col();
|
||||
}
|
||||
|
||||
.col-15 {
|
||||
width: 15%;
|
||||
@include col();
|
||||
}
|
||||
|
||||
.col-20 {
|
||||
width: 20%;
|
||||
@include col();
|
||||
}
|
||||
|
||||
.col-45 {
|
||||
width: 45%;
|
||||
@include col();
|
||||
}
|
||||
|
||||
.col-50 {
|
||||
width: 50%;
|
||||
@include col();
|
||||
}
|
||||
|
||||
.col-80 {
|
||||
width: 80%;
|
||||
@include col();
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute !important;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
.tags {
|
||||
width: 15%;
|
||||
// width: 15%;
|
||||
flex: 1 10%;
|
||||
max-width: 180px;
|
||||
min-width: 100px;
|
||||
|
||||
$tags-title-bar-height: 55px;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
$dark-gray: #2e2e2e;
|
||||
|
||||
@import "app/standard";
|
||||
@import "app/mostrap";
|
||||
@import "app/main";
|
||||
@import "app/common";
|
||||
@@ -7,41 +8,4 @@ $dark-gray: #2e2e2e;
|
||||
@import "app/tags";
|
||||
@import "app/notes";
|
||||
@import "app/editor";
|
||||
@import "app/directives";
|
||||
|
||||
@font-face {
|
||||
font-family: 'icomoon';
|
||||
src: url('icomoon/icomoon.eot');
|
||||
src: url('icomoon/icomoon.eot') format('embedded-opentype'),
|
||||
url('icomoon/icomoon.ttf') format('truetype'),
|
||||
url('icomoon/icomoon.woff') format('woff'),
|
||||
url('icomoon/icomoon.svg') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'icomoon' !important;
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
// line-height: 1;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
.inline-icon {
|
||||
display: inline-block;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.icon-markdown:before {
|
||||
content: "\e901";
|
||||
}
|
||||
@import "app/extensions";
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
%input.form-control{:name => 'server', :placeholder => 'Server URL', :required => true, :type => 'text', 'ng-model' => 'formData.url'}
|
||||
%input.form-control{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'formData.email'}
|
||||
%input.form-control{:placeholder => 'Password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'formData.user_password'}
|
||||
.checkbox{"ng-if" => "localNotesCount() > 0"}
|
||||
%label
|
||||
%input{"type" => "checkbox", "ng-model" => "formData.mergeLocal", "ng-bind" => "true", "ng-change" => "mergeLocalChanged()"}
|
||||
Merge local notes ({{localNotesCount()}} notes)
|
||||
|
||||
%div{"ng-if" => "!formData.status"}
|
||||
%button.btn.dark-button.half-button{"ng-click" => "loginSubmitPressed()", "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
||||
@@ -28,6 +32,13 @@
|
||||
%div{"ng-if" => "user"}
|
||||
%h2 {{user.email}}
|
||||
%p {{server}}
|
||||
%div.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress || syncStatus.needsMoreSync", "delay" => "1000"}
|
||||
.spinner.inline.mr-5.blue
|
||||
{{"Syncing" + (syncStatus.total > 0 ? ":" : "")}}
|
||||
%span{"ng-if" => "syncStatus.total > 0"} {{syncStatus.current}}/{{syncStatus.total}}
|
||||
%p.bold.mt-10.red.block{"ng-if" => "syncStatus.error"} Error syncing: {{syncStatus.error.message}}
|
||||
|
||||
%a.block.mt-15{"href" => "{{dashboardURL()}}", "target" => "_blank"} → Standard File Dashboard
|
||||
%a.block.mt-5{"ng-click" => "showCredentials = !showCredentials"} Show Credentials
|
||||
%section.gray-bg.mt-10.medium-padding{"ng-if" => "showCredentials"}
|
||||
%label.block
|
||||
@@ -36,13 +47,23 @@
|
||||
%label.block.mt-5.mb-0
|
||||
Server password:
|
||||
.wrap.normal.mt-1 {{serverPassword() ? serverPassword() : 'Not available. Sign out then sign back in to compute.'}}
|
||||
%a.block.mt-5{"href" => "{{dashboardURL()}}", "target" => "_blank"} Standard File Dashboard
|
||||
|
||||
%div.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress", "delay" => "1000"}
|
||||
.spinner.inline.mr-5.blue
|
||||
Syncing
|
||||
%span{"ng-if" => "syncStatus.total > 0"}: {{syncStatus.current}}/{{syncStatus.total}}
|
||||
%p.bold.mt-10.red.block{"ng-if" => "syncStatus.error"} Error syncing: {{syncStatus.error.message}}
|
||||
%a.block.mt-5{"ng-click" => "newPasswordData.changePassword = !newPasswordData.changePassword"} Change Password
|
||||
%section.gray-bg.mt-10.medium-padding{"ng-if" => "newPasswordData.changePassword"}
|
||||
%p.bold Change Password (Beta)
|
||||
%p.mt-10 Since your encryption key is based on your password, changing your password requires all your notes and tags to be re-encrypted using your new key.
|
||||
%p.mt-5 If you have thousands of items, this can take several minutes — you must keep the application window open during this process.
|
||||
%p.mt-5 After changing your password, you must log out of all other applications currently signed in to your account.
|
||||
%p.bold.mt-5 It is highly recommended you download a backup of your data before proceeding.
|
||||
%div.mt-10{"ng-if" => "!newPasswordData.status"}
|
||||
%a.red.mr-5{"ng-if" => "!newPasswordData.showForm", "ng-click" => "showPasswordChangeForm()"} Continue
|
||||
%a{"ng-click" => "newPasswordData.changePassword = false; newPasswordData.showForm = false"} Cancel
|
||||
%div.mt-10{"ng-if" => "newPasswordData.showForm"}
|
||||
%form
|
||||
%input.form-control{"type" => "text", "ng-model" => "newPasswordData.newPassword", "placeholder" => "Enter new password"}
|
||||
%input.form-control{"type" => "text", "ng-model" => "newPasswordData.newPasswordConfirmation", "placeholder" => "Confirm new password"}
|
||||
%button.btn.dark-button.btn-block{"ng-click" => "submitPasswordChange()"} Submit
|
||||
%p.italic.mt-10{"ng-if" => "newPasswordData.status"} {{newPasswordData.status}}
|
||||
|
||||
.medium-v-space
|
||||
|
||||
@@ -77,4 +98,4 @@
|
||||
|
||||
.spinner.mt-10{"ng-if" => "importData.loading"}
|
||||
|
||||
%a.block.mt-25.red{"ng-click" => "destroyLocalData()"} Destroy all local data
|
||||
%a.block.mt-25.red{"ng-click" => "destroyLocalData()"} {{ user ? "Sign out and clear local data" : "Clear all local data" }}
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark.contextual-menu
|
||||
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark.editor-menu
|
||||
.extension{"ng-repeat" => "extension in extensions"}
|
||||
.ext-header
|
||||
.name {{extension.name}}
|
||||
.access
|
||||
.menu-section-header
|
||||
.title {{extension.name}}
|
||||
.subtitle
|
||||
Can access your data
|
||||
%strong {{accessTypeForExtension(extension)}}
|
||||
.spinner.loading{"ng-if" => "extension.loading"}
|
||||
%ul
|
||||
%li.action{"ng-repeat" => "action in extension.actionsWithContextForItem(item)", "ng-click" => "executeAction(action, extension)"}
|
||||
.name {{action.label}}
|
||||
.desc {{action.desc}}
|
||||
%li.menu-item{"ng-repeat" => "action in extension.actionsWithContextForItem(item)", "ng-click" => "executeAction(action, extension)"}
|
||||
.menu-item-title {{action.label}}
|
||||
.menu-item-subtitle {{action.desc}}
|
||||
|
||||
%div{"ng-if" => "action.showNestedActions"}
|
||||
%ul.mt-10
|
||||
%li.menu-item.white-bg{"ng-repeat" => "subaction in action.subactions", "ng-click" => "executeAction(subaction, extension); $event.stopPropagation()", "style" => "margin-top: -1px;"}
|
||||
.menu-item-title {{subaction.label}}
|
||||
.menu-item-subtitle {{subaction.desc}}
|
||||
%span{"ng-if" => "subaction.running"}
|
||||
.spinner{"style" => "margin-top: 3px;"}
|
||||
|
||||
%span{"ng-if" => "action.running"}
|
||||
.spinner{"style" => "margin-top: 3px;"}
|
||||
|
||||
.extension-render-modal{"ng-if" => "renderData.showRenderModal", "ng-click" => "renderData.showRenderModal = false"}
|
||||
.content
|
||||
%h2 {{renderData.title}}
|
||||
%p.normal{"style" => "white-space: pre-wrap; font-family: monospace; font-size: 16px;"} {{renderData.text}}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark.editor-menu
|
||||
.menu-section-header
|
||||
.title System Editors
|
||||
%ul
|
||||
%li.menu-item{"ng-repeat" => "editor in sysEditors", "ng-click" => "selectEditor(editor)"}
|
||||
%span.pull-left.mr-10{"ng-if" => "!selectedEditor"} ✓
|
||||
.menu-item-title.pull-left {{editor.name}}
|
||||
|
||||
%div{"ng-if" => "editors.length > 0"}
|
||||
.menu-section-header
|
||||
.title External Editors
|
||||
.subtitle Can access your current note decrypted.
|
||||
%ul
|
||||
%li.menu-item{"ng-repeat" => "editor in editors", "ng-click" => "selectEditor(editor)"}
|
||||
%span.pull-left.mr-10{"ng-if" => "selectedEditor == editor"} ✓
|
||||
.pull-left{"style" => "width: 60%"}
|
||||
.menu-item-title {{editor.name}}
|
||||
.menu-item-subtitle.wrap {{editor.url}}
|
||||
.pull-right
|
||||
%button.white.medium.inline.top{"style" => "width: 50px; height: 40px;", "ng-click" => "deleteEditor(editor); $event.stopPropagation();"} ☓
|
||||
.menu-section-footer.mt-10
|
||||
%input.form-control{"ng-model" => "formData.url", "placeholder" => "Add new editor via URL", "ng-keyup" => "$event.keyCode == 13 && submitNewEditorRequest()"}
|
||||
%a.block.blue{"href" => "https://standardnotes.org/extensions", "target" => "_blank"} Available Editors
|
||||
@@ -1,8 +1,8 @@
|
||||
.panel.panel-default.account-panel.panel-right
|
||||
.panel-body
|
||||
%div{"style" => "font-size: 18px;", "ng-if" => "!extensionManager.extensions.length"} No extensions installed
|
||||
%div{"style" => "font-size: 15px;", "ng-if" => "!extensionManager.extensions.length"} No extensions installed
|
||||
%div{"ng-if" => "extensionManager.extensions.length"}
|
||||
%section.gray-bg.inline-h.mb-10.medium-padding{"ng-repeat" => "extension in extensionManager.extensions", "ng-init" => "extension.formData = {}"}
|
||||
%section.gray-bg.inline-h.mb-10.medium-padding{"ng-repeat" => "extension in extensionManager.extensions | orderBy: 'name'", "ng-init" => "extension.formData = {}"}
|
||||
%h3.center-align {{extension.name}}
|
||||
.center-align.centered.mt-10
|
||||
%label.block.normal Send data:
|
||||
|
||||
@@ -1,46 +1,44 @@
|
||||
.section.editor{"ng-class" => "{'fullscreen' : ctrl.fullscreen}"}
|
||||
.content
|
||||
.section-title-bar.editor-heading{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"}
|
||||
.title
|
||||
%input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)",
|
||||
"ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()",
|
||||
"select-on-click" => "true"}
|
||||
.save-status{"ng-class" => "{'red bold': ctrl.saveError}", "ng-bind-html" => "ctrl.noteStatus"}
|
||||
.tags
|
||||
%input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && ctrl.updateTagsFromTagsString($event, ctrl.tagsString)",
|
||||
"ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"}
|
||||
.section-menu
|
||||
%ul.nav.nav-pills
|
||||
%li.dropdown
|
||||
%a.dropdown-toggle{"ng-click" => "ctrl.clickedMenu(); ctrl.showExtensions = false"}
|
||||
File
|
||||
%span.caret
|
||||
%span.sr-only
|
||||
.section-title-bar.editor-heading{"ng-if" => "ctrl.note", "ng-class" => "{'fullscreen' : ctrl.fullscreen }"}
|
||||
.title
|
||||
%input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)",
|
||||
"ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()",
|
||||
"select-on-click" => "true"}
|
||||
.save-status{"ng-class" => "{'red bold': ctrl.saveError}", "ng-bind-html" => "ctrl.noteStatus"}
|
||||
.tags
|
||||
%input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();",
|
||||
"ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"}
|
||||
.section-menu{"ng-if" => "ctrl.note"}
|
||||
%ul.nav
|
||||
%li.dropdown.pull-left.mr-10{"click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"}
|
||||
%a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false;"}
|
||||
Menu
|
||||
%span.caret
|
||||
%span.sr-only
|
||||
|
||||
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"}
|
||||
%li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleFullScreen()"}
|
||||
.text Toggle Fullscreen
|
||||
.shortcut Cmd + O
|
||||
%li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleMarkdown()"}
|
||||
.text Toggle Markdown Preview
|
||||
.shortcut Cmd + M
|
||||
%li{"ng-click" => "ctrl.deleteNote()"}
|
||||
.text Delete
|
||||
%li.sep
|
||||
%li.dropdown{"ng-if" => "ctrl.hasAvailableExtensions()"}
|
||||
%a.dropdown-toggle{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false"}
|
||||
Extensions
|
||||
%span.caret
|
||||
%span.sr-only
|
||||
%contextual-extensions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"}
|
||||
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"}
|
||||
%li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleFullScreen()"}
|
||||
.text Toggle Fullscreen
|
||||
%li{"ng-click" => "ctrl.deleteNote()"}
|
||||
.text Delete Note
|
||||
|
||||
.markdown.icon{"ng-if" => "ctrl.editorMode == 'preview'", "ng-click" => "ctrl.showMarkdown = !ctrl.showMarkdown"}
|
||||
.icon-markdown
|
||||
.panel.panel-default.info-panel{"ng-if" => "ctrl.showMarkdown"}
|
||||
.panel-body{"style" => "text-align: center; color: black;"}
|
||||
This editor is Markdown enabled.
|
||||
%li.sep
|
||||
%li.dropdown.pull-left.mr-10{"click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"}
|
||||
%a.dropdown-toggle{"ng-click" => "ctrl.showEditorMenu = !ctrl.showEditorMenu; ctrl.showMenu = false;"}
|
||||
Editor
|
||||
%span.caret
|
||||
%span.sr-only
|
||||
%editor-menu{"ng-if" => "ctrl.showEditorMenu", "callback" => "ctrl.selectedEditor", "selected-editor" => "ctrl.customEditor"}
|
||||
|
||||
.editor-content{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"}
|
||||
%textarea.editable#note-text-editor{"ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-show" => "ctrl.editorMode == 'edit'", "ng-model" => "ctrl.note.text",
|
||||
"ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()"}
|
||||
.preview{"ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-if" => "ctrl.editorMode == 'preview'", "ng-bind-html" => "ctrl.renderedContent()", "ng-dblclick" => "ctrl.onPreviewDoubleClick()"}
|
||||
%li.sep
|
||||
%li.dropdown.pull-left{"ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"}
|
||||
%a.dropdown-toggle{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false;"}
|
||||
Extensions
|
||||
%span.caret
|
||||
%span.sr-only
|
||||
%contextual-extensions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"}
|
||||
|
||||
.editor-content{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"}
|
||||
%iframe#editor-iframe{"ng-if" => "ctrl.customEditor", "ng-src" => "{{ctrl.customEditor.url | trusted}}", "frameBorder" => "0", "style" => "width: 100%;"}
|
||||
%textarea.editable#note-text-editor{"ng-if" => "!ctrl.customEditor", "ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-model" => "ctrl.note.text",
|
||||
"ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()"}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.footer-bar
|
||||
.pull-left
|
||||
.footer-bar-link
|
||||
.footer-bar-link{"click-outside" => "ctrl.showAccountMenu = false;", "is-open" => "ctrl.showAccountMenu"}
|
||||
%a{"ng-click" => "ctrl.accountMenuPressed()", "ng-class" => "{red: ctrl.error}"} Account
|
||||
%account-menu{"ng-if" => "ctrl.showAccountMenu"}
|
||||
|
||||
.footer-bar-link
|
||||
.footer-bar-link{"click-outside" => "ctrl.showExtensionsMenu = false;", "is-open" => "ctrl.showExtensionsMenu"}
|
||||
%a{"ng-click" => "ctrl.toggleExtensions()"} Extensions
|
||||
%global-extensions-menu{"ng-if" => "ctrl.showExtensionsMenu"}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
.main-ui-view
|
||||
.app-container
|
||||
.app
|
||||
%tags-section{"save" => "tagsSave", "add-new" => "tagsAddNew", "will-select" => "tagsWillMakeSelection", "selection-made" => "tagsSelectionMade", "all-tag" => "allTag",
|
||||
"tags" => "tags"}
|
||||
.app
|
||||
%tags-section{"save" => "tagsSave", "add-new" => "tagsAddNew", "will-select" => "tagsWillMakeSelection", "selection-made" => "tagsSelectionMade", "all-tag" => "allTag",
|
||||
"tags" => "tags"}
|
||||
|
||||
%notes-section{"remove-tag" => "notesRemoveTag", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade",
|
||||
"tag" => "selectedTag", "remove" => "deleteNote"}
|
||||
%notes-section{"remove-tag" => "notesRemoveTag", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade",
|
||||
"tag" => "selectedTag", "remove" => "deleteNote"}
|
||||
|
||||
%editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote", "update-tags" => "updateTagsForNote"}
|
||||
%editor-section{"note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote", "update-tags" => "updateTagsForNote"}
|
||||
|
||||
%header
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.about.animated.fadeIn
|
||||
.title About
|
||||
.summary Namewhale helps you find a unique name for your startup. Using an intelligent, seed-based algorithm, names are generated based on the sound, style, and feel of the seed words you chose.
|
||||
.links
|
||||
%a{"href" => "https://itunes.apple.com/us/app/namewhale/id1028881375?ls=1&mt=8", "target" => "_blank"} Namewhale on the AppStore
|
||||
%a{"href" => "https://twitter.com/namewhale", "target" => "_blank"} @namewhale
|
||||
@@ -1,4 +0,0 @@
|
||||
%footer.footer{"ng-class" => "footerClass"}
|
||||
.container
|
||||
.row
|
||||
.footer-about-section
|
||||
@@ -10,17 +10,25 @@
|
||||
%ul.nav.nav-pills
|
||||
%li.dropdown
|
||||
%a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu"}
|
||||
Tag options
|
||||
Menu
|
||||
%span.caret
|
||||
%span.sr-only
|
||||
|
||||
%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"}
|
||||
%li
|
||||
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedSortByCreated()"}
|
||||
%span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'created_at'"} ✓
|
||||
Sort by date created
|
||||
%li
|
||||
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedSortByUpdated()"}
|
||||
%span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'updated_at'"} ✓
|
||||
Sort by date updated
|
||||
%li
|
||||
%a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagDelete()"} Delete Tag
|
||||
|
||||
.scrollable
|
||||
.infinite-scroll{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"}
|
||||
.note{"ng-repeat" => "note in ctrl.tag.notes | filter: ctrl.filterNotes | limitTo:ctrl.notesToDisplay",
|
||||
.note{"ng-repeat" => "note in (ctrl.sortedNotes = (ctrl.tag.notes | filter: ctrl.filterNotes | orderBy: ctrl.sortBy:true | limitTo:ctrl.notesToDisplay))",
|
||||
"ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"}
|
||||
.name{"ng-if" => "note.title"}
|
||||
{{note.title}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
# Prevent CSRF attacks by raising an exception.
|
||||
# For APIs, you may want to use :null_session instead.
|
||||
|
||||
protect_from_forgery with: :null_session
|
||||
after_action :set_csrf_cookie
|
||||
|
||||
after_action :allow_iframe
|
||||
|
||||
layout :false
|
||||
|
||||
def frontend
|
||||
@@ -13,8 +13,13 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
rescue_from ActionView::MissingTemplate do |exception|
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def allow_iframe
|
||||
response.headers.except! 'X-Frame-Options'
|
||||
end
|
||||
|
||||
def set_app_domain
|
||||
@appDomain = request.domain
|
||||
@appDomain << ':' + request.port.to_s unless request.port.blank?
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
</head>
|
||||
|
||||
<body ng-class="bodyClass">
|
||||
<body>
|
||||
<div ui-view="content"></div>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
"dependencies": {
|
||||
"angular": "1.6.1",
|
||||
"angular-ui-router": "^0.3.2",
|
||||
"restangular": "^1.6.1",
|
||||
"marked": "0.3.6"
|
||||
"restangular": "^1.6.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.6.1"
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1000 B After Width: | Height: | Size: 1.0 KiB |
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/favicon/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
||||
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,17 +1,18 @@
|
||||
{
|
||||
"name": "Neeto",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/favicon\/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image\/png"
|
||||
},
|
||||
{
|
||||
"src": "\/favicon\/android-chrome-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image\/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
"name": "Standard Notes",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -2,19 +2,19 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="500.000000pt" height="500.000000pt" viewBox="0 0 500.000000 500.000000"
|
||||
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,500.000000) scale(0.100000,-0.100000)"
|
||||
<g transform="translate(0.000000,16.000000) scale(0.003200,-0.003200)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M230 2490 l0 -2270 2270 0 2270 0 0 2270 0 2270 -2270 0 -2270 0 0
|
||||
-2270z m4340 0 l0 -2070 -2070 0 -2070 0 0 2070 0 2070 2070 0 2070 0 0 -2070z"/>
|
||||
<path d="M2388 3475 c-2 -2 -26 -5 -55 -9 -78 -9 -161 -30 -231 -59 -254 -102
|
||||
-404 -309 -438 -602 -5 -45 -9 -1139 -5 -1237 1 -15 20 -16 204 -15 l202 2 2
|
||||
597 c1 509 4 606 17 659 50 192 187 296 402 304 201 8 343 -64 417 -212 46
|
||||
-91 48 -126 49 -757 l0 -594 206 2 207 1 -1 600 c0 330 -3 629 -7 665 -15 143
|
||||
-80 299 -165 399 -109 128 -290 220 -487 247 -49 6 -311 14 -317 9z"/>
|
||||
<path d="M2368 3475 c-2 -1 -23 -5 -48 -8 -298 -37 -515 -205 -603 -467 -14
|
||||
-41 -29 -91 -33 -110 -4 -19 -9 -328 -11 -687 l-4 -651 168 1 168 2 2 606 c2
|
||||
648 4 683 54 782 82 162 231 240 449 234 255 -7 412 -140 454 -387 3 -14 5
|
||||
-298 5 -632 l1 -606 168 1 167 2 0 645 c-1 634 -1 646 -23 730 -81 306 -261
|
||||
468 -597 535 -31 7 -311 15 -317 10z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1005 B |
@@ -3,7 +3,7 @@ describe("app.frontend", function() {
|
||||
beforeEach(module('app.frontend'));
|
||||
|
||||
describe('Home Controller', function() {
|
||||
|
||||
|
||||
var scope;
|
||||
beforeEach(inject(function($rootScope, $controller, modelManager) {
|
||||
scope = $rootScope.$new();
|
||||
@@ -13,10 +13,6 @@ describe("app.frontend", function() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should have a body class', function() {
|
||||
expect(scope.bodyClass).toEqual('app-body-class');
|
||||
});
|
||||
|
||||
it('should have an All tag', function() {
|
||||
expect(scope.allTag).toBeDefined();
|
||||
expect(scope.allTag.title).toEqual("All");
|
||||
@@ -31,7 +27,7 @@ describe("app.frontend", function() {
|
||||
scope.tagsAddNew("testTag");
|
||||
expect($modelManager.items).toContain("testTag");
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
BIN
vendor/assets/fonts/icomoon/icomoon.eot
vendored
15
vendor/assets/fonts/icomoon/icomoon.svg
vendored
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="icomoon" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||
<glyph unicode="" glyph-name="keyboard" d="M416 384c17.664 0 32 14.304 32 32s-14.336 32-32 32-32-14.304-32-32 14.336-32 32-32zM512 512c17.696 0 32 14.304 32 32s-14.304 32-32 32-32-14.304-32-32 14.304-32 32-32zM416 640c17.664 0 32 14.304 32 32s-14.336 32-32 32-32-14.304-32-32 14.336-32 32-32zM608 640c17.696 0 32 14.304 32 32s-14.304 32-32 32-32-14.304-32-32 14.304-32 32-32zM320 512c17.664 0 32 14.304 32 32s-14.336 32-32 32-32-14.304-32-32 14.336-32 32-32zM800 640c17.696 0 32 14.304 32 32s-14.304 32-32 32-32-14.304-32-32 14.304-32 32-32zM704 512c17.696 0 32 14.304 32 32s-14.304 32-32 32-32-14.304-32-32 14.304-32 32-32zM608 384c17.696 0 32 14.304 32 32s-14.304 32-32 32-32-14.304-32-32 14.304-32 32-32zM864 576c-17.696 0-32-14.304-32-32s14.304-32 32-32 32 14.304 32 32-14.304 32-32 32zM960 832h-896c-35.328 0-64-28.672-64-64v-640c0-35.328 28.672-64 64-64h896c35.328 0 64 28.672 64 64v640c0 35.328-28.672 64-64 64zM960 160c0-17.696-14.304-32-32-32h-832c-17.664 0-32 14.304-32 32v576c0 17.696 14.336 32 32 32h832c17.696 0 32-14.304 32-32v-576zM736 256h-448c-17.664 0-32-14.304-32-32s14.336-32 32-32h448c17.696 0 32 14.304 32 32s-14.304 32-32 32zM160 576c-17.664 0-32-14.304-32-32s14.336-32 32-32 32 14.304 32 32-14.336 32-32 32zM224 448c-17.664 0-32-14.304-32-32s14.336-32 32-32 32 14.304 32 32-14.336 32-32 32zM224 640c17.664 0 32 14.304 32 32s-14.336 32-32 32-32-14.304-32-32 14.336-32 32-32zM800 448c-17.696 0-32-14.304-32-32s14.304-32 32-32 32 14.304 32 32-14.304 32-32 32z" />
|
||||
<glyph unicode="" glyph-name="markdown" d="M950.154 768h-876.308c-40.719 0-73.846-33.127-73.846-73.846v-492.308c0-40.721 33.127-73.846 73.846-73.846h876.308c40.721 0 73.846 33.125 73.846 73.846v492.308c0 40.719-33.125 73.846-73.846 73.846zM576 256.125l-128-0.125v192l-96-123.077-96 123.077v-192h-128v384h128l96-128 96 128 128 0.125v-384zM767.091 224.125l-159.091 223.875h96v192h128v-192h96l-160.909-223.875z" />
|
||||
<glyph unicode="" glyph-name="enlarge" d="M1024 960h-416l160-160-192-192 96-96 192 192 160-160zM1024-64v416l-160-160-192 192-96-96 192-192-160-160zM0-64h416l-160 160 192 192-96 96-192-192-160 160zM0 960v-416l160 160 192-192 96 96-192 192 160 160z" />
|
||||
<glyph unicode="" glyph-name="lock" d="M592 512h-16v192c0 105.87-86.13 192-192 192h-128c-105.87 0-192-86.13-192-192v-192h-16c-26.4 0-48-21.6-48-48v-480c0-26.4 21.6-48 48-48h544c26.4 0 48 21.6 48 48v480c0 26.4-21.6 48-48 48zM192 704c0 35.29 28.71 64 64 64h128c35.29 0 64-28.71 64-64v-192h-256v192z" />
|
||||
<glyph unicode="" glyph-name="rss2" d="M928 960h-832c-52.8 0-96-43.2-96-96v-832c0-52.8 43.2-96 96-96h832c52.8 0 96 43.2 96 96v832c0 52.8-43.2 96-96 96zM279 128.8c-48 0-87 38.6-87 86.6 0 47.6 39 86.8 87 86.8 48.2 0 87-39.2 87-86.8 0-48-39-86.6-87-86.6zM497.4 128c0 81.8-31.8 158.8-89.4 216.4-57.8 57.8-134.4 89.6-216 89.6v125.2c237.6 0 431.2-193.4 431.2-431.2h-125.8zM719.6 128c0 291-236.6 528-527.4 528v125.2c360 0 653-293.2 653-653.2h-125.6z" />
|
||||
</font></defs></svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |