This commit is contained in:
Mo Bitar
2016-12-15 12:52:34 -06:00
parent 1d4905d17d
commit 6a2e3e9ec1
34 changed files with 2594 additions and 264 deletions

3
.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["env"]
}

View File

@@ -13,7 +13,7 @@ module.exports = function(grunt) {
js: { js: {
files: ['app/assets/javascripts/**/*.js'], files: ['app/assets/javascripts/**/*.js'],
tasks: ['concat', 'babel'], tasks: [ 'concat:app', 'babel', 'browserify', 'concat:dist'],
options: { options: {
spawn: false, spawn: false,
}, },
@@ -21,7 +21,7 @@ module.exports = function(grunt) {
css: { css: {
files: ['app/assets/stylesheets/**/*.scss'], files: ['app/assets/stylesheets/**/*.scss'],
tasks: ['sass', 'concat'], tasks: ['sass', 'concat:css'],
options: { options: {
spawn: false, spawn: false,
}, },
@@ -73,7 +73,7 @@ module.exports = function(grunt) {
'app/assets/javascripts/app/*.js', 'app/assets/javascripts/app/*.js',
'app/assets/javascripts/app/frontend/*.js', 'app/assets/javascripts/app/frontend/*.js',
'app/assets/javascripts/app/frontend/controllers/*.js', 'app/assets/javascripts/app/frontend/controllers/*.js',
'app/assets/javascripts/app/frontend/models/*.js', 'app/assets/javascripts/app/frontend/models/**/*.js',
'app/assets/javascripts/app/services/*.js', 'app/assets/javascripts/app/services/*.js',
'app/assets/javascripts/app/services/directives/*.js', 'app/assets/javascripts/app/services/directives/*.js',
'app/assets/javascripts/app/services/helpers/*.js', 'app/assets/javascripts/app/services/helpers/*.js',
@@ -99,7 +99,7 @@ module.exports = function(grunt) {
}, },
dist: { dist: {
src: ['vendor/assets/javascripts/lib.js', 'vendor/assets/javascripts/app.js', 'vendor/assets/javascripts/templates.js'], src: ['vendor/assets/javascripts/lib.js', 'vendor/assets/javascripts/transpiled.js', 'vendor/assets/javascripts/templates.js'],
dest: 'vendor/assets/javascripts/compiled.js', dest: 'vendor/assets/javascripts/compiled.js',
}, },
@@ -117,14 +117,24 @@ module.exports = function(grunt) {
babel: { babel: {
options: { options: {
sourceMap: true, sourceMap: true,
presets: ['es2015'] presets: ['es2016']
}, },
dist: { dist: {
files: { files: {
'vendor/assets/javascripts/app.js': 'vendor/assets/javascripts/app.js' 'vendor/assets/javascripts/transpiled.js': 'vendor/assets/javascripts/app.js'
} }
} }
} },
browserify: {
dist: {
files: {
'vendor/assets/javascripts/transpiled.js': 'vendor/assets/javascripts/transpiled.js'
},
options: {
}
}
},
ngAnnotate: { ngAnnotate: {
options: { options: {
@@ -156,6 +166,9 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-ng-annotate'); grunt.loadNpmTasks('grunt-ng-annotate');
grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-browserify');
grunt.registerTask('default', ['haml', 'ngtemplates', 'sass', 'concat', 'babel', 'ngAnnotate', 'uglify']); grunt.registerTask('default', ['haml', 'ngtemplates', 'sass', 'concat:app', 'babel', 'browserify',
'concat:lib', 'concat:dist', 'concat:css', 'ngAnnotate', 'uglify']);
}; };

View File

@@ -4,13 +4,32 @@ var Neeto = Neeto || {};
angular angular
.module('app.frontend', [ .module('app.frontend', [
'app.services',
'ui.router', 'ui.router',
'ng-token-auth', 'ng-token-auth',
'restangular', 'restangular',
'ipCookie', 'ipCookie',
'oc.lazyLoad', 'oc.lazyLoad',
'angularLazyImg', 'angularLazyImg',
'ngDialog', 'ngDialog'
]) ])
.config(configureAuth); // Configure path to API
.config(function (RestangularProvider, apiControllerProvider) {
var url = apiControllerProvider.defaultServerURL();
RestangularProvider.setBaseUrl(url);
console.log(url);
RestangularProvider.setFullRequestInterceptor(function(element, operation, route, url, headers, params, httpConfig) {
var token = localStorage.getItem("jwt");
if(token) {
headers = _.extend(headers, {Authorization: "Bearer " + localStorage.getItem("jwt")});
}
return {
element: element,
params: params,
headers: headers,
httpConfig: httpConfig
};
});
})
}

View File

@@ -1,37 +0,0 @@
'use strict';
angular
.module('app.services', [
'restangular'
])
// Configure path to API
.config(function (RestangularProvider, apiControllerProvider) {
var url = apiControllerProvider.defaultServerURL();
RestangularProvider.setBaseUrl(url);
RestangularProvider.setFullRequestInterceptor(function(element, operation, route, url, headers, params, httpConfig) {
var token = localStorage.getItem("jwt");
if(token) {
headers = _.extend(headers, {Authorization: "Bearer " + localStorage.getItem("jwt")});
}
return {
element: element,
params: params,
headers: headers,
httpConfig: httpConfig
};
});
})
// Shared function for configure auth service. Can be overwritten.
function configureAuth ($authProvider, apiControllerProvider) {
var url = apiControllerProvider.defaultServerURL();
$authProvider.configure([{
default: {
apiUrl: url,
passwordResetSuccessUrl: window.location.protocol + '//' + window.location.host + '/auth/reset',
}
}]);
}

View File

@@ -1,21 +1,13 @@
angular.module('app.frontend') class BaseCtrl {
.controller('BaseCtrl', function ($rootScope, $scope, $state, $auth, apiController) { constructor($rootScope, modelManager) {
$rootScope.$on('auth:password-change-success', function(ev) { // $rootScope.resetPasswordSubmit = function() {
$state.go("home"); // var new_keys = Neeto.crypto.generateEncryptionKeysForUser($rootScope.resetData.password, $rootScope.resetData.email);
}); // var data = _.clone($rootScope.resetData);
// data.password = new_keys.pw;
$rootScope.$on('auth:password-change-error', function(ev, reason) { // data.password_confirmation = new_keys.pw;
alert("Error: " + reason); // $auth.updatePassword(data);
}); // apiController.setGk(new_keys.gk);
// }
$rootScope.resetPasswordSubmit = function() {
var new_keys = Neeto.crypto.generateEncryptionKeysForUser($rootScope.resetData.password, $rootScope.resetData.email);
var data = _.clone($rootScope.resetData);
data.password = new_keys.pw;
data.password_confirmation = new_keys.pw;
$auth.updatePassword(data);
apiController.setGk(new_keys.gk);
}
// var note = new Note(); // var note = new Note();
// note.content = {title: "hello", text: "world"}; // note.content = {title: "hello", text: "world"};
@@ -24,5 +16,7 @@ angular.module('app.frontend')
// console.log("note json", JSON.stringify(note)); // console.log("note json", JSON.stringify(note));
// //
// console.log("Copy", _.cloneDeep(note)); // console.log("Copy", _.cloneDeep(note));
}
}
}); angular.module('app.frontend').controller('BaseCtrl', BaseCtrl);

View File

@@ -259,14 +259,14 @@ angular.module('app.frontend')
a.click(); a.click();
} }
apiController.shareNote(this.user, this.note, function(note){ apiController.shareItem(this.user, this.note, function(note){
openInNewTab(this.publicUrlForNote(note)); openInNewTab(this.publicUrlForNote(note));
}.bind(this)) }.bind(this))
this.showMenu = false; this.showMenu = false;
} }
this.unshareNote = function() { this.unshareNote = function() {
apiController.unshareNote(this.user, this.note, function(note){ apiController.unshareItem(this.user, this.note, function(note){
}) })
this.showMenu = false; this.showMenu = false;

View File

@@ -27,7 +27,7 @@ angular.module('app.frontend')
} }
} }
}) })
.controller('GroupsCtrl', function (apiController) { .controller('GroupsCtrl', function () {
var initialLoad = true; var initialLoad = true;
@@ -54,8 +54,8 @@ angular.module('app.frontend')
} }
this.newGroup = new Group({notes : []}); this.newGroup = new Group({notes : []});
if(!this.user.id) { if(!this.user.uuid) {
this.newGroup.id = Neeto.crypto.generateRandomKey() this.newGroup.uuid = Neeto.crypto.generateRandomKey()
} }
this.selectedGroup = this.newGroup; this.selectedGroup = this.newGroup;
this.editingGroup = this.newGroup; this.editingGroup = this.newGroup;
@@ -97,18 +97,6 @@ angular.module('app.frontend')
} }
this.handleDrop = function(e, newGroup, note) { this.handleDrop = function(e, newGroup, note) {
if(this.selectedGroup.all) {
// coming from all, remove from original group if applicable
if(note.group_id) {
var originalGroup = this.groups.filter(function(group){
return group.id == note.group_id;
})[0];
_.remove(originalGroup.notes, note);
}
} else {
_.remove(this.selectedGroup.notes, note);
}
this.updateNoteGroup()(note, newGroup, this.selectedGroup); this.updateNoteGroup()(note, newGroup, this.selectedGroup);
}.bind(this) }.bind(this)

