From fcd0a8e624d9c0f5927530f92e3d8f350d19dbc1 Mon Sep 17 00:00:00 2001 From: korelstar Date: Tue, 16 Apr 2019 21:08:45 +0200 Subject: [PATCH] adjust design of checkboxes --- css/notes.scss | 6 +++--- js/public/app.min.js.map | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/css/notes.scss b/css/notes.scss index 4c2798dc..c4990619 100644 --- a/css/notes.scss +++ b/css/notes.scss @@ -294,19 +294,19 @@ form.category .icon-confirm { .CodeMirror .CodeMirror-code .cm-formatting-task { position: relative; display: inline-block; - width: 2em; + width: 1.8em; } .CodeMirror .CodeMirror-code .cm-formatting-task.cm-meta::before { content: "\2610"; - font-size: 1.7em; + font-size: 1.5em; background: white; position: absolute; } .CodeMirror .CodeMirror-code .cm-formatting-task.cm-property::before { content: "\2611"; - font-size: 1.7em; + font-size: 1.5em; background: white; position: absolute; } diff --git a/js/public/app.min.js.map b/js/public/app.min.js.map index 8f8f9a01..b4ed8847 100644 --- a/js/public/app.min.js.map +++ b/js/public/app.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["app.js","controllers/appcontroller.js","controllers/notecontroller.js","controllers/notescontroller.js","controllers/notessettingscontroller.js","app.min.js","directives/autofocus.js","directives/editor.js","directives/tooltip.js","filters/and.js","filters/categoryTitle.js","filters/groupNotes.js","filters/wordCount.js","services/debounce.js","services/is.js","services/notesmodel.js","services/savequeue.js","services/urlFinder.js"],"names":["angular","$","requestToken","SimpleMDE","undefined","app","module","config","$provide","$routeProvider","RestangularProvider","$httpProvider","$windowProvider","defaults","headers","common","requesttoken","value","saveInterval","when","templateUrl","controller","resolve","note","$route","$q","is","Restangular","deferred","defer","noteId","current","params","loading","one","get","then","reject","promise","otherwise","redirectTo","baseUrl","OC","generateUrl","setBaseUrl","run","$rootScope","$location","NotesModel","attr","filePath","$on","notes","getAll","length","sorted","sort","a","b","modified","path","id","$scope","init","lastViewedNote","errorMessage","defaultTitle","document","title","Notification","showTemporary","initSearch","search","OCA","Search","query","$apply","css","hasClass","click","$routeParams","SaveQueue","debounce","$document","$timeout","updateIfExists","isSaving","isManualSaving","updateTitle","content","replace","trim","split","t","toggleCheckbox","el","$el","cm","CodeMirror","doc","getDoc","index","parents","line","getLineHandle","newvalue","text","replaceRange","ch","indexOf","onEdit","unsaved","autoSave","add","manualSave","error","addManual","editCategory","showEditCategory","val","category","autocomplete","source","getCategories","minLength","position","my","at","of","open","width","innerWidth","addClass","insertAfter","focus","saveCategory","isCategorySaving","customPUT","updatedNote","unbind","bind","event","ctrlKey","metaKey","String","fromCharCode","which","toLowerCase","preventDefault","toggleDistractionFree","launchIntoFullscreen","element","requestFullscreen","mozRequestFullScreen","webkitRequestFullscreen","msRequestFullscreen","exitFullscreen","mozCancelFullScreen","webkitExitFullscreen","fullscreenElement","mozFullScreenElement","webkitFullscreenElement","getElementById","$watch","newValue","$window","route","notesLoaded","folderSelectorOpen","filterCategory","orderRecent","orderAlpha","filterOrder","notesResource","all","getList","addAll","create","post","remove","$emit","toggleFavorite","favorite","target","blur","categories","toggleFolderSelector","setFilter","animate","scrollTop","categoryFilter","startsWith","isCategory","item","onbeforeunload","i","extensions","settings","isObject","on","msg","show","put","window","location","reload","directive","restrict","link","scope","urlFinder","simplemde","spellChecker","autoDownloadFontAwesome","toolbar","status","forceSync","editorElement","codemirror","getWrapperElement","e","stopImmediatePropagation","removeClass","url","this","isDefined","tooltip","container","filter","$filter","items","searchString","searchValues","filtered","str","prevCat","splice","substring","wordCount","n","factory","func","delay","timeout","context","args","arguments","cancel","apply","notesIds","prototype","type","updated","push","nthIndexOf","pattern","maxLevel","details","cat","result","name","count","_queue","_flushLock","_manualSaveActive","_flush","keys","Object","self","requests","_noteUpdateRequest","_saveFailed","response","prev","next","jQuery","oc_requesttoken"],"mappings":"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","file":"app.min.js","sourcesContent":["/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n/* jshint unused: false */\nvar app = angular.module('Notes', ['restangular', 'ngRoute']).\nconfig(function($provide, $routeProvider, RestangularProvider, $httpProvider,\n $windowProvider) {\n 'use strict';\n\n // Always send the CSRF token by default\n $httpProvider.defaults.headers.common.requesttoken = requestToken;\n\n // you have to use $provide inside the config method to provide a globally\n // shared and injectable object\n $provide.value('Constants', {\n saveInterval: 5*1000 // miliseconds\n });\n\n // define your routes that that load templates into the ng-view\n $routeProvider.when('/notes/:noteId', {\n templateUrl: 'note.html',\n controller: 'NoteController',\n resolve: {\n // $routeParams does not work inside resolve so use $route\n // note is the name of the argument that will be injected into the\n // controller\n /* @ngInject */\n note: function ($route, $q, is, Restangular) {\n\n var deferred = $q.defer();\n var noteId = $route.current.params.noteId;\n is.loading = true;\n\n Restangular.one('notes', noteId).get().then(function (note) {\n is.loading = false;\n deferred.resolve(note);\n }, function () {\n is.loading = false;\n deferred.reject();\n });\n\n return deferred.promise;\n }\n }\n }).otherwise({\n redirectTo: '/'\n });\n\n var baseUrl = OC.generateUrl('/apps/notes');\n RestangularProvider.setBaseUrl(baseUrl);\n\n\n\n}).run(function ($rootScope, $location, NotesModel) {\n 'use strict';\n\n $('link[rel=\"shortcut icon\"]').attr(\n\t\t 'href',\n\t\t OC.filePath('notes', 'img', 'favicon.png')\n );\n\n // handle route errors\n $rootScope.$on('$routeChangeError', function () {\n var notes = NotesModel.getAll();\n\n // route change error should redirect to the latest note if possible\n if (notes.length > 0) {\n var sorted = notes.sort(function (a, b) {\n if(a.modified > b.modified) {\n return 1;\n } else if(a.modified < b.modified) {\n return -1;\n } else {\n return 0;\n }\n });\n\n var note = notes[sorted.length-1];\n $location.path('/notes/' + note.id);\n } else {\n $location.path('/');\n }\n });\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('AppController', function ($scope, $location, is) {\n 'use strict';\n\n $scope.is = is;\n\n $scope.init = function (lastViewedNote, errorMessage) {\n $scope.defaultTitle = document.title;\n\n if(lastViewedNote !== 0 && $location.path()==='') {\n $location.path('/notes/' + lastViewedNote);\n }\n if(errorMessage) {\n OC.Notification.showTemporary(errorMessage);\n }\n $scope.initSearch();\n };\n\n $scope.search = '';\n $scope.defaultTitle = null;\n\n $scope.initSearch = function() {\n new OCA.Search(\n function (query) {\n $scope.search = query;\n $scope.$apply();\n if($('#app-navigation-toggle').css('display')!=='none' &&\n !$('body').hasClass('snapjs-left')) {\n $('#app-navigation-toggle').click();\n }\n },\n function () {\n $scope.search = '';\n $scope.$apply();\n }\n );\n };\n\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('NoteController', function($routeParams, $scope, NotesModel,\n SaveQueue, note, debounce,\n $document, $timeout) {\n 'use strict';\n\n NotesModel.updateIfExists(note);\n\n $scope.note = NotesModel.get($routeParams.noteId);\n\n $scope.isSaving = function () {\n return SaveQueue.isSaving();\n };\n $scope.isManualSaving = function () {\n return SaveQueue.isManualSaving();\n };\n\n $scope.updateTitle = function () {\n var content = $scope.note.content;\n\n // prepare content: remove markdown characters and empty spaces\n content = content.replace(/^\\s*[*+-]\\s+/mg, ''); // list item\n content = content.replace(/^#+\\s+(.*?)\\s*#*$/mg, '$1'); // headline\n content = content.replace(/^(=+|-+)$/mg, ''); // separate headline\n content = content.replace(/(\\*+|_+)(.*?)\\1/mg, '$2'); // emphasis\n\n // prevent directory traversal, illegal characters\n content = content.replace(/[\\*\\|\\/\\\\\\:\\\"<>\\?]/g, '');\n // prevent unintended file names\n content = content.replace(/^[\\. ]+/mg, '');\n\n // generate title from the first line of the content\n $scope.note.title = content.trim().split(/\\r?\\n/, 2)[0] ||\n t('notes', 'New note');\n };\n\n $scope.toggleCheckbox = function (el) {\n var $el = $(el);\n var cm = $('.CodeMirror')[0].CodeMirror;\n var doc = cm.getDoc();\n var index = $el.parents('.CodeMirror-line').index();\n var line = doc.getLineHandle(index);\n\n var newvalue = ( $el.text() === '[x]' ) ? '[ ]' : '[x]';\n\n // + 1 for some reason... not sure why\n doc.replaceRange(newvalue,\n {line: index, ch: line.text.indexOf('[')},\n {line: index, ch: line.text.indexOf(']') + 1}\n );\n };\n\n $scope.onEdit = function() {\n var note = $scope.note;\n note.unsaved = true;\n $scope.autoSave(note);\n };\n\n $scope.autoSave = debounce(function(note) {\n SaveQueue.add(note);\n }, 1000);\n\n $scope.manualSave = function() {\n var note = $scope.note;\n note.error = false;\n SaveQueue.addManual(note);\n };\n\n $scope.editCategory = false;\n $scope.showEditCategory = function() {\n $('#category').val($scope.note.category);\n $scope.editCategory = true;\n $('#category').autocomplete({\n source: NotesModel.getCategories(NotesModel.getAll(), 0, false),\n minLength: 0,\n position: { my: 'left bottom', at: 'left top', of: '#category' },\n open: function() {\n $timeout(function() {\n var width = $('form.category').innerWidth() - 2;\n $('.ui-autocomplete.ui-menu').width(width);\n });\n },\n }).autocomplete('widget').addClass('category-autocomplete');\n // fix space between input and confirm-button\n $('form.category .icon-confirm').insertAfter('#category');\n\n $timeout(function() {\n $('#category').focus();\n $('#category').autocomplete('search', '');\n });\n };\n $scope.saveCategory = function () {\n var category = $('#category').val();\n if($scope.note.category === category) {\n $scope.editCategory = false;\n return;\n }\n $scope.isCategorySaving = true;\n $scope.note.customPUT({category: category}, 'category', {}, {})\n .then(\n function (updatedNote) {\n $scope.note.category = updatedNote.category;\n if(category !== updatedNote.category) {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.'+\n ' Is the target directory writable?')\n );\n }\n },\n function () {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.')\n );\n }\n )\n .finally(\n function () {\n $scope.isCategorySaving = false;\n $scope.editCategory = false;\n }\n );\n };\n\n\n $document.unbind('keypress.notes.save');\n $document.bind('keypress.notes.save', function(event) {\n if(event.ctrlKey || event.metaKey) {\n switch(String.fromCharCode(event.which).toLowerCase()) {\n case 's':\n event.preventDefault();\n $scope.manualSave();\n break;\n }\n }\n });\n\n $scope.toggleDistractionFree = function() {\n function launchIntoFullscreen(element) {\n if(element.requestFullscreen) {\n element.requestFullscreen();\n } else if(element.mozRequestFullScreen) {\n element.mozRequestFullScreen();\n } else if(element.webkitRequestFullscreen) {\n element.webkitRequestFullscreen();\n } else if(element.msRequestFullscreen) {\n element.msRequestFullscreen();\n }\n }\n\n function exitFullscreen() {\n if(document.exitFullscreen) {\n document.exitFullscreen();\n } else if(document.mozCancelFullScreen) {\n document.mozCancelFullScreen();\n } else if(document.webkitExitFullscreen) {\n document.webkitExitFullscreen();\n }\n }\n\n if(document.fullscreenElement ||\n document.mozFullScreenElement ||\n document.webkitFullscreenElement) {\n exitFullscreen();\n } else {\n launchIntoFullscreen(document.getElementById('app-content'));\n }\n };\n\n $scope.$watch(function() {\n return $scope.note.title;\n }, function(newValue) {\n if(newValue) {\n document.title = newValue + ' - ' + $scope.defaultTitle;\n } else {\n document.title = $scope.defaultTitle;\n }\n });\n\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// This is available by using ng-controller=\"NotesController\" in your HTML\napp.controller('NotesController', function($routeParams, $scope, $location,\n Restangular, NotesModel, $window) {\n 'use strict';\n\n $scope.route = $routeParams;\n $scope.notesLoaded = false;\n $scope.notes = NotesModel.getAll();\n\n $scope.folderSelectorOpen = false;\n $scope.filterCategory = null;\n\n $scope.orderRecent = ['-favorite','-modified'];\n $scope.orderAlpha = ['category','-favorite','title'];\n $scope.filterOrder = $scope.orderRecent;\n\n var notesResource = Restangular.all('notes');\n\n // initial request for getting all notes\n notesResource.getList().then(function (notes) {\n NotesModel.addAll(notes);\n $scope.notesLoaded = true;\n });\n\n $scope.create = function () {\n notesResource.post({category: $scope.filterCategory})\n .then(function (note) {\n NotesModel.add(note);\n $location.path('/notes/' + note.id);\n });\n };\n\n $scope.delete = function (noteId) {\n var note = NotesModel.get(noteId);\n note.remove().then(function () {\n NotesModel.remove(noteId);\n $scope.$emit('$routeChangeError');\n });\n };\n\n $scope.toggleFavorite = function (noteId, event) {\n var note = NotesModel.get(noteId);\n note.customPUT({favorite: !note.favorite},\n 'favorite', {}, {}).then(function (favorite) {\n note.favorite = favorite ? true : false;\n });\n event.target.blur();\n };\n\n $scope.categories = [];\n $scope.$watch('notes', function(notes) {\n $scope.categories = NotesModel.getCategories(notes, 1, true);\n }, true);\n\n $scope.toggleFolderSelector = function () {\n $scope.folderSelectorOpen = !$scope.folderSelectorOpen;\n };\n\n $scope.setFilter = function (category) {\n if(category===null) {\n $scope.filterOrder = $scope.orderRecent;\n } else {\n $scope.filterOrder = $scope.orderAlpha;\n }\n $scope.filterCategory = category;\n $scope.folderSelectorOpen = false;\n $('#app-navigation > ul').animate({scrollTop: 0}, 'fast');\n };\n\n $scope.categoryFilter = function (note) {\n if($scope.filterCategory!==null) {\n if(note.category===$scope.filterCategory) {\n return true;\n } else if(note.category!==null) {\n return note.category.startsWith($scope.filterCategory+'/');\n }\n }\n return true;\n };\n\n $scope.isCategory = function (item) {\n return typeof item === 'string';\n };\n\n $window.onbeforeunload = function() {\n var notes = NotesModel.getAll();\n for(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n/* jshint unused: false */\nvar app = angular.module('Notes', ['restangular', 'ngRoute']).\nconfig([\"$provide\", \"$routeProvider\", \"RestangularProvider\", \"$httpProvider\", \"$windowProvider\", function($provide, $routeProvider, RestangularProvider, $httpProvider,\n $windowProvider) {\n 'use strict';\n\n // Always send the CSRF token by default\n $httpProvider.defaults.headers.common.requesttoken = requestToken;\n\n // you have to use $provide inside the config method to provide a globally\n // shared and injectable object\n $provide.value('Constants', {\n saveInterval: 5*1000 // miliseconds\n });\n\n // define your routes that that load templates into the ng-view\n $routeProvider.when('/notes/:noteId', {\n templateUrl: 'note.html',\n controller: 'NoteController',\n resolve: {\n // $routeParams does not work inside resolve so use $route\n // note is the name of the argument that will be injected into the\n // controller\n /* @ngInject */\n note: [\"$route\", \"$q\", \"is\", \"Restangular\", function ($route, $q, is, Restangular) {\n\n var deferred = $q.defer();\n var noteId = $route.current.params.noteId;\n is.loading = true;\n\n Restangular.one('notes', noteId).get().then(function (note) {\n is.loading = false;\n deferred.resolve(note);\n }, function () {\n is.loading = false;\n deferred.reject();\n });\n\n return deferred.promise;\n }]\n }\n }).otherwise({\n redirectTo: '/'\n });\n\n var baseUrl = OC.generateUrl('/apps/notes');\n RestangularProvider.setBaseUrl(baseUrl);\n\n\n\n}]).run([\"$rootScope\", \"$location\", \"NotesModel\", function ($rootScope, $location, NotesModel) {\n 'use strict';\n\n $('link[rel=\"shortcut icon\"]').attr(\n\t\t 'href',\n\t\t OC.filePath('notes', 'img', 'favicon.png')\n );\n\n // handle route errors\n $rootScope.$on('$routeChangeError', function () {\n var notes = NotesModel.getAll();\n\n // route change error should redirect to the latest note if possible\n if (notes.length > 0) {\n var sorted = notes.sort(function (a, b) {\n if(a.modified > b.modified) {\n return 1;\n } else if(a.modified < b.modified) {\n return -1;\n } else {\n return 0;\n }\n });\n\n var note = notes[sorted.length-1];\n $location.path('/notes/' + note.id);\n } else {\n $location.path('/');\n }\n });\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('AppController', [\"$scope\", \"$location\", \"is\", function ($scope, $location, is) {\n 'use strict';\n\n $scope.is = is;\n\n $scope.init = function (lastViewedNote, errorMessage) {\n $scope.defaultTitle = document.title;\n\n if(lastViewedNote !== 0 && $location.path()==='') {\n $location.path('/notes/' + lastViewedNote);\n }\n if(errorMessage) {\n OC.Notification.showTemporary(errorMessage);\n }\n $scope.initSearch();\n };\n\n $scope.search = '';\n $scope.defaultTitle = null;\n\n $scope.initSearch = function() {\n new OCA.Search(\n function (query) {\n $scope.search = query;\n $scope.$apply();\n if($('#app-navigation-toggle').css('display')!=='none' &&\n !$('body').hasClass('snapjs-left')) {\n $('#app-navigation-toggle').click();\n }\n },\n function () {\n $scope.search = '';\n $scope.$apply();\n }\n );\n };\n\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('NoteController', [\"$routeParams\", \"$scope\", \"NotesModel\", \"SaveQueue\", \"note\", \"debounce\", \"$document\", \"$timeout\", function($routeParams, $scope, NotesModel,\n SaveQueue, note, debounce,\n $document, $timeout) {\n 'use strict';\n\n NotesModel.updateIfExists(note);\n\n $scope.note = NotesModel.get($routeParams.noteId);\n\n $scope.isSaving = function () {\n return SaveQueue.isSaving();\n };\n $scope.isManualSaving = function () {\n return SaveQueue.isManualSaving();\n };\n\n $scope.updateTitle = function () {\n var content = $scope.note.content;\n\n // prepare content: remove markdown characters and empty spaces\n content = content.replace(/^\\s*[*+-]\\s+/mg, ''); // list item\n content = content.replace(/^#+\\s+(.*?)\\s*#*$/mg, '$1'); // headline\n content = content.replace(/^(=+|-+)$/mg, ''); // separate headline\n content = content.replace(/(\\*+|_+)(.*?)\\1/mg, '$2'); // emphasis\n\n // prevent directory traversal, illegal characters\n content = content.replace(/[\\*\\|\\/\\\\\\:\\\"<>\\?]/g, '');\n // prevent unintended file names\n content = content.replace(/^[\\. ]+/mg, '');\n\n // generate title from the first line of the content\n $scope.note.title = content.trim().split(/\\r?\\n/, 2)[0] ||\n t('notes', 'New note');\n };\n\n $scope.toggleCheckbox = function (el) {\n var $el = $(el);\n var cm = $('.CodeMirror')[0].CodeMirror;\n var doc = cm.getDoc();\n var index = $el.parents('.CodeMirror-line').index();\n var line = doc.getLineHandle(index);\n\n var newvalue = ( $el.text() === '[x]' ) ? '[ ]' : '[x]';\n\n // + 1 for some reason... not sure why\n doc.replaceRange(newvalue,\n {line: index, ch: line.text.indexOf('[')},\n {line: index, ch: line.text.indexOf(']') + 1}\n );\n };\n\n $scope.onEdit = function() {\n var note = $scope.note;\n note.unsaved = true;\n $scope.autoSave(note);\n };\n\n $scope.autoSave = debounce(function(note) {\n SaveQueue.add(note);\n }, 1000);\n\n $scope.manualSave = function() {\n var note = $scope.note;\n note.error = false;\n SaveQueue.addManual(note);\n };\n\n $scope.editCategory = false;\n $scope.showEditCategory = function() {\n $('#category').val($scope.note.category);\n $scope.editCategory = true;\n $('#category').autocomplete({\n source: NotesModel.getCategories(NotesModel.getAll(), 0, false),\n minLength: 0,\n position: { my: 'left bottom', at: 'left top', of: '#category' },\n open: function() {\n $timeout(function() {\n var width = $('form.category').innerWidth() - 2;\n $('.ui-autocomplete.ui-menu').width(width);\n });\n },\n }).autocomplete('widget').addClass('category-autocomplete');\n // fix space between input and confirm-button\n $('form.category .icon-confirm').insertAfter('#category');\n\n $timeout(function() {\n $('#category').focus();\n $('#category').autocomplete('search', '');\n });\n };\n $scope.saveCategory = function () {\n var category = $('#category').val();\n if($scope.note.category === category) {\n $scope.editCategory = false;\n return;\n }\n $scope.isCategorySaving = true;\n $scope.note.customPUT({category: category}, 'category', {}, {})\n .then(\n function (updatedNote) {\n $scope.note.category = updatedNote.category;\n if(category !== updatedNote.category) {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.'+\n ' Is the target directory writable?')\n );\n }\n },\n function () {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.')\n );\n }\n )\n .finally(\n function () {\n $scope.isCategorySaving = false;\n $scope.editCategory = false;\n }\n );\n };\n\n\n $document.unbind('keypress.notes.save');\n $document.bind('keypress.notes.save', function(event) {\n if(event.ctrlKey || event.metaKey) {\n switch(String.fromCharCode(event.which).toLowerCase()) {\n case 's':\n event.preventDefault();\n $scope.manualSave();\n break;\n }\n }\n });\n\n $scope.toggleDistractionFree = function() {\n function launchIntoFullscreen(element) {\n if(element.requestFullscreen) {\n element.requestFullscreen();\n } else if(element.mozRequestFullScreen) {\n element.mozRequestFullScreen();\n } else if(element.webkitRequestFullscreen) {\n element.webkitRequestFullscreen();\n } else if(element.msRequestFullscreen) {\n element.msRequestFullscreen();\n }\n }\n\n function exitFullscreen() {\n if(document.exitFullscreen) {\n document.exitFullscreen();\n } else if(document.mozCancelFullScreen) {\n document.mozCancelFullScreen();\n } else if(document.webkitExitFullscreen) {\n document.webkitExitFullscreen();\n }\n }\n\n if(document.fullscreenElement ||\n document.mozFullScreenElement ||\n document.webkitFullscreenElement) {\n exitFullscreen();\n } else {\n launchIntoFullscreen(document.getElementById('app-content'));\n }\n };\n\n $scope.$watch(function() {\n return $scope.note.title;\n }, function(newValue) {\n if(newValue) {\n document.title = newValue + ' - ' + $scope.defaultTitle;\n } else {\n document.title = $scope.defaultTitle;\n }\n });\n\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// This is available by using ng-controller=\"NotesController\" in your HTML\napp.controller('NotesController', [\"$routeParams\", \"$scope\", \"$location\", \"Restangular\", \"NotesModel\", \"$window\", function($routeParams, $scope, $location,\n Restangular, NotesModel, $window) {\n 'use strict';\n\n $scope.route = $routeParams;\n $scope.notesLoaded = false;\n $scope.notes = NotesModel.getAll();\n\n $scope.folderSelectorOpen = false;\n $scope.filterCategory = null;\n\n $scope.orderRecent = ['-favorite','-modified'];\n $scope.orderAlpha = ['category','-favorite','title'];\n $scope.filterOrder = $scope.orderRecent;\n\n var notesResource = Restangular.all('notes');\n\n // initial request for getting all notes\n notesResource.getList().then(function (notes) {\n NotesModel.addAll(notes);\n $scope.notesLoaded = true;\n });\n\n $scope.create = function () {\n notesResource.post({category: $scope.filterCategory})\n .then(function (note) {\n NotesModel.add(note);\n $location.path('/notes/' + note.id);\n });\n };\n\n $scope.delete = function (noteId) {\n var note = NotesModel.get(noteId);\n note.remove().then(function () {\n NotesModel.remove(noteId);\n $scope.$emit('$routeChangeError');\n });\n };\n\n $scope.toggleFavorite = function (noteId, event) {\n var note = NotesModel.get(noteId);\n note.customPUT({favorite: !note.favorite},\n 'favorite', {}, {}).then(function (favorite) {\n note.favorite = favorite ? true : false;\n });\n event.target.blur();\n };\n\n $scope.categories = [];\n $scope.$watch('notes', function(notes) {\n $scope.categories = NotesModel.getCategories(notes, 1, true);\n }, true);\n\n $scope.toggleFolderSelector = function () {\n $scope.folderSelectorOpen = !$scope.folderSelectorOpen;\n };\n\n $scope.setFilter = function (category) {\n if(category===null) {\n $scope.filterOrder = $scope.orderRecent;\n } else {\n $scope.filterOrder = $scope.orderAlpha;\n }\n $scope.filterCategory = category;\n $scope.folderSelectorOpen = false;\n $('#app-navigation > ul').animate({scrollTop: 0}, 'fast');\n };\n\n $scope.categoryFilter = function (note) {\n if($scope.filterCategory!==null) {\n if(note.category===$scope.filterCategory) {\n return true;\n } else if(note.category!==null) {\n return note.category.startsWith($scope.filterCategory+'/');\n }\n }\n return true;\n };\n\n $scope.isCategory = function (item) {\n return typeof item === 'string';\n };\n\n $window.onbeforeunload = function() {\n var notes = NotesModel.getAll();\n for(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesAutofocus', function () {\n 'use strict';\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.focus();\n }\n };\n});\n\n/*global SimpleMDE*/\napp.directive('editor', ['$timeout',\n 'urlFinder',\n function ($timeout, urlFinder) {\n\t'use strict';\n\treturn {\n\t\trestrict: 'A',\n\t\tlink: function(scope, element) {\n\n\t\t\tvar simplemde = new SimpleMDE({\n\t\t\t\telement: element[0],\n\t\t\t\tspellChecker: false,\n\t\t\t\tautoDownloadFontAwesome: false,\n\t\t\t\ttoolbar: false,\n\t\t\t\tstatus: false,\n\t\t\t\tforceSync: true\n\t\t\t});\n\t\t\tvar editorElement = $(simplemde.codemirror.getWrapperElement());\n\n\t\t\tsimplemde.value(scope.note.content);\n\t\t\tsimplemde.codemirror.focus();\n\n\t\t\t/* Initialize Checkboxes */\n\t\t\t$('.CodeMirror-code').on('mousedown.checkbox touchstart.checkbox', '.cm-formatting-task', function (e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n scope.toggleCheckbox(e.target);\n\t\t\t});\n\n simplemde.codemirror.on('update', function () {\n $('.CodeMirror-line').removeClass('completed-task');\n $('.CodeMirror-line:contains(\"[x]\")').addClass('completed-task');\n });\n\n\t\t\tsimplemde.codemirror.on('change', function() {\n\t\t\t\t$timeout(function() {\n\t\t\t\t\tscope.$apply(function () {\n\t\t\t\t\t\tscope.note.content = simplemde.value();\n\t\t\t\t\t\tscope.onEdit();\n\t\t\t\t\t\tscope.updateTitle();\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t\teditorElement.on('click', '.cm-link, .cm-url', function(event) {\n\t\t\t\tif(event.ctrlKey) {\n\t\t\t\t\tvar url = urlFinder(this);\n\t\t\t\t\tif(angular.isDefined(url)) {\n\t\t\t\t\t\twindow.open(url, '_blank');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesTooltip', function () {\n 'use strict';\n\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.tooltip({'container': 'body'});\n\n element.on('$destroy', function() {\n element.tooltip('hide');\n });\n\n element.on('click', function() {\n element.tooltip('hide');\n });\n }\n };\n});\n\n/**\n * filter by multiple words (AND operation)\n */\napp.filter('and', ['$filter', function ($filter) {\n\t'use strict';\n\treturn function (items, searchString) {\n\t\tvar searchValues = searchString.split(' ');\n\t\tvar filtered = items;\n\t\tfor(var i in searchValues) {\n\t\t\tfiltered = $filter('filter')(filtered, searchValues[i]);\n\t\t}\n\t\treturn filtered;\n\t};\n}]);\n\napp.filter('categoryTitle', function () {\n\t'use strict';\n\treturn function (str) {\n\t\tif (str && (typeof str === 'string')) {\n\t\t\treturn str.replace(/\\//g, ' / ');\n\t\t} else {\n\t\t\treturn '';\n\t\t}\n\t};\n});\n\n/**\n * group notes by (sub) category\n */\napp.filter('groupNotes', function () {\n\t'use strict';\n\treturn function (items, category) {\n\t\tif(category) {\n\t\t\tvar prevCat = null;\n\t\t\tfor(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('is', function () {\n 'use strict';\n\n return {\n loading: false\n };\n});\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// take care of fileconflicts by appending a number\napp.factory('NotesModel', function () {\n 'use strict';\n\n var NotesModel = function () {\n this.notes = [];\n this.notesIds = {};\n };\n\n NotesModel.prototype = {\n addAll: function (notes) {\n for(var i=0; i0) {\n var index = this.nthIndexOf(cat, '/', maxLevel);\n if(index>0) {\n cat = cat.substring(0, index);\n }\n }\n if(categories[cat]===undefined) {\n categories[cat] = 1;\n } else {\n categories[cat] += 1;\n }\n }\n var result = [];\n for(var category in categories) {\n if(details) {\n result.push({\n name: category,\n count: categories[category],\n });\n } else if(category) {\n result.push(category);\n }\n }\n if(!details) {\n result.sort();\n }\n return result;\n },\n\n };\n\n return new NotesModel();\n});\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('SaveQueue', [\"$q\", function($q) {\n 'use strict';\n\n var SaveQueue = function () {\n this._queue = {};\n this._flushLock = false;\n this._manualSaveActive = false;\n };\n\n SaveQueue.prototype = {\n add: function (note) {\n this._queue[note.id] = note;\n this._flush();\n },\n addManual: function (note) {\n this._manualSaveActive = true;\n this.add(note);\n },\n _flush: function () {\n // if there are no changes dont execute the requests\n var keys = Object.keys(this._queue);\n if(keys.length === 0 || this._flushLock) {\n return;\n } else {\n this._flushLock = true;\n }\n\n var self = this;\n var requests = [];\n\n // iterate over updated objects and run an update request for\n // each one of them\n for(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesAutofocus', function () {\n 'use strict';\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.focus();\n }\n };\n});\n","/*global SimpleMDE*/\napp.directive('editor', ['$timeout',\n 'urlFinder',\n function ($timeout, urlFinder) {\n\t'use strict';\n\treturn {\n\t\trestrict: 'A',\n\t\tlink: function(scope, element) {\n\n\t\t\tvar simplemde = new SimpleMDE({\n\t\t\t\telement: element[0],\n\t\t\t\tspellChecker: false,\n\t\t\t\tautoDownloadFontAwesome: false,\n\t\t\t\ttoolbar: false,\n\t\t\t\tstatus: false,\n\t\t\t\tforceSync: true\n\t\t\t});\n\t\t\tvar editorElement = $(simplemde.codemirror.getWrapperElement());\n\n\t\t\tsimplemde.value(scope.note.content);\n\t\t\tsimplemde.codemirror.focus();\n\n\t\t\t/* Initialize Checkboxes */\n\t\t\t$('.CodeMirror-code').on('mousedown.checkbox touchstart.checkbox', '.cm-formatting-task', function (e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n scope.toggleCheckbox(e.target);\n\t\t\t});\n\n simplemde.codemirror.on('update', function () {\n $('.CodeMirror-line').removeClass('completed-task');\n $('.CodeMirror-line:contains(\"[x]\")').addClass('completed-task');\n });\n\n\t\t\tsimplemde.codemirror.on('change', function() {\n\t\t\t\t$timeout(function() {\n\t\t\t\t\tscope.$apply(function () {\n\t\t\t\t\t\tscope.note.content = simplemde.value();\n\t\t\t\t\t\tscope.onEdit();\n\t\t\t\t\t\tscope.updateTitle();\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t\teditorElement.on('click', '.cm-link, .cm-url', function(event) {\n\t\t\t\tif(event.ctrlKey) {\n\t\t\t\t\tvar url = urlFinder(this);\n\t\t\t\t\tif(angular.isDefined(url)) {\n\t\t\t\t\t\twindow.open(url, '_blank');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}]);\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesTooltip', function () {\n 'use strict';\n\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.tooltip({'container': 'body'});\n\n element.on('$destroy', function() {\n element.tooltip('hide');\n });\n\n element.on('click', function() {\n element.tooltip('hide');\n });\n }\n };\n});\n","/**\n * filter by multiple words (AND operation)\n */\napp.filter('and', ['$filter', function ($filter) {\n\t'use strict';\n\treturn function (items, searchString) {\n\t\tvar searchValues = searchString.split(' ');\n\t\tvar filtered = items;\n\t\tfor(var i in searchValues) {\n\t\t\tfiltered = $filter('filter')(filtered, searchValues[i]);\n\t\t}\n\t\treturn filtered;\n\t};\n}]);\n","app.filter('categoryTitle', function () {\n\t'use strict';\n\treturn function (str) {\n\t\tif (str && (typeof str === 'string')) {\n\t\t\treturn str.replace(/\\//g, ' / ');\n\t\t} else {\n\t\t\treturn '';\n\t\t}\n\t};\n});\n","/**\n * group notes by (sub) category\n */\napp.filter('groupNotes', function () {\n\t'use strict';\n\treturn function (items, category) {\n\t\tif(category) {\n\t\t\tvar prevCat = null;\n\t\t\tfor(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('is', function () {\n 'use strict';\n\n return {\n loading: false\n };\n});","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// take care of fileconflicts by appending a number\napp.factory('NotesModel', function () {\n 'use strict';\n\n var NotesModel = function () {\n this.notes = [];\n this.notesIds = {};\n };\n\n NotesModel.prototype = {\n addAll: function (notes) {\n for(var i=0; i0) {\n var index = this.nthIndexOf(cat, '/', maxLevel);\n if(index>0) {\n cat = cat.substring(0, index);\n }\n }\n if(categories[cat]===undefined) {\n categories[cat] = 1;\n } else {\n categories[cat] += 1;\n }\n }\n var result = [];\n for(var category in categories) {\n if(details) {\n result.push({\n name: category,\n count: categories[category],\n });\n } else if(category) {\n result.push(category);\n }\n }\n if(!details) {\n result.sort();\n }\n return result;\n },\n\n };\n\n return new NotesModel();\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('SaveQueue', function($q) {\n 'use strict';\n\n var SaveQueue = function () {\n this._queue = {};\n this._flushLock = false;\n this._manualSaveActive = false;\n };\n\n SaveQueue.prototype = {\n add: function (note) {\n this._queue[note.id] = note;\n this._flush();\n },\n addManual: function (note) {\n this._manualSaveActive = true;\n this.add(note);\n },\n _flush: function () {\n // if there are no changes dont execute the requests\n var keys = Object.keys(this._queue);\n if(keys.length === 0 || this._flushLock) {\n return;\n } else {\n this._flushLock = true;\n }\n\n var self = this;\n var requests = [];\n\n // iterate over updated objects and run an update request for\n // each one of them\n for(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n/* jshint unused: false */\nvar app = angular.module('Notes', ['restangular', 'ngRoute']).\nconfig(function($provide, $routeProvider, RestangularProvider, $httpProvider,\n $windowProvider) {\n 'use strict';\n\n // Always send the CSRF token by default\n $httpProvider.defaults.headers.common.requesttoken = requestToken;\n\n // you have to use $provide inside the config method to provide a globally\n // shared and injectable object\n $provide.value('Constants', {\n saveInterval: 5*1000 // miliseconds\n });\n\n // define your routes that that load templates into the ng-view\n $routeProvider.when('/notes/:noteId', {\n templateUrl: 'note.html',\n controller: 'NoteController',\n resolve: {\n // $routeParams does not work inside resolve so use $route\n // note is the name of the argument that will be injected into the\n // controller\n /* @ngInject */\n note: function ($route, $q, is, Restangular) {\n\n var deferred = $q.defer();\n var noteId = $route.current.params.noteId;\n is.loading = true;\n\n Restangular.one('notes', noteId).get().then(function (note) {\n is.loading = false;\n deferred.resolve(note);\n }, function () {\n is.loading = false;\n deferred.reject();\n });\n\n return deferred.promise;\n }\n }\n }).otherwise({\n redirectTo: '/'\n });\n\n var baseUrl = OC.generateUrl('/apps/notes');\n RestangularProvider.setBaseUrl(baseUrl);\n\n\n\n}).run(function ($rootScope, $location, NotesModel) {\n 'use strict';\n\n $('link[rel=\"shortcut icon\"]').attr(\n\t\t 'href',\n\t\t OC.filePath('notes', 'img', 'favicon.png')\n );\n\n // handle route errors\n $rootScope.$on('$routeChangeError', function () {\n var notes = NotesModel.getAll();\n\n // route change error should redirect to the latest note if possible\n if (notes.length > 0) {\n var sorted = notes.sort(function (a, b) {\n if(a.modified > b.modified) {\n return 1;\n } else if(a.modified < b.modified) {\n return -1;\n } else {\n return 0;\n }\n });\n\n var note = notes[sorted.length-1];\n $location.path('/notes/' + note.id);\n } else {\n $location.path('/');\n }\n });\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('AppController', function ($scope, $location, is) {\n 'use strict';\n\n $scope.is = is;\n\n $scope.init = function (lastViewedNote, errorMessage) {\n $scope.defaultTitle = document.title;\n\n if(lastViewedNote !== 0 && $location.path()==='') {\n $location.path('/notes/' + lastViewedNote);\n }\n if(errorMessage) {\n OC.Notification.showTemporary(errorMessage);\n }\n $scope.initSearch();\n };\n\n $scope.search = '';\n $scope.defaultTitle = null;\n\n $scope.initSearch = function() {\n new OCA.Search(\n function (query) {\n $scope.search = query;\n $scope.$apply();\n if($('#app-navigation-toggle').css('display')!=='none' &&\n !$('body').hasClass('snapjs-left')) {\n $('#app-navigation-toggle').click();\n }\n },\n function () {\n $scope.search = '';\n $scope.$apply();\n }\n );\n };\n\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('NoteController', function($routeParams, $scope, NotesModel,\n SaveQueue, note, debounce,\n $document, $timeout) {\n 'use strict';\n\n NotesModel.updateIfExists(note);\n\n $scope.note = NotesModel.get($routeParams.noteId);\n\n $scope.isSaving = function () {\n return SaveQueue.isSaving();\n };\n $scope.isManualSaving = function () {\n return SaveQueue.isManualSaving();\n };\n\n $scope.updateTitle = function () {\n var content = $scope.note.content;\n\n // prepare content: remove markdown characters and empty spaces\n content = content.replace(/^\\s*[*+-]\\s+/mg, ''); // list item\n content = content.replace(/^#+\\s+(.*?)\\s*#*$/mg, '$1'); // headline\n content = content.replace(/^(=+|-+)$/mg, ''); // separate headline\n content = content.replace(/(\\*+|_+)(.*?)\\1/mg, '$2'); // emphasis\n\n // prevent directory traversal, illegal characters\n content = content.replace(/[\\*\\|\\/\\\\\\:\\\"<>\\?]/g, '');\n // prevent unintended file names\n content = content.replace(/^[\\. ]+/mg, '');\n\n // generate title from the first line of the content\n $scope.note.title = content.trim().split(/\\r?\\n/, 2)[0] ||\n t('notes', 'New note');\n };\n\n $scope.toggleCheckbox = function (el) {\n var $el = $(el);\n var cm = $('.CodeMirror')[0].CodeMirror;\n var doc = cm.getDoc();\n var index = $el.parents('.CodeMirror-line').index();\n var line = doc.getLineHandle(index);\n\n var newvalue = ( $el.text() === '[x]' ) ? '[ ]' : '[x]';\n\n // + 1 for some reason... not sure why\n doc.replaceRange(newvalue,\n {line: index, ch: line.text.indexOf('[')},\n {line: index, ch: line.text.indexOf(']') + 1}\n );\n };\n\n $scope.onEdit = function() {\n var note = $scope.note;\n note.unsaved = true;\n $scope.autoSave(note);\n };\n\n $scope.autoSave = debounce(function(note) {\n SaveQueue.add(note);\n }, 1000);\n\n $scope.manualSave = function() {\n var note = $scope.note;\n note.error = false;\n SaveQueue.addManual(note);\n };\n\n $scope.editCategory = false;\n $scope.showEditCategory = function() {\n $('#category').val($scope.note.category);\n $scope.editCategory = true;\n $('#category').autocomplete({\n source: NotesModel.getCategories(NotesModel.getAll(), 0, false),\n minLength: 0,\n position: { my: 'left bottom', at: 'left top', of: '#category' },\n open: function() {\n $timeout(function() {\n var width = $('form.category').innerWidth() - 2;\n $('.ui-autocomplete.ui-menu').width(width);\n });\n },\n }).autocomplete('widget').addClass('category-autocomplete');\n // fix space between input and confirm-button\n $('form.category .icon-confirm').insertAfter('#category');\n\n $timeout(function() {\n $('#category').focus();\n $('#category').autocomplete('search', '');\n });\n };\n $scope.saveCategory = function () {\n var category = $('#category').val();\n if($scope.note.category === category) {\n $scope.editCategory = false;\n return;\n }\n $scope.isCategorySaving = true;\n $scope.note.customPUT({category: category}, 'category', {}, {})\n .then(\n function (updatedNote) {\n $scope.note.category = updatedNote.category;\n if(category !== updatedNote.category) {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.'+\n ' Is the target directory writable?')\n );\n }\n },\n function () {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.')\n );\n }\n )\n .finally(\n function () {\n $scope.isCategorySaving = false;\n $scope.editCategory = false;\n }\n );\n };\n\n\n $document.unbind('keypress.notes.save');\n $document.bind('keypress.notes.save', function(event) {\n if(event.ctrlKey || event.metaKey) {\n switch(String.fromCharCode(event.which).toLowerCase()) {\n case 's':\n event.preventDefault();\n $scope.manualSave();\n break;\n }\n }\n });\n\n $scope.toggleDistractionFree = function() {\n function launchIntoFullscreen(element) {\n if(element.requestFullscreen) {\n element.requestFullscreen();\n } else if(element.mozRequestFullScreen) {\n element.mozRequestFullScreen();\n } else if(element.webkitRequestFullscreen) {\n element.webkitRequestFullscreen();\n } else if(element.msRequestFullscreen) {\n element.msRequestFullscreen();\n }\n }\n\n function exitFullscreen() {\n if(document.exitFullscreen) {\n document.exitFullscreen();\n } else if(document.mozCancelFullScreen) {\n document.mozCancelFullScreen();\n } else if(document.webkitExitFullscreen) {\n document.webkitExitFullscreen();\n }\n }\n\n if(document.fullscreenElement ||\n document.mozFullScreenElement ||\n document.webkitFullscreenElement) {\n exitFullscreen();\n } else {\n launchIntoFullscreen(document.getElementById('app-content'));\n }\n };\n\n $scope.$watch(function() {\n return $scope.note.title;\n }, function(newValue) {\n if(newValue) {\n document.title = newValue + ' - ' + $scope.defaultTitle;\n } else {\n document.title = $scope.defaultTitle;\n }\n });\n\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// This is available by using ng-controller=\"NotesController\" in your HTML\napp.controller('NotesController', function($routeParams, $scope, $location,\n Restangular, NotesModel, $window) {\n 'use strict';\n\n $scope.route = $routeParams;\n $scope.notesLoaded = false;\n $scope.notes = NotesModel.getAll();\n\n $scope.folderSelectorOpen = false;\n $scope.filterCategory = null;\n\n $scope.orderRecent = ['-favorite','-modified'];\n $scope.orderAlpha = ['category','-favorite','title'];\n $scope.filterOrder = $scope.orderRecent;\n\n var notesResource = Restangular.all('notes');\n\n // initial request for getting all notes\n notesResource.getList().then(function (notes) {\n NotesModel.addAll(notes);\n $scope.notesLoaded = true;\n });\n\n $scope.create = function () {\n notesResource.post({category: $scope.filterCategory})\n .then(function (note) {\n NotesModel.add(note);\n $location.path('/notes/' + note.id);\n });\n };\n\n $scope.delete = function (noteId) {\n var note = NotesModel.get(noteId);\n note.remove().then(function () {\n NotesModel.remove(noteId);\n $scope.$emit('$routeChangeError');\n });\n };\n\n $scope.toggleFavorite = function (noteId, event) {\n var note = NotesModel.get(noteId);\n note.customPUT({favorite: !note.favorite},\n 'favorite', {}, {}).then(function (favorite) {\n note.favorite = favorite ? true : false;\n });\n event.target.blur();\n };\n\n $scope.categories = [];\n $scope.$watch('notes', function(notes) {\n $scope.categories = NotesModel.getCategories(notes, 1, true);\n }, true);\n\n $scope.toggleFolderSelector = function () {\n $scope.folderSelectorOpen = !$scope.folderSelectorOpen;\n };\n\n $scope.setFilter = function (category) {\n if(category===null) {\n $scope.filterOrder = $scope.orderRecent;\n } else {\n $scope.filterOrder = $scope.orderAlpha;\n }\n $scope.filterCategory = category;\n $scope.folderSelectorOpen = false;\n $('#app-navigation > ul').animate({scrollTop: 0}, 'fast');\n };\n\n $scope.categoryFilter = function (note) {\n if($scope.filterCategory!==null) {\n if(note.category===$scope.filterCategory) {\n return true;\n } else if(note.category!==null) {\n return note.category.startsWith($scope.filterCategory+'/');\n }\n }\n return true;\n };\n\n $scope.isCategory = function (item) {\n return typeof item === 'string';\n };\n\n $window.onbeforeunload = function() {\n var notes = NotesModel.getAll();\n for(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n/* jshint unused: false */\nvar app = angular.module('Notes', ['restangular', 'ngRoute']).\nconfig([\"$provide\", \"$routeProvider\", \"RestangularProvider\", \"$httpProvider\", \"$windowProvider\", function($provide, $routeProvider, RestangularProvider, $httpProvider,\n $windowProvider) {\n 'use strict';\n\n // Always send the CSRF token by default\n $httpProvider.defaults.headers.common.requesttoken = requestToken;\n\n // you have to use $provide inside the config method to provide a globally\n // shared and injectable object\n $provide.value('Constants', {\n saveInterval: 5*1000 // miliseconds\n });\n\n // define your routes that that load templates into the ng-view\n $routeProvider.when('/notes/:noteId', {\n templateUrl: 'note.html',\n controller: 'NoteController',\n resolve: {\n // $routeParams does not work inside resolve so use $route\n // note is the name of the argument that will be injected into the\n // controller\n /* @ngInject */\n note: [\"$route\", \"$q\", \"is\", \"Restangular\", function ($route, $q, is, Restangular) {\n\n var deferred = $q.defer();\n var noteId = $route.current.params.noteId;\n is.loading = true;\n\n Restangular.one('notes', noteId).get().then(function (note) {\n is.loading = false;\n deferred.resolve(note);\n }, function () {\n is.loading = false;\n deferred.reject();\n });\n\n return deferred.promise;\n }]\n }\n }).otherwise({\n redirectTo: '/'\n });\n\n var baseUrl = OC.generateUrl('/apps/notes');\n RestangularProvider.setBaseUrl(baseUrl);\n\n\n\n}]).run([\"$rootScope\", \"$location\", \"NotesModel\", function ($rootScope, $location, NotesModel) {\n 'use strict';\n\n $('link[rel=\"shortcut icon\"]').attr(\n\t\t 'href',\n\t\t OC.filePath('notes', 'img', 'favicon.png')\n );\n\n // handle route errors\n $rootScope.$on('$routeChangeError', function () {\n var notes = NotesModel.getAll();\n\n // route change error should redirect to the latest note if possible\n if (notes.length > 0) {\n var sorted = notes.sort(function (a, b) {\n if(a.modified > b.modified) {\n return 1;\n } else if(a.modified < b.modified) {\n return -1;\n } else {\n return 0;\n }\n });\n\n var note = notes[sorted.length-1];\n $location.path('/notes/' + note.id);\n } else {\n $location.path('/');\n }\n });\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('AppController', [\"$scope\", \"$location\", \"is\", function ($scope, $location, is) {\n 'use strict';\n\n $scope.is = is;\n\n $scope.init = function (lastViewedNote, errorMessage) {\n $scope.defaultTitle = document.title;\n\n if(lastViewedNote !== 0 && $location.path()==='') {\n $location.path('/notes/' + lastViewedNote);\n }\n if(errorMessage) {\n OC.Notification.showTemporary(errorMessage);\n }\n $scope.initSearch();\n };\n\n $scope.search = '';\n $scope.defaultTitle = null;\n\n $scope.initSearch = function() {\n new OCA.Search(\n function (query) {\n $scope.search = query;\n $scope.$apply();\n if($('#app-navigation-toggle').css('display')!=='none' &&\n !$('body').hasClass('snapjs-left')) {\n $('#app-navigation-toggle').click();\n }\n },\n function () {\n $scope.search = '';\n $scope.$apply();\n }\n );\n };\n\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.controller('NoteController', [\"$routeParams\", \"$scope\", \"NotesModel\", \"SaveQueue\", \"note\", \"debounce\", \"$document\", \"$timeout\", function($routeParams, $scope, NotesModel,\n SaveQueue, note, debounce,\n $document, $timeout) {\n 'use strict';\n\n NotesModel.updateIfExists(note);\n\n $scope.note = NotesModel.get($routeParams.noteId);\n\n $scope.isSaving = function () {\n return SaveQueue.isSaving();\n };\n $scope.isManualSaving = function () {\n return SaveQueue.isManualSaving();\n };\n\n $scope.updateTitle = function () {\n var content = $scope.note.content;\n\n // prepare content: remove markdown characters and empty spaces\n content = content.replace(/^\\s*[*+-]\\s+/mg, ''); // list item\n content = content.replace(/^#+\\s+(.*?)\\s*#*$/mg, '$1'); // headline\n content = content.replace(/^(=+|-+)$/mg, ''); // separate headline\n content = content.replace(/(\\*+|_+)(.*?)\\1/mg, '$2'); // emphasis\n\n // prevent directory traversal, illegal characters\n content = content.replace(/[\\*\\|\\/\\\\\\:\\\"<>\\?]/g, '');\n // prevent unintended file names\n content = content.replace(/^[\\. ]+/mg, '');\n\n // generate title from the first line of the content\n $scope.note.title = content.trim().split(/\\r?\\n/, 2)[0] ||\n t('notes', 'New note');\n };\n\n $scope.toggleCheckbox = function (el) {\n var $el = $(el);\n var cm = $('.CodeMirror')[0].CodeMirror;\n var doc = cm.getDoc();\n var index = $el.parents('.CodeMirror-line').index();\n var line = doc.getLineHandle(index);\n\n var newvalue = ( $el.text() === '[x]' ) ? '[ ]' : '[x]';\n\n // + 1 for some reason... not sure why\n doc.replaceRange(newvalue,\n {line: index, ch: line.text.indexOf('[')},\n {line: index, ch: line.text.indexOf(']') + 1}\n );\n };\n\n $scope.onEdit = function() {\n var note = $scope.note;\n note.unsaved = true;\n $scope.autoSave(note);\n };\n\n $scope.autoSave = debounce(function(note) {\n SaveQueue.add(note);\n }, 1000);\n\n $scope.manualSave = function() {\n var note = $scope.note;\n note.error = false;\n SaveQueue.addManual(note);\n };\n\n $scope.editCategory = false;\n $scope.showEditCategory = function() {\n $('#category').val($scope.note.category);\n $scope.editCategory = true;\n $('#category').autocomplete({\n source: NotesModel.getCategories(NotesModel.getAll(), 0, false),\n minLength: 0,\n position: { my: 'left bottom', at: 'left top', of: '#category' },\n open: function() {\n $timeout(function() {\n var width = $('form.category').innerWidth() - 2;\n $('.ui-autocomplete.ui-menu').width(width);\n });\n },\n }).autocomplete('widget').addClass('category-autocomplete');\n // fix space between input and confirm-button\n $('form.category .icon-confirm').insertAfter('#category');\n\n $timeout(function() {\n $('#category').focus();\n $('#category').autocomplete('search', '');\n });\n };\n $scope.saveCategory = function () {\n var category = $('#category').val();\n if($scope.note.category === category) {\n $scope.editCategory = false;\n return;\n }\n $scope.isCategorySaving = true;\n $scope.note.customPUT({category: category}, 'category', {}, {})\n .then(\n function (updatedNote) {\n $scope.note.category = updatedNote.category;\n if(category !== updatedNote.category) {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.'+\n ' Is the target directory writable?')\n );\n }\n },\n function () {\n OC.Notification.showTemporary(\n t('notes', 'Updating the note\\'s category has failed.')\n );\n }\n )\n .finally(\n function () {\n $scope.isCategorySaving = false;\n $scope.editCategory = false;\n }\n );\n };\n\n\n $document.unbind('keypress.notes.save');\n $document.bind('keypress.notes.save', function(event) {\n if(event.ctrlKey || event.metaKey) {\n switch(String.fromCharCode(event.which).toLowerCase()) {\n case 's':\n event.preventDefault();\n $scope.manualSave();\n break;\n }\n }\n });\n\n $scope.toggleDistractionFree = function() {\n function launchIntoFullscreen(element) {\n if(element.requestFullscreen) {\n element.requestFullscreen();\n } else if(element.mozRequestFullScreen) {\n element.mozRequestFullScreen();\n } else if(element.webkitRequestFullscreen) {\n element.webkitRequestFullscreen();\n } else if(element.msRequestFullscreen) {\n element.msRequestFullscreen();\n }\n }\n\n function exitFullscreen() {\n if(document.exitFullscreen) {\n document.exitFullscreen();\n } else if(document.mozCancelFullScreen) {\n document.mozCancelFullScreen();\n } else if(document.webkitExitFullscreen) {\n document.webkitExitFullscreen();\n }\n }\n\n if(document.fullscreenElement ||\n document.mozFullScreenElement ||\n document.webkitFullscreenElement) {\n exitFullscreen();\n } else {\n launchIntoFullscreen(document.getElementById('app-content'));\n }\n };\n\n $scope.$watch(function() {\n return $scope.note.title;\n }, function(newValue) {\n if(newValue) {\n document.title = newValue + ' - ' + $scope.defaultTitle;\n } else {\n document.title = $scope.defaultTitle;\n }\n });\n\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// This is available by using ng-controller=\"NotesController\" in your HTML\napp.controller('NotesController', [\"$routeParams\", \"$scope\", \"$location\", \"Restangular\", \"NotesModel\", \"$window\", function($routeParams, $scope, $location,\n Restangular, NotesModel, $window) {\n 'use strict';\n\n $scope.route = $routeParams;\n $scope.notesLoaded = false;\n $scope.notes = NotesModel.getAll();\n\n $scope.folderSelectorOpen = false;\n $scope.filterCategory = null;\n\n $scope.orderRecent = ['-favorite','-modified'];\n $scope.orderAlpha = ['category','-favorite','title'];\n $scope.filterOrder = $scope.orderRecent;\n\n var notesResource = Restangular.all('notes');\n\n // initial request for getting all notes\n notesResource.getList().then(function (notes) {\n NotesModel.addAll(notes);\n $scope.notesLoaded = true;\n });\n\n $scope.create = function () {\n notesResource.post({category: $scope.filterCategory})\n .then(function (note) {\n NotesModel.add(note);\n $location.path('/notes/' + note.id);\n });\n };\n\n $scope.delete = function (noteId) {\n var note = NotesModel.get(noteId);\n note.remove().then(function () {\n NotesModel.remove(noteId);\n $scope.$emit('$routeChangeError');\n });\n };\n\n $scope.toggleFavorite = function (noteId, event) {\n var note = NotesModel.get(noteId);\n note.customPUT({favorite: !note.favorite},\n 'favorite', {}, {}).then(function (favorite) {\n note.favorite = favorite ? true : false;\n });\n event.target.blur();\n };\n\n $scope.categories = [];\n $scope.$watch('notes', function(notes) {\n $scope.categories = NotesModel.getCategories(notes, 1, true);\n }, true);\n\n $scope.toggleFolderSelector = function () {\n $scope.folderSelectorOpen = !$scope.folderSelectorOpen;\n };\n\n $scope.setFilter = function (category) {\n if(category===null) {\n $scope.filterOrder = $scope.orderRecent;\n } else {\n $scope.filterOrder = $scope.orderAlpha;\n }\n $scope.filterCategory = category;\n $scope.folderSelectorOpen = false;\n $('#app-navigation > ul').animate({scrollTop: 0}, 'fast');\n };\n\n $scope.categoryFilter = function (note) {\n if($scope.filterCategory!==null) {\n if(note.category===$scope.filterCategory) {\n return true;\n } else if(note.category!==null) {\n return note.category.startsWith($scope.filterCategory+'/');\n }\n }\n return true;\n };\n\n $scope.isCategory = function (item) {\n return typeof item === 'string';\n };\n\n $window.onbeforeunload = function() {\n var notes = NotesModel.getAll();\n for(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesAutofocus', function () {\n 'use strict';\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.focus();\n }\n };\n});\n\n/*global SimpleMDE*/\napp.directive('editor', ['$timeout',\n 'urlFinder',\n function ($timeout, urlFinder) {\n\t'use strict';\n\treturn {\n\t\trestrict: 'A',\n\t\tlink: function(scope, element) {\n\n\t\t\tvar simplemde = new SimpleMDE({\n\t\t\t\telement: element[0],\n\t\t\t\tspellChecker: false,\n\t\t\t\tautoDownloadFontAwesome: false,\n\t\t\t\ttoolbar: false,\n\t\t\t\tstatus: false,\n\t\t\t\tforceSync: true\n\t\t\t});\n\t\t\tvar editorElement = $(simplemde.codemirror.getWrapperElement());\n\n\t\t\tsimplemde.value(scope.note.content);\n\t\t\tsimplemde.codemirror.focus();\n\n\t\t\t/* Initialize Checkboxes */\n\t\t\t$('.CodeMirror-code').on('mousedown.checkbox touchstart.checkbox', '.cm-formatting-task', function (e) {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopImmediatePropagation();\n\t\t\t\tscope.toggleCheckbox(e.target);\n\t\t\t});\n\n\t\t\tsimplemde.codemirror.on('update', function () {\n\t\t\t\t// For strikethrough styling of completed tasks\n\t\t\t\t$('.CodeMirror-line').removeClass('completed-task');\n\t\t\t\t$('.CodeMirror-line:contains(\"[x]\")').addClass('completed-task');\n\t\t\t});\n\n\t\t\tsimplemde.codemirror.on('change', function() {\n\t\t\t\t$timeout(function() {\n\t\t\t\t\tscope.$apply(function () {\n\t\t\t\t\t\tscope.note.content = simplemde.value();\n\t\t\t\t\t\tscope.onEdit();\n\t\t\t\t\t\tscope.updateTitle();\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t\teditorElement.on('click', '.cm-link, .cm-url', function(event) {\n\t\t\t\tif(event.ctrlKey) {\n\t\t\t\t\tvar url = urlFinder(this);\n\t\t\t\t\tif(angular.isDefined(url)) {\n\t\t\t\t\t\twindow.open(url, '_blank');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}]);\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesTooltip', function () {\n 'use strict';\n\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.tooltip({'container': 'body'});\n\n element.on('$destroy', function() {\n element.tooltip('hide');\n });\n\n element.on('click', function() {\n element.tooltip('hide');\n });\n }\n };\n});\n\n/**\n * filter by multiple words (AND operation)\n */\napp.filter('and', ['$filter', function ($filter) {\n\t'use strict';\n\treturn function (items, searchString) {\n\t\tvar searchValues = searchString.split(' ');\n\t\tvar filtered = items;\n\t\tfor(var i in searchValues) {\n\t\t\tfiltered = $filter('filter')(filtered, searchValues[i]);\n\t\t}\n\t\treturn filtered;\n\t};\n}]);\n\napp.filter('categoryTitle', function () {\n\t'use strict';\n\treturn function (str) {\n\t\tif (str && (typeof str === 'string')) {\n\t\t\treturn str.replace(/\\//g, ' / ');\n\t\t} else {\n\t\t\treturn '';\n\t\t}\n\t};\n});\n\n/**\n * group notes by (sub) category\n */\napp.filter('groupNotes', function () {\n\t'use strict';\n\treturn function (items, category) {\n\t\tif(category) {\n\t\t\tvar prevCat = null;\n\t\t\tfor(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('is', function () {\n 'use strict';\n\n return {\n loading: false\n };\n});\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// take care of fileconflicts by appending a number\napp.factory('NotesModel', function () {\n 'use strict';\n\n var NotesModel = function () {\n this.notes = [];\n this.notesIds = {};\n };\n\n NotesModel.prototype = {\n addAll: function (notes) {\n for(var i=0; i0) {\n var index = this.nthIndexOf(cat, '/', maxLevel);\n if(index>0) {\n cat = cat.substring(0, index);\n }\n }\n if(categories[cat]===undefined) {\n categories[cat] = 1;\n } else {\n categories[cat] += 1;\n }\n }\n var result = [];\n for(var category in categories) {\n if(details) {\n result.push({\n name: category,\n count: categories[category],\n });\n } else if(category) {\n result.push(category);\n }\n }\n if(!details) {\n result.sort();\n }\n return result;\n },\n\n };\n\n return new NotesModel();\n});\n\n/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('SaveQueue', [\"$q\", function($q) {\n 'use strict';\n\n var SaveQueue = function () {\n this._queue = {};\n this._flushLock = false;\n this._manualSaveActive = false;\n };\n\n SaveQueue.prototype = {\n add: function (note) {\n this._queue[note.id] = note;\n this._flush();\n },\n addManual: function (note) {\n this._manualSaveActive = true;\n this.add(note);\n },\n _flush: function () {\n // if there are no changes dont execute the requests\n var keys = Object.keys(this._queue);\n if(keys.length === 0 || this._flushLock) {\n return;\n } else {\n this._flushLock = true;\n }\n\n var self = this;\n var requests = [];\n\n // iterate over updated objects and run an update request for\n // each one of them\n for(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesAutofocus', function () {\n 'use strict';\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.focus();\n }\n };\n});\n","/*global SimpleMDE*/\napp.directive('editor', ['$timeout',\n 'urlFinder',\n function ($timeout, urlFinder) {\n\t'use strict';\n\treturn {\n\t\trestrict: 'A',\n\t\tlink: function(scope, element) {\n\n\t\t\tvar simplemde = new SimpleMDE({\n\t\t\t\telement: element[0],\n\t\t\t\tspellChecker: false,\n\t\t\t\tautoDownloadFontAwesome: false,\n\t\t\t\ttoolbar: false,\n\t\t\t\tstatus: false,\n\t\t\t\tforceSync: true\n\t\t\t});\n\t\t\tvar editorElement = $(simplemde.codemirror.getWrapperElement());\n\n\t\t\tsimplemde.value(scope.note.content);\n\t\t\tsimplemde.codemirror.focus();\n\n\t\t\t/* Initialize Checkboxes */\n\t\t\t$('.CodeMirror-code').on('mousedown.checkbox touchstart.checkbox', '.cm-formatting-task', function (e) {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopImmediatePropagation();\n\t\t\t\tscope.toggleCheckbox(e.target);\n\t\t\t});\n\n\t\t\tsimplemde.codemirror.on('update', function () {\n\t\t\t\t// For strikethrough styling of completed tasks\n\t\t\t\t$('.CodeMirror-line').removeClass('completed-task');\n\t\t\t\t$('.CodeMirror-line:contains(\"[x]\")').addClass('completed-task');\n\t\t\t});\n\n\t\t\tsimplemde.codemirror.on('change', function() {\n\t\t\t\t$timeout(function() {\n\t\t\t\t\tscope.$apply(function () {\n\t\t\t\t\t\tscope.note.content = simplemde.value();\n\t\t\t\t\t\tscope.onEdit();\n\t\t\t\t\t\tscope.updateTitle();\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t\teditorElement.on('click', '.cm-link, .cm-url', function(event) {\n\t\t\t\tif(event.ctrlKey) {\n\t\t\t\t\tvar url = urlFinder(this);\n\t\t\t\t\tif(angular.isDefined(url)) {\n\t\t\t\t\t\twindow.open(url, '_blank');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}]);\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.directive('notesTooltip', function () {\n 'use strict';\n\n return {\n restrict: 'A',\n link: function (scope, element) {\n element.tooltip({'container': 'body'});\n\n element.on('$destroy', function() {\n element.tooltip('hide');\n });\n\n element.on('click', function() {\n element.tooltip('hide');\n });\n }\n };\n});\n","/**\n * filter by multiple words (AND operation)\n */\napp.filter('and', ['$filter', function ($filter) {\n\t'use strict';\n\treturn function (items, searchString) {\n\t\tvar searchValues = searchString.split(' ');\n\t\tvar filtered = items;\n\t\tfor(var i in searchValues) {\n\t\t\tfiltered = $filter('filter')(filtered, searchValues[i]);\n\t\t}\n\t\treturn filtered;\n\t};\n}]);\n","app.filter('categoryTitle', function () {\n\t'use strict';\n\treturn function (str) {\n\t\tif (str && (typeof str === 'string')) {\n\t\t\treturn str.replace(/\\//g, ' / ');\n\t\t} else {\n\t\t\treturn '';\n\t\t}\n\t};\n});\n","/**\n * group notes by (sub) category\n */\napp.filter('groupNotes', function () {\n\t'use strict';\n\treturn function (items, category) {\n\t\tif(category) {\n\t\t\tvar prevCat = null;\n\t\t\tfor(var i=0; i\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('is', function () {\n 'use strict';\n\n return {\n loading: false\n };\n});","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n// take care of fileconflicts by appending a number\napp.factory('NotesModel', function () {\n 'use strict';\n\n var NotesModel = function () {\n this.notes = [];\n this.notesIds = {};\n };\n\n NotesModel.prototype = {\n addAll: function (notes) {\n for(var i=0; i0) {\n var index = this.nthIndexOf(cat, '/', maxLevel);\n if(index>0) {\n cat = cat.substring(0, index);\n }\n }\n if(categories[cat]===undefined) {\n categories[cat] = 1;\n } else {\n categories[cat] += 1;\n }\n }\n var result = [];\n for(var category in categories) {\n if(details) {\n result.push({\n name: category,\n count: categories[category],\n });\n } else if(category) {\n result.push(category);\n }\n }\n if(!details) {\n result.sort();\n }\n return result;\n },\n\n };\n\n return new NotesModel();\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt \n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\napp.factory('SaveQueue', function($q) {\n 'use strict';\n\n var SaveQueue = function () {\n this._queue = {};\n this._flushLock = false;\n this._manualSaveActive = false;\n };\n\n SaveQueue.prototype = {\n add: function (note) {\n this._queue[note.id] = note;\n this._flush();\n },\n addManual: function (note) {\n this._manualSaveActive = true;\n this.add(note);\n },\n _flush: function () {\n // if there are no changes dont execute the requests\n var keys = Object.keys(this._queue);\n if(keys.length === 0 || this._flushLock) {\n return;\n } else {\n this._flushLock = true;\n }\n\n var self = this;\n var requests = [];\n\n // iterate over updated objects and run an update request for\n // each one of them\n for(var i=0; i