diff --git a/css/notes.css b/css/notes.css
index 5f4f1f4c2..c39c7bfe3 100644
--- a/css/notes.css
+++ b/css/notes.css
@@ -14,6 +14,25 @@
     padding-left: 2px;
 }
 
+#app-navigation li .nav-entry {
+	display: block;
+	width: 100%;
+	line-height: 44px;
+	min-height: 44px;
+	padding: 0 12px;
+	overflow: hidden;
+	box-sizing: border-box;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	color: #000;
+	opacity: .57;
+}
+
+#app-navigation li:hover .nav-entry,
+#app-navigation li:focus .nav-entry {
+	opacity: 1;
+}
+
 #app-navigation #note-add:hover span,
 #app-navigation #note-add:focus span {
     display: inline;
@@ -52,6 +71,25 @@
     opacity: 1 !important;
 }
 
+.note-search span {
+    background-position: 0 center;
+    background-origin: content-box;
+}
+
+.note-search input {
+    width: 100%;
+    padding-left: 20px;
+    background-color: transparent;
+    border: none;
+    border-radius: 0;
+}
+
+.note-search input:active,
+.note-search input:focus,
+.note-search input:hover {
+    border-bottom: 1px solid grey;
+}
+
 .tooltip {
     text-shadow: none;
     text-transform: none;
diff --git a/js/app/controllers/appcontroller.js b/js/app/controllers/appcontroller.js
index 609e9b19d..5fa07ec1d 100644
--- a/js/app/controllers/appcontroller.js
+++ b/js/app/controllers/appcontroller.js
@@ -15,4 +15,6 @@ app.controller('AppController', function ($scope, $location, is) {
             $location.path('/notes/' + lastViewedNote);
         }
     };
+
+    $scope.search = '';
 });