View File

@@ -1,5 +1,5 @@
angular.module('app.frontend') angular.module('app.frontend')
.controller('HomeCtrl', function ($scope, $rootScope, Restangular, $timeout, $state, $sce, $auth, apiController) { .controller('HomeCtrl', function ($scope, $rootScope, $timeout, apiController, modelManager) {
$rootScope.bodyClass = "app-body-class"; $rootScope.bodyClass = "app-body-class";
$rootScope.title = "Notes — Neeto, a secure code box for developers"; $rootScope.title = "Notes — Neeto, a secure code box for developers";
$rootScope.description = "A secure code box for developers to store common commands and useful notes."; $rootScope.description = "A secure code box for developers to store common commands and useful notes.";
@@ -7,7 +7,7 @@ angular.module('app.frontend')
var onUserSet = function() { var onUserSet = function() {
$scope.allGroup = new Group({name: "All", all: true}); $scope.allGroup = new Group({name: "All", all: true});
$scope.groups = $scope.defaultUser.groups; $scope.groups = modelManager.groups;
apiController.verifyEncryptionStatusOfAllItems($scope.defaultUser, function(success){ apiController.verifyEncryptionStatusOfAllItems($scope.defaultUser, function(success){
@@ -16,7 +16,8 @@ angular.module('app.frontend')
apiController.getCurrentUser(function(response){ apiController.getCurrentUser(function(response){
if(response && !response.errors) { if(response && !response.errors) {
$scope.defaultUser = new User(response.plain()); $scope.defaultUser = new User(response);
modelManager.items = response.items;
$rootScope.title = "Notes — Neeto"; $rootScope.title = "Notes — Neeto";
onUserSet(); onUserSet();
} else { } else {
@@ -30,9 +31,7 @@ angular.module('app.frontend')
*/ */
$scope.updateAllGroup = function() { $scope.updateAllGroup = function() {
var allNotes = Note.filterDummyNotes($scope.defaultUser.notes); $scope.allGroup.notes = modelManager.filteredNotes;
$scope.defaultUser.notes = allNotes;
$scope.allGroup.notes = allNotes;
} }
$scope.groupsWillMakeSelection = function(group) { $scope.groupsWillMakeSelection = function(group) {
@@ -49,11 +48,11 @@ angular.module('app.frontend')
} }
$scope.groupsAddNew = function(group) { $scope.groupsAddNew = function(group) {
$scope.defaultUser.groups.unshift(group); modelManager.addTag(group);
} }
$scope.groupsSave = function(group, callback) { $scope.groupsSave = function(group, callback) {
apiController.saveItem($scope.defaultUser, group, callback); apiController.saveItems([group], callback);
} }
/* /*
@@ -62,18 +61,13 @@ angular.module('app.frontend')
*/ */
$scope.groupsUpdateNoteGroup = function(noteCopy, newGroup, oldGroup) { $scope.groupsUpdateNoteGroup = function(noteCopy, newGroup, oldGroup) {
var originalNote = _.find($scope.defaultUser.notes, {id: noteCopy.id}); var originalNote = _.find($scope.defaultUser.notes, {uuid: noteCopy.uuid});
modelManager.removeTagFromNote(oldGroup, originalNote);
$scope.defaultUser.itemManager.removeReferencesBetweenItems(oldGroup, originalNote);
if(!newGroup.all) { if(!newGroup.all) {
$scope.defaultUser.itemManager.createReferencesBetweenItems(newGroup, originalNote); modelManager.addTagToNote(newGroup, originalNote);
newGroup.updateReferencesLocalMapping();
} }
apiController.saveBatchItems($scope.defaultUser, [originalNote, newGroup, oldGroup], function(){ apiController.saveDirtyItems(function(){});
});
} }
/* /*
@@ -88,7 +82,7 @@ angular.module('app.frontend')
// force scope groups to update on sub directives // force scope groups to update on sub directives
$scope.groups = []; $scope.groups = [];
$timeout(function(){ $timeout(function(){
$scope.groups = $scope.defaultUser.groups; $scope.groups = modelManager.groups;
}) })
}); });
} else { } else {
@@ -106,12 +100,10 @@ angular.module('app.frontend')
note.id = Neeto.crypto.generateRandomKey(); note.id = Neeto.crypto.generateRandomKey();
} }
$scope.defaultUser.notes.unshift(note); modelManager.addNote(note);
if(!$scope.selectedGroup.all) { if(!$scope.selectedGroup.all) {
$scope.selectedGroup.notes.unshift(note); modelManager.addTagToNote($scope.selectedGroup, note);
note.group_id = $scope.selectedGroup.id;
} }
} }
@@ -120,11 +112,8 @@ angular.module('app.frontend')
*/ */
$scope.saveNote = function(note, callback) { $scope.saveNote = function(note, callback) {
apiController.saveNote($scope.defaultUser, note, function(){ apiController.saveItems([note], function(){
// add to All notes if it doesnt exist modelManager.addNote(note);
if(!_.find($scope.defaultUser.notes, {id: note.id})) {
$scope.defaultUser.notes.unshift(note);
}
note.hasChanges = false; note.hasChanges = false;
if(callback) { if(callback) {
@@ -134,15 +123,8 @@ angular.module('app.frontend')
} }
$scope.deleteNote = function(note) { $scope.deleteNote = function(note) {
_.remove($scope.defaultUser.notes, note);
if($scope.selectedGroup.all && note.group_id) { modelManager.deleteNote(note);
var originalGroup = _.find($scope.groups, {id: note.group_id});
if(originalGroup) {
_.remove(originalGroup.notes, note);
}
} else {
_.remove($scope.selectedGroup.notes, note);
}
if(note == $scope.selectedNote) { if(note == $scope.selectedNote) {
$scope.selectedNote = null; $scope.selectedNote = null;
@@ -152,8 +134,8 @@ angular.module('app.frontend')
return; return;
} }
apiController.deleteNote($scope.defaultUser, note, function(success){ apiController.deleteItem($scope.defaultUser, note, function(success){})
}) apiController.saveDirtyItems(function(){});
} }
/* /*

View File

@@ -77,7 +77,7 @@ angular.module('app.frontend')
} }
var callback = function(username) { var callback = function(username) {
apiController.shareGroup(this.user, this.group, function(response){ apiController.shareItem(this.user, this.group, function(response){
}) })
}.bind(this); }.bind(this);
@@ -99,7 +99,7 @@ angular.module('app.frontend')
this.selectedGroupUnshare = function() { this.selectedGroupUnshare = function() {
this.showMenu = false; this.showMenu = false;
apiController.unshareGroup(this.user, this.group, function(response){ apiController.unshareItem(this.user, this.group, function(response){
}) })
} }
@@ -138,7 +138,7 @@ angular.module('app.frontend')
var title = "New Note" + (this.notes ? (" " + (this.notes.length + 1)) : ""); var title = "New Note" + (this.notes ? (" " + (this.notes.length + 1)) : "");
this.newNote = new Note({dummy: true}); this.newNote = new Note({dummy: true});
this.newNote.content.title = title; this.newNote.content.title = title;
this.newNote.group = this.group; modelManager.addTagToNote(this.group, this.newNote);
this.selectNote(this.newNote); this.selectNote(this.newNote);
this.addNew()(this.newNote); this.addNew()(this.newNote);
} }

View File

@@ -1,4 +1,5 @@
class Note extends Item { class Item {
constructor(json_obj) { constructor(json_obj) {
var content; var content;
@@ -24,40 +25,37 @@ class Note extends Item {
enumerable: true, enumerable: true,
}); });
_.merge(this, json_obj);
this.setContentRaw = function(rawContent) { this.setContentRaw = function(rawContent) {
content = rawContent; content = rawContent;
} }
_.merge(this, json_obj);
if(!this.content) {
this.content = {title: "", text: ""};
}
} }
filterDummyNotes(notes) { referencesMatchingContentType(contentType) {
var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null}); return this.references.filter(function(reference){
return filtered; return reference.content_type == content_type;
});
} }
updateReferencesLocalMapping() { updateReferencesLocalMapping() {
super.updateReferencesLocalMapping(); // should be overriden to manage local properties
this.groups = this.referencesMatchingContentType("Group");
} }
get hasOnePublicGroup() { /* Returns true if note is shared individually or via group */
var hasPublicGroup = false; isPublic() {
this.groups.forEach(function(group){ return this.presentation;
if(group.isPublic()) {
hasPublicGroup = true;
return;
}
})
return hasPublicGroup;
} }
function isPublic() { isEncrypted() {
return super.isPublic() || this.hasOnePublicGroup; return this.encryptionEnabled() && typeof this.content === 'string' ? true : false;
}
encryptionEnabled() {
return this.loc_eek;
}
presentationURL() {
return this.presentation.url;
} }
} }

View File

@@ -0,0 +1,36 @@
class Note extends Item {
constructor(json_obj) {
super(json_obj);
if(!this.content) {
this.content = {title: "", text: ""};
}
}
filterDummyNotes(notes) {
var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null});
return filtered;
}
get hasOnePublicGroup() {
var hasPublicGroup = false;
this.groups.forEach(function(group){
if(group.isPublic()) {
hasPublicGroup = true;
return;
}
})
return hasPublicGroup;
}
isPublic() {
return super.isPublic() || this.hasOnePublicGroup;
}
get content_type() {
return "Note";
}
}

View File

@@ -0,0 +1,10 @@
class Tag extends Item {
constructor(json_obj) {
super(json_obj);
}
get content_type() {
return "Tag";
}
}

View File

@@ -0,0 +1,5 @@
class User {
constructor(json_obj) {
_.merge(this, json_obj);
}
}

View File

@@ -1,13 +0,0 @@
class Group extends Item {
constructor(json_obj) {
_.merge(this, json_obj);
}
updateReferencesLocalMapping() {
super.updateReferencesLocalMapping();
this.notes = this.referencesMatchingContentType("Note");
this.notes.sort(function(a,b){
return new Date(b.created_at) - new Date(a.created_at);
});
}
}

View File

@@ -1,29 +0,0 @@
class Item {
referencesMatchingContentType(contentType) {
return this.references.filter(function(reference){
return reference.content_type == content_type;
});
}
updateReferencesLocalMapping() {
// should be overriden to manage local properties
}
/* Returns true if note is shared individually or via group */
isPublic() {
return this.presentation;
}
isEncrypted() {
return this.encryptionEnabled() && typeof this.content === 'string' ? true : false;
}
encryptionEnabled() {
return this.loc_eek;
}
presentationURL() {
return this.presentation.url;
}
}

View File

@@ -1,23 +0,0 @@
class User {
constructor(json_obj) {
_.merge(this, json_obj);
this.itemManager = new ItemManager();
this.itemManager.items = this.items;
this.items = null;
this.notes = _.map(this.itemManager.itemsForContentType("Note"), function(json_obj) {
return new Note(json_obj);
})
this.groups = _.map(this.itemManager.itemsForContentType("Group"), function(json_obj) {
var group = Group(json_obj);
group.updateReferencesLocalMapping();
return group;
})
}
filteredNotes() {
return Note.filterDummyNotes(this.notes);
}
}

View File

@@ -1,4 +1,4 @@
angular.module('app.services') angular.module('app.frontend')
.provider('apiController', function () { .provider('apiController', function () {
function domainName() { function domainName() {
@@ -59,7 +59,7 @@ angular.module('app.services')
return; return;
} }
Restangular.one("users/current").get().then(function(response){ Restangular.one("users/current").get().then(function(response){
callback(response); callback(response.plain());
}) })
} }
@@ -171,37 +171,26 @@ angular.module('app.services')
Items Items
*/ */
this.saveBatchItems = function(user, items, callback) { this.saveDirtyItems = function(callback) {
var request = Restangular.one("users", user.uuid).one("items/batch_update"); var dirtyItems = modelManager.dirtyItems;
request.items = _.map(items, function(item){
return this.createRequestParamsFromItem(item, user); this.saveItems(dirtyItems, function(response){
}.bind(this)); modelManager.clearDirtyItems();
request.put().then(function(response){
var success = response.plain().success;
callback(success);
}) })
} }
this.saveItem = function(user, item, callback) { this.saveItems = function(items, callback) {
if(!user.id) { var request = Restangular.one("users", user.uuid).one("items");
this.writeUserToLocalStorage(user); request.items = _.map(items, function(item){
callback(item); return this.createRequestParamsFromItem(item, user);
return; }.bind(this));
}
var params = this.createRequestParamsForItem(item, user); request.post().then(function(response) {
var savedItems = response.items;
var request = Restangular.one("users", user.uuid).one("item", item.uuid); items.forEach(function(item){
_.merge(request, params); _.merge(item, _.find(savedItems, {uuid: item.uuid}));
request.customOperation(request.uuid ? "put" : "post") })
.then(function(response) { callback(response);
var responseObject = response.plain();
responseObject.content = item.content;
_.merge(item, responseObject);
callback(item);
})
.catch(function(response){
callback(null);
}) })
} }

View File

@@ -1,5 +1,5 @@
angular angular
.module('app.services') .module('app.frontend')
.directive('mbAutofocus', ['$timeout', function($timeout) { .directive('mbAutofocus', ['$timeout', function($timeout) {
return { return {
restrict: 'A', restrict: 'A',

View File

@@ -1,5 +1,5 @@
angular angular
.module('app.services') .module('app.frontend')
.directive('draggable', function() { .directive('draggable', function() {
return { return {
scope: { scope: {
@@ -35,7 +35,7 @@ angular
}); });
angular angular
.module('app.services') .module('app.frontend')
.directive('droppable', function() { .directive('droppable', function() {
return { return {
scope: { scope: {

View File

@@ -1,5 +1,5 @@
angular angular
.module('app.services') .module('app.frontend')
.directive('fileChange', function() { .directive('fileChange', function() {
return { return {
restrict: 'A', restrict: 'A',

View File

@@ -1,5 +1,5 @@
angular angular
.module('app.services') .module('app.frontend')
.directive('lowercase', function() { .directive('lowercase', function() {
return { return {
require: 'ngModel', require: 'ngModel',

View File

@@ -1,5 +1,5 @@
angular angular
.module('app.services') .module('app.frontend')
.directive('selectOnClick', ['$window', function ($window) { .directive('selectOnClick', ['$window', function ($window) {
return { return {
restrict: 'A', restrict: 'A',

View File

@@ -1,5 +1,5 @@
angular angular
.module('app.services') .module('app.frontend')
.directive('note', function($timeout) { .directive('note', function($timeout) {
return { return {
restrict: 'E', restrict: 'E',

View File

@@ -30,7 +30,7 @@
angular angular
.module('app.services').directive('typewrite', ['$timeout', function ($timeout) { .module('app.frontend').directive('typewrite', ['$timeout', function ($timeout) {
function linkFunction($scope, $element, $attrs) { function linkFunction($scope, $element, $attrs) {
var timer = null, var timer = null,
initialDelay = $attrs.initialDelay ? getTypeDelay($attrs.initialDelay) : 200, initialDelay = $attrs.initialDelay ? getTypeDelay($attrs.initialDelay) : 200,

View File

@@ -1,4 +1,4 @@
class ItemManager() { class ItemManager {
set items(items) { set items(items) {
this.items = items; this.items = items;
@@ -27,8 +27,8 @@ class ItemManager() {
// returns dirty item references that need saving // returns dirty item references that need saving
deleteItem(item) { deleteItem(item) {
_.remove(this.items, item); _.remove(this.items, item);
item.references.forEach(function(reference){ item.references.forEach(function(referencedItem){
removeReferencesFromItem(reference, item); removeReferencesBetweenItems(referencedItem, item);
}) })
return item.references; return item.references;
@@ -40,16 +40,11 @@ class ItemManager() {
return [itemOne, itemTwo]; return [itemOne, itemTwo];
} }
removeReferencesBetweenItems(itemOne, itemTwo) {
itemOne.references.push(itemTwo);
itemTwo.references.push(itemOne);
return [itemOne, itemTwo];
}
createReferencesBetweenItems(itemOne, itemTwo) { createReferencesBetweenItems(itemOne, itemTwo) {
itemOne.references.push(itemTwo); itemOne.references.push(itemTwo);
itemTwo.references.push(itemOne); itemTwo.references.push(itemOne);
return [itemOne, itemTwo]; return [itemOne, itemTwo];
} }
} }
angular.module('app.frontend').service('itemManager', ItemManager);

View File

@@ -1,4 +1,4 @@
angular.module('app.services') angular.module('app.frontend')
.service('markdownRenderer', function ($sce) { .service('markdownRenderer', function ($sce) {
marked.setOptions({ marked.setOptions({

View File

@@ -0,0 +1,85 @@
class ModelManager extends ItemManager {
set items(items) {
super.items = items;
this.notes = _.map(this.items.itemsForContentType("Note"), function(json_obj) {
return new Note(json_obj);
})
this.groups = _.map(this.items.itemsForContentType("Group"), function(json_obj) {
var group = Group(json_obj);
group.updateReferencesLocalMapping();
return group;
})
}
addDirtyItems(items) {
if(this.dirtyItems) {
this.dirtyItems = [];
}
this.dirtyItems.concat(items);
}
get dirtyItems() {
return this.dirtyItems || [];
}
get filteredNotes() {
return Note.filterDummyNotes(this.notes);
}
clearDirtyItems() {
this.dirtyItems = [];
}
addNote(note) {
if(!_.find(this.notes, {uuid: note.uuid})) {
this.notes.unshift(note);
}
}
addTag(tag) {
this.tags.unshift(tag);
}
addTagToNote(tag, note) {
var dirty = this.createReferencesBetweenItems(tag, note);
this.refreshRelationshipsForTag(tag);
this.refreshRelationshipsForNote(note);
this.addDirtyItems(dirty);
}
refreshRelationshipsForTag(tag) {
tag.notes = tag.referencesMatchingContentType("Note");
tag.notes.sort(function(a,b){
return new Date(b.created_at) - new Date(a.created_at);
});
}
refreshRelationshipsForNote(note) {
note.groups = note.referencesMatchingContentType("Group");
}
removeTagFromNote(tag, note) {
var dirty = this.removeReferencesBetweenItems(tag, note);
this.addDirtyItems(dirty);
}
deleteNote(note) {
var dirty = this.deleteItem(note);
this.addDirtyItems(dirty);
}
deleteTag(tag) {
var dirty = this.deleteItem(tag);
this.addDirtyItems(dirty);
}
filteredNotes() {
return Note.filterDummyNotes(this.notes);
}
}
angular.module('app.frontend').service('modelManager', ModelManager);

View File

@@ -1,4 +1,4 @@
angular.module('app.services') angular.module('app.frontend')
.service('serverSideValidation', function ($sce) { .service('serverSideValidation', function ($sce) {
// Show validation errors in form. // Show validation errors in form.
this.showErrors = function (formErrors, form) { this.showErrors = function (formErrors, form) {

View File

@@ -1,4 +1,4 @@
//= require app/app.services.js //= require app/app.frontend.js
//= require_tree ./app/services //= require_tree ./app/services
//= require app/app.frontend.js //= require app/app.frontend.js

View File

@@ -2,10 +2,14 @@
"name": "neeto", "name": "neeto",
"version": "1.0.0", "version": "1.0.0",
"devDependencies": { "devDependencies": {
"babel-cli": "^6.18.0",
"babel-preset-env": "^1.1.1",
"babel-preset-es2015": "^6.18.0", "babel-preset-es2015": "^6.18.0",
"babel-preset-es2016": "^6.16.0",
"grunt": "^1.0.1", "grunt": "^1.0.1",
"grunt-angular-templates": "^1.1.0", "grunt-angular-templates": "^1.1.0",
"grunt-babel": "^6.0.0", "grunt-babel": "^6.0.0",
"grunt-browserify": "^5.0.0",
"grunt-contrib-concat": "^1.0.1", "grunt-contrib-concat": "^1.0.1",
"grunt-contrib-cssmin": "^1.0.2", "grunt-contrib-cssmin": "^1.0.2",
"grunt-contrib-jshint": "~0.10.0", "grunt-contrib-jshint": "~0.10.0",

1
vendor/assets/javascripts/app.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2308
vendor/assets/javascripts/transpiled.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long