11"use strict" ;
22
33var m = require ( "mithril" ) ;
4+ var FuzzySort = require ( "fuzzysort" ) ;
45var app = "com.dannyvankooten.browserpass" ;
56var activeTab ;
67var searching = false ;
7- var logins ;
8+ var resultLogins = [ ] ;
9+ var logins = [ ] ;
10+ var fillOnSubmit = false ;
811var error ;
912var domain , urlDuringSearch ;
1013
@@ -24,10 +27,10 @@ function view() {
2427 results = m ( "div.status-text" , "Error: " + error ) ;
2528 error = undefined ;
2629 } else if ( logins ) {
27- if ( logins . length === 0 ) {
30+ if ( logins . length === 0 && domain && domain . length > 0 ) {
2831 results = m (
2932 "div.status-text" ,
30- m . trust ( `No passwords found for <strong>${ domain } </strong>.` )
33+ m . trust ( `No matching passwords found for <strong>${ domain } </strong>.` )
3134 ) ;
3235 } else if ( logins . length > 0 ) {
3336 results = logins . map ( function ( login ) {
@@ -61,22 +64,29 @@ function view() {
6164 m (
6265 "form" ,
6366 {
64- onsubmit : submitSearchForm
67+ onsubmit : submitSearchForm ,
68+ onkeydown : searchKeyHandler
6569 } ,
6670 [
67- m ( "input" , {
68- type : "text" ,
69- id : "search-field" ,
70- name : "s" ,
71- placeholder : "Search password.." ,
72- autocomplete : "off" ,
73- autofocus : "on"
71+ m ( "div" , {
72+ "id" : "filter-search"
7473 } ) ,
75- m ( "input" , {
76- type : "submit" ,
77- value : "Search" ,
78- style : "display: none;"
79- } )
74+ m ( "div" , [
75+ m ( "input" , {
76+ type : "text" ,
77+ id : "search-field" ,
78+ name : "s" ,
79+ placeholder : "Search passwords.." ,
80+ autocomplete : "off" ,
81+ autofocus : "on" ,
82+ oninput : filterLogins
83+ } ) ,
84+ m ( "input" , {
85+ type : "submit" ,
86+ value : "Search" ,
87+ style : "display: none;"
88+ } )
89+ ] )
8090 ]
8191 )
8292 ] ) ,
@@ -86,15 +96,73 @@ function view() {
8696 ] ) ;
8797}
8898
99+ function filterLogins ( e ) {
100+ // use fuzzy search to filter results
101+ var filter = e . target . value . trim ( ) . split ( / [ \s \/ ] + / ) ;
102+ if ( filter . length > 0 ) {
103+ logins = resultLogins . slice ( 0 ) ;
104+ filter . forEach ( function ( word ) {
105+ if ( word . length > 0 ) {
106+ var refine = [ ] ;
107+ FuzzySort . go ( word , logins , { allowTypo : false } ) . forEach ( function ( result ) {
108+ refine . push ( result . target ) ;
109+ } ) ;
110+ logins = refine . slice ( 0 ) ;
111+ }
112+ } ) ;
113+
114+ // fill login forms on submit rather than initiating a search
115+ fillOnSubmit = logins . length > 0 ;
116+ } else {
117+ // reset the result list if the filter is empty
118+ logins = resultLogins . slice ( 0 ) ;
119+ }
120+
121+ // redraw the list
122+ m . redraw ( ) ;
123+
124+ // show / hide the filter hint
125+ showFilterHint ( logins . length ) ;
126+ }
127+
128+ function searchKeyHandler ( e ) {
129+ // switch to search mode if backspace is pressed and no filter text has been entered
130+ if ( e . code == "Backspace" && logins . length > 0 && e . target . value . length == 0 ) {
131+ e . preventDefault ( ) ;
132+ logins = resultLogins = [ ] ;
133+ e . target . value = fillOnSubmit ? '' : domain ;
134+ domain = '' ;
135+ showFilterHint ( false ) ;
136+ }
137+ }
138+
139+ function showFilterHint ( show = true ) {
140+ var filterHint = document . getElementById ( "filter-search" ) ;
141+ var searchField = document . getElementById ( "search-field" ) ;
142+ if ( show ) {
143+ filterHint . style . display = "block" ;
144+ searchField . setAttribute ( "placeholder" , "Refine search..." ) ;
145+ } else {
146+ filterHint . style . display = "none" ;
147+ searchField . setAttribute ( "placeholder" , "Search passwords..." ) ;
148+ }
149+ }
150+
89151function submitSearchForm ( e ) {
90152 e . preventDefault ( ) ;
91153
92- // don't search without input.
93- if ( ! this . s . value . length ) {
94- return ;
95- }
154+ if ( fillOnSubmit && logins . length > 0 ) {
155+ // fill using the first result
156+ getLoginData . bind ( logins [ 0 ] ) ( ) ;
157+ } else {
158+ // don't search without input.
159+ if ( ! this . s . value . length ) {
160+ return ;
161+ }
96162
97- searchPassword ( this . s . value ) ;
163+ // search for matching entries
164+ searchPassword ( this . s . value , "search" , false ) ;
165+ }
98166}
99167
100168function init ( tab ) {
@@ -108,9 +176,9 @@ function init(tab) {
108176 searchPassword ( activeDomain , "match_domain" ) ;
109177}
110178
111- function searchPassword ( _domain , action = "search" ) {
179+ function searchPassword ( _domain , action = "search" , useFillOnSubmit = true ) {
112180 searching = true ;
113- logins = null ;
181+ logins = resultLogins = [ ] ;
114182 domain = _domain ;
115183 urlDuringSearch = activeTab . url ;
116184 m . redraw ( ) ;
@@ -132,7 +200,13 @@ function searchPassword(_domain, action="search") {
132200 }
133201
134202 searching = false ;
135- logins = response ;
203+ logins = resultLogins = response ? response : [ ] ;
204+ document . getElementById ( "filter-search" ) . textContent = domain ;
205+ fillOnSubmit = useFillOnSubmit && logins . length > 0 ;
206+ if ( logins . length > 0 ) {
207+ showFilterHint ( true ) ;
208+ document . getElementById ( "search-field" ) . value = '' ;
209+ }
136210 m . redraw ( ) ;
137211 }
138212 ) ;
@@ -160,13 +234,14 @@ function getFaviconUrl(domain) {
160234
161235function getLoginData ( ) {
162236 searching = true ;
163- logins = null ;
237+ logins = resultLogins = [ ] ;
164238 m . redraw ( ) ;
165239
166240 chrome . runtime . sendMessage (
167241 { action : "login" , entry : this , urlDuringSearch : urlDuringSearch } ,
168242 function ( response ) {
169243 searching = false ;
244+ fillOnSubmit = false ;
170245
171246 if ( response . error ) {
172247 error = response . error ;
@@ -222,14 +297,16 @@ function keyHandler(e) {
222297 switchFocus ( "div.entry:first-child > .login" , "nextElementSibling" ) ;
223298 break ;
224299 case "c" :
225- if ( e . ctrlKey ) {
300+ if ( e . target . id != "search-field" && e . ctrlKey ) {
226301 document . activeElement [ "nextElementSibling" ] [
227302 "nextElementSibling"
228303 ] . click ( ) ;
229304 }
230305 break ;
231306 case "C" :
232- document . activeElement [ "nextElementSibling" ] . click ( ) ;
307+ if ( e . target . id != "search-field" ) {
308+ document . activeElement [ "nextElementSibling" ] . click ( ) ;
309+ }
233310 break ;
234311 }
235312}
0 commit comments