diff --git a/js/app/filters/and.js b/js/app/filters/and.js
new file mode 100644
index 000000000..61a3be8ec
--- /dev/null
+++ b/js/app/filters/and.js
@@ -0,0 +1,14 @@
+/**
+ * filter by multiple words (AND operation)
+ */
+app.filter('and', ['$filter', function ($filter) {
+	'use strict';
+	return function (items, searchString) {
+		var searchValues = searchString.split(' ');
+		var filtered = items;
+		for(var i in searchValues) {
+			filtered = $filter('filter')(filtered, searchValues[i]);
+		}
+		return filtered;
+	};
+}]);
diff --git a/js/public/app.min.js b/js/public/app.min.js
index c62615cdd..c1a996890 100644
--- a/js/public/app.min.js
+++ b/js/public/app.min.js
@@ -1,2 +1,2 @@
-!function(e,n,o,i,r){"use strict";var s=e.module("Notes",["restangular","ngRoute"]).config(["$provide","$routeProvider","RestangularProvider","$httpProvider","$windowProvider",function(t,e,n,i,r){i.defaults.headers.common.requesttoken=o,t.value("Constants",{saveInterval:5e3}),e.when("/notes/:noteId",{templateUrl:"note.html",controller:"NoteController",resolve:{note:["$route","$q","is","Restangular",function(t,e,n,o){var i=e.defer(),r=t.current.params.noteId;return n.loading=!0,o.one("notes",r).get().then(function(t){n.loading=!1,i.resolve(t)},function(){n.loading=!1,i.reject()}),i.promise}]}}).otherwise({redirectTo:"/"});var s=OC.generateUrl("/apps/notes");n.setBaseUrl(s)}]).run(["$rootScope","$location","NotesModel",function(t,e,o){n('link[rel="shortcut icon"]').attr("href",OC.filePath("notes","img","favicon.png")),t.$on("$routeChangeError",function(){var t=o.getAll();if(t.length>0){var n=t.sort(function(t,e){return t.modified>e.modified?1:t.modified<e.modified?-1:0}),i=t[n.length-1];e.path("/notes/"+i.id)}else e.path("/")})}]);s.controller("AppController",["$scope","$location","is",function(t,e,n){t.is=n,t.init=function(t){0!==t&&e.path("/notes/"+t)}}]),s.controller("NoteController",["$routeParams","$scope","NotesModel","SaveQueue","note",function(e,n,o,i,r){o.updateIfExists(r),n.note=o.get(e.noteId),n.isSaving=function(){return i.isSaving()},n.updateTitle=function(){n.note.title=n.note.content.split("\n")[0]||t("notes","New note")},n.save=function(){var t=n.note;i.add(t)}}]),s.controller("NotesController",["$routeParams","$scope","$location","Restangular","NotesModel",function(t,e,n,o,i){e.route=t,e.notes=i.getAll();var r=o.all("notes");r.getList().then(function(t){i.addAll(t)}),e.create=function(){r.post().then(function(t){i.add(t),n.path("/notes/"+t.id)})},e["delete"]=function(t){var n=i.get(t);n.remove().then(function(){i.remove(t),e.$emit("$routeChangeError")})},e.toggleFavorite=function(t){var e=i.get(t);e.customPUT({favorite:!e.favorite},"favorite",{},{}).then(function(t){e.favorite=!!t})}}]),s.filter("noteTitle",function(){return function(t){return t=t.split("\n")[0]||"newNote",t.trim().replace(/^#+/g,"")}}),s.filter("wordCount",function(){return function(t){if(t&&"string"==typeof t){var e=t.split(/\s+/).filter(function(t){return t.search(/[A-Za-z0-9]/)!==-1}).length;return window.n("notes","%n word","%n words",e)}return 0}}),s.directive("notesAutofocus",function(){return{restrict:"A",link:function(t,e){e.focus()}}}),s.directive("editor",["$timeout",function(t){return{restrict:"A",link:function(e,o){var r=i(o[0],{change:function(n){t(function(){e.$apply(function(){e.note.content=n,e.updateTitle()})})}});r.setValue(e.note.content),o.on("click",".link",function(t){if(t.ctrlKey){var e=n(this).find(".link-params-inner").text();window.open(e,"_blank")}})}}}]),s.directive("notesIsSaving",["$window",function(e){return{restrict:"A",scope:{notesIsSaving:"="},link:function(n){e.onbeforeunload=function(){return n.notesIsSaving?t("notes","Note is currently saving. Leaving the page will delete all changes!"):null}}}}]),s.directive("notesTimeoutChange",["$timeout",function(t){return{restrict:"A",link:function(e,o,i){var r,s=300;n(o).bind("input propertychange paste",function(){t.cancel(r),r=t(function(){e.$apply(i.notesTimeoutChange)},s)})}}}]),s.directive("notesTooltip",function(){return{restrict:"A",link:function(t,e){e.tooltip()}}}),s.factory("is",function(){return{loading:!1}}),s.factory("NotesModel",function(){var t=function(){this.notes=[],this.notesIds={}};return t.prototype={addAll:function(t){for(var e=0;e<t.length;e+=1)this.add(t[e])},add:function(t){this.updateIfExists(t)},getAll:function(){return this.notes},get:function(t){return this.notesIds[t]},updateIfExists:function(t){var n=this.notesIds[t.id];e.isDefined(n)?(n.title=t.title,n.modified=t.modified,n.content=t.content,n.favorite=t.favorite):(this.notes.push(t),this.notesIds[t.id]=t)},remove:function(t){for(var e=0;e<this.notes.length;e+=1){var n=this.notes[e];if(n.id===t){this.notes.splice(e,1),delete this.notesIds[t];break}}}},new t}),s.factory("SaveQueue",["$q",function(t){var e=function(){this._queue={},this._flushLock=!1};return e.prototype={add:function(t){this._queue[t.id]=t,this._flush()},_flush:function(){var e=Object.keys(this._queue);if(0!==e.length&&!this._flushLock){this._flushLock=!0;for(var n=this,o=[],i=0;i<e.length;i+=1){var r=this._queue[e[i]];o.push(r.put().then(this._noteUpdateRequest.bind(null,r)))}this._queue={},t.all(o).then(function(){n._flushLock=!1,n._flush()})}},_noteUpdateRequest:function(t,e){t.title=e.title,t.modified=e.modified},isSaving:function(){return this._flushLock}},new e}])}(angular,jQuery,oc_requesttoken,mdEdit);
+!function(e,n,o,i,r){"use strict";var u=e.module("Notes",["restangular","ngRoute"]).config(["$provide","$routeProvider","RestangularProvider","$httpProvider","$windowProvider",function(t,e,n,i,r){i.defaults.headers.common.requesttoken=o,t.value("Constants",{saveInterval:5e3}),e.when("/notes/:noteId",{templateUrl:"note.html",controller:"NoteController",resolve:{note:["$route","$q","is","Restangular",function(t,e,n,o){var i=e.defer(),r=t.current.params.noteId;return n.loading=!0,o.one("notes",r).get().then(function(t){n.loading=!1,i.resolve(t)},function(){n.loading=!1,i.reject()}),i.promise}]}}).otherwise({redirectTo:"/"});var u=OC.generateUrl("/apps/notes");n.setBaseUrl(u)}]).run(["$rootScope","$location","NotesModel",function(t,e,o){n('link[rel="shortcut icon"]').attr("href",OC.filePath("notes","img","favicon.png")),t.$on("$routeChangeError",function(){var t=o.getAll();if(t.length>0){var n=t.sort(function(t,e){return t.modified>e.modified?1:t.modified<e.modified?-1:0}),i=t[n.length-1];e.path("/notes/"+i.id)}else e.path("/")})}]);u.controller("AppController",["$scope","$location","is",function(t,e,n){t.is=n,t.init=function(t){0!==t&&e.path("/notes/"+t)},t.search=""}]),u.controller("NoteController",["$routeParams","$scope","NotesModel","SaveQueue","note",function(e,n,o,i,r){o.updateIfExists(r),n.note=o.get(e.noteId),n.isSaving=function(){return i.isSaving()},n.updateTitle=function(){n.note.title=n.note.content.split("\n")[0]||t("notes","New note")},n.save=function(){var t=n.note;i.add(t)}}]),u.controller("NotesController",["$routeParams","$scope","$location","Restangular","NotesModel",function(t,e,n,o,i){e.route=t,e.notes=i.getAll();var r=o.all("notes");r.getList().then(function(t){i.addAll(t)}),e.create=function(){r.post().then(function(t){i.add(t),n.path("/notes/"+t.id)})},e["delete"]=function(t){var n=i.get(t);n.remove().then(function(){i.remove(t),e.$emit("$routeChangeError")})},e.toggleFavorite=function(t){var e=i.get(t);e.customPUT({favorite:!e.favorite},"favorite",{},{}).then(function(t){e.favorite=!!t})}}]),u.directive("notesAutofocus",function(){return{restrict:"A",link:function(t,e){e.focus()}}}),u.directive("editor",["$timeout",function(t){return{restrict:"A",link:function(e,o){var r=i(o[0],{change:function(n){t(function(){e.$apply(function(){e.note.content=n,e.updateTitle()})})}});r.setValue(e.note.content),o.on("click",".link",function(t){if(t.ctrlKey){var e=n(this).find(".link-params-inner").text();window.open(e,"_blank")}})}}}]),u.directive("notesIsSaving",["$window",function(e){return{restrict:"A",scope:{notesIsSaving:"="},link:function(n){e.onbeforeunload=function(){return n.notesIsSaving?t("notes","Note is currently saving. Leaving the page will delete all changes!"):null}}}}]),u.directive("notesTimeoutChange",["$timeout",function(t){return{restrict:"A",link:function(e,o,i){var r,u=300;n(o).bind("input propertychange paste",function(){t.cancel(r),r=t(function(){e.$apply(i.notesTimeoutChange)},u)})}}}]),u.directive("notesTooltip",function(){return{restrict:"A",link:function(t,e){e.tooltip()}}}),u.filter("and",["$filter",function(t){return function(e,n){var o=n.split(" "),i=e;for(var r in o)i=t("filter")(i,o[r]);return i}}]),u.filter("noteTitle",function(){return function(t){return t=t.split("\n")[0]||"newNote",t.trim().replace(/^#+/g,"")}}),u.filter("wordCount",function(){return function(t){if(t&&"string"==typeof t){var e=t.split(/\s+/).filter(function(t){return t.search(/[A-Za-z0-9]/)!==-1}).length;return window.n("notes","%n word","%n words",e)}return 0}}),u.factory("is",function(){return{loading:!1}}),u.factory("NotesModel",function(){var t=function(){this.notes=[],this.notesIds={}};return t.prototype={addAll:function(t){for(var e=0;e<t.length;e+=1)this.add(t[e])},add:function(t){this.updateIfExists(t)},getAll:function(){return this.notes},get:function(t){return this.notesIds[t]},updateIfExists:function(t){var n=this.notesIds[t.id];e.isDefined(n)?(n.title=t.title,n.modified=t.modified,n.content=t.content,n.favorite=t.favorite):(this.notes.push(t),this.notesIds[t.id]=t)},remove:function(t){for(var e=0;e<this.notes.length;e+=1){var n=this.notes[e];if(n.id===t){this.notes.splice(e,1),delete this.notesIds[t];break}}}},new t}),u.factory("SaveQueue",["$q",function(t){var e=function(){this._queue={},this._flushLock=!1};return e.prototype={add:function(t){this._queue[t.id]=t,this._flush()},_flush:function(){var e=Object.keys(this._queue);if(0!==e.length&&!this._flushLock){this._flushLock=!0;for(var n=this,o=[],i=0;i<e.length;i+=1){var r=this._queue[e[i]];o.push(r.put().then(this._noteUpdateRequest.bind(null,r)))}this._queue={},t.all(o).then(function(){n._flushLock=!1,n._flush()})}},_noteUpdateRequest:function(t,e){t.title=e.title,t.modified=e.modified},isSaving:function(){return this._flushLock}},new e}])}(angular,jQuery,oc_requesttoken,mdEdit);
 //# sourceMappingURL=app.min.js.map
diff --git a/js/public/app.min.js.map b/js/public/app.min.js.map
index e28f98c95..5e94a609b 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","filters/noteTitle.js","filters/wordCount.js","directives/autofocus.js","directives/editor.js","directives/issaving.js","directives/timeoutchange.js","directives/tooltip.js","services/is.js","services/notesmodel.js","services/savequeue.js"],"names":["angular","$","requestToken","mdEdit","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","$routeParams","SaveQueue","updateIfExists","isSaving","updateTitle","title","content","split","t","save","add","route","notesResource","all","getList","addAll","create","post","remove","$emit","toggleFavorite","customPUT","favorite","filter","trim","replace","wordCount","search","window","n","directive","restrict","link","scope","element","focus","$timeout","editor","change","$apply","setValue","on","event","ctrlKey","url","this","find","text","open","$window","notesIsSaving","onbeforeunload","attributes","timeout","interval","bind","cancel","notesTimeoutChange","tooltip","factory","notesIds","prototype","i","updated","isDefined","push","splice","_queue","_flushLock","_flush","keys","Object","self","requests","put","_noteUpdateRequest","response","jQuery","oc_requesttoken"],"mappings":"CAAA,SAAAA,EAAAC,EAAAC,EAAAC,EAAAC,GAAA,YAQA,IAAAC,GAAAL,EAAAM,OAAA,SAAA,cAAA,YACAC,QAAA,WAAA,iBAAA,sBAAA,gBAAA,kBAAA,SAAAC,EAAAC,EAAAC,EAAAC,EACAC,GAIAD,EAAAE,SAAAC,QAAAC,OAAAC,aAAAd,EAIAM,EAAAS,MAAA,aACAC,aAAA,MAIAT,EAAAU,KAAA,kBACAC,YAAA,YACAC,WAAA,iBACAC,SAKAC,MAAA,SAAA,KAAA,KAAA,cAAA,SAAAC,EAAAC,EAAAC,EAAAC,GAEA,GAAAC,GAAAH,EAAAI,QACAC,EAAAN,EAAAO,QAAAC,OAAAF,MAWA,OAVAJ,GAAAO,SAAA,EAEAN,EAAAO,IAAA,QAAAJ,GAAAK,MAAAC,KAAA,SAAAb,GACAG,EAAAO,SAAA,EACAL,EAAAN,QAAAC,IACA,WACAG,EAAAO,SAAA,EACAL,EAAAS,WAGAT,EAAAU,aAGAC,WACAC,WAAA,KAGA,IAAAC,GAAAC,GAAAC,YAAA,cACAjC,GAAAkC,WAAAH,MAIAI,KAAA,aAAA,YAAA,aAAA,SAAAC,EAAAC,EAAAC,GAGA/C,EAAA,6BAAAgD,KACA,OACAP,GAAAQ,SAAA,QAAA,MAAA,gBAIAJ,EAAAK,IAAA,oBAAA,WACA,GAAAC,GAAAJ,EAAAK,QAGA,IAAAD,EAAAE,OAAA,EAAA,CACA,GAAAC,GAAAH,EAAAI,KAAA,SAAAC,EAAAC,GACA,MAAAD,GAAAE,SAAAD,EAAAC,SACA,EACAF,EAAAE,SAAAD,EAAAC,YAGA,IAIApC,EAAA6B,EAAAG,EAAAD,OAAA,EACAP,GAAAa,KAAA,UAAArC,EAAAsC,QAEAd,GAAAa,KAAA,SC7EAvD,GAAAgB,WAAA,iBAAA,SAAA,YAAA,KAAA,SAAAyC,EAAAf,EAAArB,GAGAoC,EAAApC,GAAAA,EAEAoC,EAAAC,KAAA,SAAAC,GACA,IAAAA,GACAjB,EAAAa,KAAA,UAAAI,OCPA3D,EAAAgB,WAAA,kBAAA,eAAA,SAAA,aAAA,YAAA,OAAA,SAAA4C,EAAAH,EAAAd,EACAkB,EAAA3C,GAGAyB,EAAAmB,eAAA5C,GAEAuC,EAAAvC,KAAAyB,EAAAb,IAAA8B,EAAAnC,QAEAgC,EAAAM,SAAA,WACA,MAAAF,GAAAE,YAGAN,EAAAO,YAAA,WACAP,EAAAvC,KAAA+C,MAAAR,EAAAvC,KAAAgD,QAAAC,MAAA,MAAA,IACAC,EAAA,QAAA,aAGAX,EAAAY,KAAA,WACA,GAAAnD,GAAAuC,EAAAvC,IACA2C,GAAAS,IAAApD,OClBAlB,EAAAgB,WAAA,mBAAA,eAAA,SAAA,YAAA,cAAA,aAAA,SAAA4C,EAAAH,EAAAf,EACApB,EAAAqB,GAGAc,EAAAc,MAAAX,EACAH,EAAAV,MAAAJ,EAAAK,QAEA,IAAAwB,GAAAlD,EAAAmD,IAAA,QAGAD,GAAAE,UAAA3C,KAAA,SAAAgB,GACAJ,EAAAgC,OAAA5B,KAGAU,EAAAmB,OAAA,WACAJ,EAAAK,OAAA9C,KAAA,SAAAb,GACAyB,EAAA2B,IAAApD,GACAwB,EAAAa,KAAA,UAAArC,EAAAsC,OAIAC,EAAAA,UAAA,SAAAhC,GACA,GAAAP,GAAAyB,EAAAb,IAAAL,EACAP,GAAA4D,SAAA/C,KAAA,WACAY,EAAAmC,OAAArD,GACAgC,EAAAsB,MAAA,wBAIAtB,EAAAuB,eAAA,SAAAvD,GACA,GAAAP,GAAAyB,EAAAb,IAAAL,EACAP,GAAA+D,WAAAC,UAAAhE,EAAAgE,UACA,kBAAAnD,KAAA,SAAAmD,GACAhE,EAAAgE,WAAAA,QCtCAlF,EAAAmF,OAAA,YAAA,WAEA,MAAA,UAAAvE,GAEA,MADAA,GAAAA,EAAAuD,MAAA,MAAA,IAAA,UACAvD,EAAAwE,OAAAC,QAAA,OAAA,OCPArF,EAAAmF,OAAA,YAAA,WAEA,MAAA,UAAAvE,GACA,GAAAA,GAAA,gBAAAA,GAAA,CACA,GAAA0E,GAAA1E,EAAAuD,MAAA,OAAAgB,OAGA,SAAAvE,GACA,MAAAA,GAAA2E,OAAA,sBAEAtC,MACA,OAAAuC,QAAAC,EAAA,QAAA,UAAA,WAAAH,GAEA,MAAA,MCNAtF,EAAA0F,UAAA,iBAAA,WAEA,OACAC,SAAA,IACAC,KAAA,SAAAC,EAAAC,GACAA,EAAAC,YCXA/F,EAAA0F,UAAA,UAAA,WAAA,SAAAM,GAEA,OACAL,SAAA,IACAC,KAAA,SAAAC,EAAAC,GACA,GAAAG,GAAAnG,EAAAgG,EAAA,IAAAI,OAAA,SAAAtF,GACAoF,EAAA,WACAH,EAAAM,OAAA,WACAN,EAAA3E,KAAAgD,QAAAtD,EACAiF,EAAA7B,oBAIAiC,GAAAG,SAAAP,EAAA3E,KAAAgD,SACA4B,EAAAO,GAAA,QAAA,QAAA,SAAAC,GACA,GAAAA,EAAAC,QAAA,CACA,GAAAC,GAAA5G,EAAA6G,MAAAC,KAAA,sBAAAC,MACAnB,QAAAoB,KAAAJ,EAAA,kBCXAxG,EAAA0F,UAAA,iBAAA,UAAA,SAAAmB,GAEA,OACAlB,SAAA,IACAE,OACAiB,cAAA,KAEAlB,KAAA,SAAAC,GACAgB,EAAAE,eAAA,WACA,MAAAlB,GAAAiB,cACA1C,EAAA,QAAA,uEAGA,WCTApE,EAAA0F,UAAA,sBAAA,WAAA,SAAAM,GAGA,OACAL,SAAA,IACAC,KAAA,SAAAC,EAAAC,EAAAkB,GACA,GACAC,GADAC,EAAA,GAGAtH,GAAAkG,GAAAqB,KAAA,6BAAA,WACAnB,EAAAoB,OAAAH,GAEAA,EAAAjB,EAAA,WACAH,EAAAM,OAAAa,EAAAK,qBACAH,UClBAlH,EAAA0F,UAAA,eAAA,WAGA,OACAC,SAAA,IACAC,KAAA,SAAAC,EAAAC,GACAA,EAAAwB,cCNAtH,EAAAuH,QAAA,KAAA,WAGA,OACA3F,SAAA,KCHA5B,EAAAuH,QAAA,aAAA,WAGA,GAAA5E,GAAA,WACA8D,KAAA1D,SACA0D,KAAAe,YA0CA,OAvCA7E,GAAA8E,WACA9C,OAAA,SAAA5B,GACA,IAAA,GAAA2E,GAAA,EAAAA,EAAA3E,EAAAE,OAAAyE,GAAA,EACAjB,KAAAnC,IAAAvB,EAAA2E,KAGApD,IAAA,SAAApD,GACAuF,KAAA3C,eAAA5C,IAEA8B,OAAA,WACA,MAAAyD,MAAA1D,OAEAjB,IAAA,SAAA0B,GACA,MAAAiD,MAAAe,SAAAhE,IAEAM,eAAA,SAAA6D,GACA,GAAAzG,GAAAuF,KAAAe,SAAAG,EAAAnE,GACA7D,GAAAiI,UAAA1G,IACAA,EAAA+C,MAAA0D,EAAA1D,MACA/C,EAAAoC,SAAAqE,EAAArE,SACApC,EAAAgD,QAAAyD,EAAAzD,QACAhD,EAAAgE,SAAAyC,EAAAzC,WAEAuB,KAAA1D,MAAA8E,KAAAF,GACAlB,KAAAe,SAAAG,EAAAnE,IAAAmE,IAGA7C,OAAA,SAAAtB,GACA,IAAA,GAAAkE,GAAA,EAAAA,EAAAjB,KAAA1D,MAAAE,OAAAyE,GAAA,EAAA,CACA,GAAAxG,GAAAuF,KAAA1D,MAAA2E,EACA,IAAAxG,EAAAsC,KAAAA,EAAA,CACAiD,KAAA1D,MAAA+E,OAAAJ,EAAA,SACAjB,MAAAe,SAAAhE,EACA,WAMA,GAAAb,KChDA3C,EAAAuH,QAAA,aAAA,KAAA,SAAAnG,GAGA,GAAAyC,GAAA,WACA4C,KAAAsB,UACAtB,KAAAuB,YAAA,EAgDA,OA7CAnE,GAAA4D,WACAnD,IAAA,SAAApD,GACAuF,KAAAsB,OAAA7G,EAAAsC,IAAAtC,EACAuF,KAAAwB,UAEAA,OAAA,WAEA,GAAAC,GAAAC,OAAAD,KAAAzB,KAAAsB,OACA,IAAA,IAAAG,EAAAjF,SAAAwD,KAAAuB,WAAA,CAGAvB,KAAAuB,YAAA,CAQA,KAAA,GALAI,GAAA3B,KACA4B,KAIAX,EAAA,EAAAA,EAAAQ,EAAAjF,OAAAyE,GAAA,EAAA,CACA,GAAAxG,GAAAuF,KAAAsB,OAAAG,EAAAR,GAGAW,GAAAR,KAAA3G,EAAAoH,MAAAvG,KACA0E,KAAA8B,mBAAApB,KAAA,KAAAjG,KAGAuF,KAAAsB,UAIA3G,EAAAqD,IAAA4D,GAAAtG,KAAA,WACAqG,EAAAJ,YAAA,EACAI,EAAAH,aAGAM,mBAAA,SAAArH,EAAAsH,GACAtH,EAAA+C,MAAAuE,EAAAvE,MACA/C,EAAAoC,SAAAkF,EAAAlF,UAEAS,SAAA,WACA,MAAA0C,MAAAuB,aAIA,GAAAnE,OACAlE,QAAA8I,OAAAC,gBAAA5I","file":"app.min.js","sourcesContent":["/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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 <dev@bernhard-posselt.com>\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) {\n        if(lastViewedNote !== 0) {\n            $location.path('/notes/' + lastViewedNote);\n        }\n    };\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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) {\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\n    $scope.updateTitle = function () {\n        $scope.note.title = $scope.note.content.split('\\n')[0] ||\n            t('notes', 'New note');\n    };\n\n    $scope.save = function() {\n        var note = $scope.note;\n        SaveQueue.add(note);\n    };\n\n});","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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) {\n    'use strict';\n\n    $scope.route = $routeParams;\n    $scope.notes = NotesModel.getAll();\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    });\n\n    $scope.create = function () {\n        notesResource.post().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) {\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    };\n\n});\n","/**\n * removes whitespaces and leading #\n */\napp.filter('noteTitle', function () {\n\t'use strict';\n\treturn function (value) {\n        \tvalue = value.split('\\n')[0] || 'newNote';\n\t\treturn value.trim().replace(/^#+/g, '');\n\t};\n});\n","app.filter('wordCount', function () {\n\t'use strict';\n\treturn function (value) {\n\t\tif (value && (typeof value === 'string')) {\n\t\t\tvar wordCount = value.split(/\\s+/).filter(\n\t\t\t\t// only count words containing\n\t\t\t\t// at least one alphanumeric character\n\t\t\t\tfunction(value) {\n\t\t\t\t\treturn value.search(/[A-Za-z0-9]/) !== -1;\n\t\t\t\t}\n\t\t\t).length;\n\t\t\treturn window.n('notes', '%n word', '%n words', wordCount);\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t};\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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 mdEdit*/\napp.directive('editor', ['$timeout', function ($timeout) {\n\t'use strict';\n\treturn {\n\t\trestrict: 'A',\n\t\tlink: function(scope, element) {\n\t\t\tvar editor = mdEdit(element[0], {change: function(value) {\n\t\t\t\t$timeout(function(){\n\t\t\t\t\tscope.$apply(function() {\n\t\t\t\t\t\tscope.note.content = value;\n\t\t\t\t\t\tscope.updateTitle();\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}});\n\t\t\teditor.setValue(scope.note.content);\n\t\t\telement.on('click', '.link', function(event) {\n\t\t\t\tif(event.ctrlKey) {\n\t\t\t\t\tvar url = $(this).find('.link-params-inner').text();\n\t\t\t\t\twindow.open(url, '_blank');\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}]);\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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('notesIsSaving', function ($window) {\n    'use strict';\n    return {\n        restrict: 'A',\n        scope: {\n            'notesIsSaving': '='\n        },\n        link: function (scope) {\n            $window.onbeforeunload = function () {\n                if (scope.notesIsSaving) {\n                    return t('notes', 'Note is currently saving. Leaving ' +\n                                      'the page will delete all changes!');\n                } else {\n                    return null;\n                }\n            };\n        }\n    };\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n/**\n * Like ng-change only that it does not fire when you type faster than\n * 300 ms\n */\napp.directive('notesTimeoutChange', function ($timeout) {\n    'use strict';\n\n    return {\n        restrict: 'A',\n        link: function (scope, element, attributes) {\n            var interval = 300;  // 300 miliseconds timeout after typing\n            var timeout;\n\n            $(element).bind('input propertychange paste', function () {\n                $timeout.cancel(timeout);\n\n                timeout = $timeout(function () {\n                    scope.$apply(attributes.notesTimeoutChange);\n                }, interval);\n            });\n        }\n    };\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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();\n        }\n    };\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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 <dev@bernhard-posselt.com>\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; i<notes.length; i+=1) {\n                this.add(notes[i]);\n            }\n        },\n        add: function(note) {\n            this.updateIfExists(note);\n        },\n        getAll: function () {\n            return this.notes;\n        },\n        get: function (id) {\n            return this.notesIds[id];\n        },\n        updateIfExists: function(updated) {\n            var note = this.notesIds[updated.id];\n            if(angular.isDefined(note)) {\n                note.title = updated.title;\n                note.modified = updated.modified;\n                note.content = updated.content;\n                note.favorite = updated.favorite;\n            } else {\n                this.notes.push(updated);\n                this.notesIds[updated.id] = updated;\n            }\n        },\n        remove: function (id) {\n            for(var i=0; i<this.notes.length; i+=1) {\n                var note = this.notes[i];\n                if(note.id === id) {\n                    this.notes.splice(i, 1);\n                    delete this.notesIds[id];\n                    break;\n                }\n            }\n        }\n    };\n\n    return new NotesModel();\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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    };\n\n    SaveQueue.prototype = {\n        add: function (note) {\n            this._queue[note.id] = note;\n            this._flush();\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<keys.length; i+=1) {\n                var note = this._queue[keys[i]];\n                // if the update finished, update the modified and title\n                // attributes on the note\n                requests.push(note.put().then(\n                    this._noteUpdateRequest.bind(null, note))\n                );\n            }\n            this._queue = {};\n\n            // if all update requests are completed, run the flush\n            // again to update the next batch of queued notes\n            $q.all(requests).then(function () {\n                self._flushLock = false;\n                self._flush();\n            });\n        },\n        _noteUpdateRequest: function (note, response) {\n            note.title = response.title;\n            note.modified = response.modified;\n        },\n        isSaving: function () {\n            return this._flushLock;\n        }\n    };\n\n    return new SaveQueue();\n});"]}
\ No newline at end of file
+{"version":3,"sources":["app.js","controllers/appcontroller.js","controllers/notecontroller.js","controllers/notescontroller.js","directives/autofocus.js","directives/editor.js","directives/issaving.js","directives/timeoutchange.js","directives/tooltip.js","filters/and.js","filters/noteTitle.js","filters/wordCount.js","services/is.js","services/notesmodel.js","services/savequeue.js"],"names":["angular","$","requestToken","mdEdit","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","search","$routeParams","SaveQueue","updateIfExists","isSaving","updateTitle","title","content","split","t","save","add","route","notesResource","all","getList","addAll","create","post","remove","$emit","toggleFavorite","customPUT","favorite","directive","restrict","link","scope","element","focus","$timeout","editor","change","$apply","setValue","on","event","ctrlKey","url","this","find","text","window","open","$window","notesIsSaving","onbeforeunload","attributes","timeout","interval","bind","cancel","notesTimeoutChange","tooltip","filter","$filter","items","searchString","searchValues","filtered","i","trim","replace","wordCount","n","factory","notesIds","prototype","updated","isDefined","push","splice","_queue","_flushLock","_flush","keys","Object","self","requests","put","_noteUpdateRequest","response","jQuery","oc_requesttoken"],"mappings":"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","file":"app.min.js","sourcesContent":["/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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 <dev@bernhard-posselt.com>\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) {\n        if(lastViewedNote !== 0) {\n            $location.path('/notes/' + lastViewedNote);\n        }\n    };\n\n    $scope.search = '';\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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) {\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\n    $scope.updateTitle = function () {\n        $scope.note.title = $scope.note.content.split('\\n')[0] ||\n            t('notes', 'New note');\n    };\n\n    $scope.save = function() {\n        var note = $scope.note;\n        SaveQueue.add(note);\n    };\n\n});","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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) {\n    'use strict';\n\n    $scope.route = $routeParams;\n    $scope.notes = NotesModel.getAll();\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    });\n\n    $scope.create = function () {\n        notesResource.post().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) {\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    };\n\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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 mdEdit*/\napp.directive('editor', ['$timeout', function ($timeout) {\n\t'use strict';\n\treturn {\n\t\trestrict: 'A',\n\t\tlink: function(scope, element) {\n\t\t\tvar editor = mdEdit(element[0], {change: function(value) {\n\t\t\t\t$timeout(function(){\n\t\t\t\t\tscope.$apply(function() {\n\t\t\t\t\t\tscope.note.content = value;\n\t\t\t\t\t\tscope.updateTitle();\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}});\n\t\t\teditor.setValue(scope.note.content);\n\t\t\telement.on('click', '.link', function(event) {\n\t\t\t\tif(event.ctrlKey) {\n\t\t\t\t\tvar url = $(this).find('.link-params-inner').text();\n\t\t\t\t\twindow.open(url, '_blank');\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t};\n}]);\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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('notesIsSaving', function ($window) {\n    'use strict';\n    return {\n        restrict: 'A',\n        scope: {\n            'notesIsSaving': '='\n        },\n        link: function (scope) {\n            $window.onbeforeunload = function () {\n                if (scope.notesIsSaving) {\n                    return t('notes', 'Note is currently saving. Leaving ' +\n                                      'the page will delete all changes!');\n                } else {\n                    return null;\n                }\n            };\n        }\n    };\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING file.\n */\n\n/**\n * Like ng-change only that it does not fire when you type faster than\n * 300 ms\n */\napp.directive('notesTimeoutChange', function ($timeout) {\n    'use strict';\n\n    return {\n        restrict: 'A',\n        link: function (scope, element, attributes) {\n            var interval = 300;  // 300 miliseconds timeout after typing\n            var timeout;\n\n            $(element).bind('input propertychange paste', function () {\n                $timeout.cancel(timeout);\n\n                timeout = $timeout(function () {\n                    scope.$apply(attributes.notesTimeoutChange);\n                }, interval);\n            });\n        }\n    };\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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();\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","/**\n * removes whitespaces and leading #\n */\napp.filter('noteTitle', function () {\n\t'use strict';\n\treturn function (value) {\n        \tvalue = value.split('\\n')[0] || 'newNote';\n\t\treturn value.trim().replace(/^#+/g, '');\n\t};\n});\n","app.filter('wordCount', function () {\n\t'use strict';\n\treturn function (value) {\n\t\tif (value && (typeof value === 'string')) {\n\t\t\tvar wordCount = value.split(/\\s+/).filter(\n\t\t\t\t// only count words containing\n\t\t\t\t// at least one alphanumeric character\n\t\t\t\tfunction(value) {\n\t\t\t\t\treturn value.search(/[A-Za-z0-9]/) !== -1;\n\t\t\t\t}\n\t\t\t).length;\n\t\t\treturn window.n('notes', '%n word', '%n words', wordCount);\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t};\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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 <dev@bernhard-posselt.com>\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; i<notes.length; i+=1) {\n                this.add(notes[i]);\n            }\n        },\n        add: function(note) {\n            this.updateIfExists(note);\n        },\n        getAll: function () {\n            return this.notes;\n        },\n        get: function (id) {\n            return this.notesIds[id];\n        },\n        updateIfExists: function(updated) {\n            var note = this.notesIds[updated.id];\n            if(angular.isDefined(note)) {\n                note.title = updated.title;\n                note.modified = updated.modified;\n                note.content = updated.content;\n                note.favorite = updated.favorite;\n            } else {\n                this.notes.push(updated);\n                this.notesIds[updated.id] = updated;\n            }\n        },\n        remove: function (id) {\n            for(var i=0; i<this.notes.length; i+=1) {\n                var note = this.notes[i];\n                if(note.id === id) {\n                    this.notes.splice(i, 1);\n                    delete this.notesIds[id];\n                    break;\n                }\n            }\n        }\n    };\n\n    return new NotesModel();\n});\n","/**\n * Copyright (c) 2013, Bernhard Posselt <dev@bernhard-posselt.com>\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    };\n\n    SaveQueue.prototype = {\n        add: function (note) {\n            this._queue[note.id] = note;\n            this._flush();\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<keys.length; i+=1) {\n                var note = this._queue[keys[i]];\n                // if the update finished, update the modified and title\n                // attributes on the note\n                requests.push(note.put().then(\n                    this._noteUpdateRequest.bind(null, note))\n                );\n            }\n            this._queue = {};\n\n            // if all update requests are completed, run the flush\n            // again to update the next batch of queued notes\n            $q.all(requests).then(function () {\n                self._flushLock = false;\n                self._flush();\n            });\n        },\n        _noteUpdateRequest: function (note, response) {\n            note.title = response.title;\n            note.modified = response.modified;\n        },\n        isSaving: function () {\n            return this._flushLock;\n        }\n    };\n\n    return new SaveQueue();\n});"]}
\ No newline at end of file
diff --git a/js/tests/unit/filters/andfilterSpec.js b/js/tests/unit/filters/andfilterSpec.js
new file mode 100644
index 000000000..1f4495252
--- /dev/null
+++ b/js/tests/unit/filters/andfilterSpec.js
@@ -0,0 +1,36 @@
+describe('and filter', function() {
+	'use strict';
+
+	var result;
+	var $filter;
+
+	beforeEach(module('Notes'));
+
+	beforeEach(inject(function(_$filter_) {
+		$filter = _$filter_;
+	}));
+
+
+	it ('should do nothing if search string is empty', function() {
+		result = $filter('and')([], '');
+		expect(result).toEqual([]);
+
+		result = $filter('and')(['a', 'lot', 'of', 'strings'], '');
+		expect(result).toEqual(['a', 'lot', 'of', 'strings']);
+	});
+
+	it ('should match single words', function() {
+		result = $filter('and')(['a', 'ad', 'multiple words'], 'd');
+		expect(result).toEqual(['ad', 'multiple words']);
+	});
+
+	it ('should math multiple words', function() {
+		result = $filter('and')(['a b c', 'a c e', 'a d'], 'a c');
+		expect(result).toEqual(['a b c', 'a c e']);
+	});
+
+	it ('should return nothing if nothing matches', function() {
+		result = $filter('and')(['brown fox jumps over the lazy dog'], 'quick');
+		expect(result).toEqual([]);
+	});
+});
diff --git a/templates/main.php b/templates/main.php
index 7d8c0400c..509203c6c 100644
--- a/templates/main.php
+++ b/templates/main.php
@@ -36,13 +36,18 @@
 
     <div id="app-navigation" ng-controller="NotesController">
         <ul>
+            <li class="note-search">
+                <span class="nav-entry icon-search">
+                    <input type="text" ng-model="search" />
+                </span>
+            </li>
             <!-- new note button -->
             <li id="note-add" ng-click="create()"
                 oc-click-focus="{ selector: '#app-content textarea' }">
                 <a href='#'>+ <span><?php p($l->t('New note')); ?></span></a>
             </li>
             <!-- notes list -->
-            <li ng-repeat="note in notes|orderBy:['-favorite','-modified']"
+            <li ng-repeat="note in filteredNotes = (notes| and:search | orderBy:['-favorite','-modified'])"
                 ng-class="{ active: note.id == route.noteId }">
                 <a href="#/notes/{{ note.id }}">
                     {{ note.title | noteTitle }}
@@ -61,6 +66,11 @@
                         ng-class="{'icon-starred': note.favorite}"></button>
                 </span>
             </li>
+            <li ng-hide="filteredNotes.length">
+                <span class="nav-entry">
+                    <?php p($l->t('No notes found')); ?>
+                </span>
+            </li>
 
         </ul>
     </div>