All files / manuel index.js

100% Statements 88/88
100% Branches 114/114
100% Functions 29/29
100% Lines 88/88
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382                154x             120x         63x 60x     3x     1x   11x           11x                     11x 11x 11x 11x 11x 11x 11x 11x 11x   11x 7x 1x 6x 1x 5x         3x   2x     4x             1x 1x 1x     44x 44x 44x 44x 44x 44x   44x   44x     44x     44x     44x         44x       158x         44x 42x     44x                           1x 1x   1x           1x                         29x                 71x         71x     33x   33x                                   71x               3x   3x   3x 2x     3x 2x               2x 1x               4x 3x             3x           42x                         75x                 79x         2x 2x                   42x       83x                 42x                       43x                                                 15x         15x         15x             15x       15x   7x       15x       15x                 9x 9x           44x     1x     1x
/* eslint-disable fp/no-mutating-methods */
/*
* The following utilities are all adapted from
* https://github.com/LeaVerou/awesomplete
*/
 
// String -> String -> Boolean
function contains(input, text) {
	return input.trim().length
		? RegExp(regExpEscape(input.trim()), "i").test(text)
		: true
}
 
// String -> String
function regExpEscape(s) {
	return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
}
 
// a -> b -> Number
function sortByLength(a, b) {
	if (a.length !== b.length) {
		return a.length - b.length;
	}
 
	return a < b? -1 : 1;
};
 
var keyboard = {
	submit: function submit(code, highlighted){
		return code == 13 && highlighted
			? [highlighted]
			: []
	}
 
	,dismiss: function dismiss(code){
		return code == 27
			? [true]
			: []
	}
 
	,navigate: function navigate(
		showingDrawer
		, highlighted
		, renderedList
		, code
	){
		var KEY_UP = 38
		var KEY_DOWN = 40
		var i = renderedList.indexOf(highlighted)
		var NO_MATCH = i == -1
		var LOWER_BOUND = 0
		var UPPER_BOUND = renderedList.length -1
		var MATCH = i >= -1
		var NEXT = i+1
		var PREV = i-1
 
		if( showingDrawer && (code == KEY_UP || code == KEY_DOWN) ){
			if( code == KEY_UP && (NO_MATCH || i == LOWER_BOUND) ){
				return [renderedList[UPPER_BOUND]]
			} else if(code == KEY_UP && MATCH) {
				return [renderedList[PREV]]
			} else if (
				code == KEY_DOWN && (
					NO_MATCH || i == UPPER_BOUND
				)
			) {
				return [renderedList[LOWER_BOUND]]
			} else { // ( code == KEY_DOWN && MATCH )
				return [renderedList[NEXT]]
			}
		} else {
			return []
		}
	}
}
 
function BaseAutocomplete(framework){
 
	var h = framework.hyperscript
	var get = framework.get
	var set = framework.set
 
	function Autocomplete(model, nullableOverrides){
		var overrides = nullableOverrides || {}
		var list = model.list
		var input = model.input
		var chosen = model.chosen
		var open = model.open
		var highlighted = model.highlighted
 
		var value = get(input)
 
		var minChars = typeof overrides.minChars != 'undefined'
			? overrides.minChars
			: 2
		var maxItems = typeof overrides.maxItems != 'undefined'
			? overrides.maxItems
			: 10
		var sort = typeof overrides.sort != 'undefined'
			? overrides.sort
			: sortByLength
		var filter = typeof overrides.filter != 'undefined'
			? overrides.filter
			: contains
 
		var filteredList =
			typeof overrides.filteredList != 'undefined'
				? overrides.filteredList
				: get( list )
					.filter(function (s){
						return filter(value, s)
					})
					.sort( sort )
					.slice(0, maxItems)
 
		if( filteredList.indexOf( get( highlighted ) ) == -1 ){
			set(highlighted, null)
		}
 
		var config = {
 
			filteredList: filteredList
			,showingDrawer:
				typeof overrides.showingDrawer != 'undefined'
					? overrides.showingDrawer
					: get(open)
						&& value.length >= minChars
						&& filteredList.length > 0
 
			,choose:
				typeof overrides.choose != 'undefined'
					? overrides.choose
					: function choose(x){
						set(chosen, x)
						set(input, x)
 
						config.close()
					}
			,clickItem:
				typeof overrides.clickItem != 'undefined'
					? overrides.clickItem
					: function clickItem(x){
						return config.choose(x)
 
					}
			,PATTERN_INPUT:
				typeof overrides.PATTERN_INPUT != 'undefined'
					? overrides.PATTERN_INPUT
					: value
						? new RegExp(value, 'gi')
						: null
			,mark:
				typeof overrides.mark != 'undefined'
					? overrides.mark
					: function(x){
						return h('mark', x)
					}
 
			,highlight:
				typeof overrides.highlight != 'undefined'
					? overrides.highlight
					: function highlight( x ){
 
				var matches =
					config.PATTERN_INPUT != null
					? x.match( config.PATTERN_INPUT )
					: null
 
				var processed =
					matches != null
					? matches
						.reduce(function(p, n){
							var i = p.buffer.indexOf(n)
 
							return {
								buffer: p.buffer.slice(i+n.length)
								,output: p.output.concat(
									i === 0
									? []
									: p.buffer.slice(0, i)
									,[ config.mark(
										p.buffer.slice(i, i+n.length)
									)
									]
								)
							}
						}, {
							buffer: x
							,output: []
						})
					: { output: x, buffer: '' }
 
				return processed.output.concat(
					processed.buffer || []
				)
			}
			,oninput:
				typeof overrides.oninput != 'undefined'
					? overrides.oninput
					: function oninput(e){
						var v = e.currentTarget.value
 
						set(input, v)
 
						if( !get(open) ){
							set(open, true)
						}
 
						if( get(chosen) != v ){
							set(chosen, null)
						}
					}
 
			,onfocus:
				typeof overrides.onfocus != 'undefined'
					? overrides.onfocus
					: function onfocus(){
						if( !get(open) ){
							set(open, true)
						}
					}
			,close:
				typeof overrides.close != 'undefined'
					? overrides.close
					: function close(){
						//eslint-disable-next-line fp/no-mutation
						if( get(open) ){
							set(open, false)
						}
					}
			,onblur:
				typeof overrides.onblur != 'undefined'
					? overrides.onblur
					: function onblur(){
						config.close()
					}
			,renderInput:
				typeof overrides.renderInput != 'undefined'
					? overrides.renderInput
					: function renderInput(){
						return h('input'
							,{ value: value
							, oninput: config.oninput
							, onfocus: config.onfocus
							, onblur: config.onblur
							}
						)
					}
 
			,itemClassNames:
				typeof overrides.itemClassNames != 'undefined'
					? overrides.itemClassNames
					: function itemClassNames(x){
						return 	x == get(highlighted)
							? 'highlight'
							: ''
					}
 
			,renderItem:
				typeof overrides.renderItem != 'undefined'
					? overrides.renderItem
					: function renderItem(x, config){
						return h(
							'li'
							, { className:
								config.itemClassNames(x, config)
							, onmousedown: function(e){
								config.clickItem(x)
								e.stopPropagation()
							}
							}
							, config.highlight(x)
						)
					}
			,renderItems:
				typeof overrides.renderItems != 'undefined'
					? overrides.renderItems
					: function renderItems(config){
						return h(
							'ul'
							, config.filteredList.map(
								function filteredList$map(x){
									return config.renderItem(x, config)
								}
							)
						)
					}
			,classNames:
				typeof overrides.classNames != 'undefined'
					? overrides.classNames
					: function classNames(){
						return ['manuel-complete']
							.concat(
								config.showingDrawer ? ['open'] : []
								,value.length > 0 ? ['not-empty'] : []
								,get(list).length > 0 ? ['loaded'] : []
							)
							.join(' ')
					}
			,renderRoot:
				typeof overrides.renderRoot != 'undefined'
					? overrides.renderRoot
					: function renderRoot(config){
						return h('div'
							,{ className: config.classNames()
							, onkeydown: config.onkeydown
							}
							,config.renderInput(config)
							,config.renderItems(config)
						)
					}
			,keyboardSubmit:
				typeof overrides.keyboardSubmit != 'undefined'
					? overrides.keyboardSubmit
					: keyboard.submit
			,keyboardDismiss:
				typeof overrides.keyboardDismiss != 'undefined'
					? overrides.keyboardDismiss
					: keyboard.dismiss
			,keyboardNavigate:
				typeof overrides.keyboardNavigate != 'undefined'
					? overrides.keyboardNavigate
					: keyboard.navigate
			,onkeydown:
				typeof overrides.onkeydown != 'undefined'
					? overrides.onkeydown
					: function onkeydown(e){
							var new_chosen =
								config.keyboardSubmit(
									e.keyCode
									, get(highlighted)
								)
 
							var dismiss = config.keyboardDismiss(
								e.keyCode
							)
 
							var new_highlighted =
								config.keyboardNavigate(
									config.showingDrawer
									, get(highlighted)
									, config.filteredList
									, e.keyCode
								)
 
							new_chosen.map(
								config.choose
							)
 
							new_highlighted.map(
								function new_highlighted$map(v){
									return set(highlighted, v)
								}
							)
 
							dismiss.map(
								config.close
							)
 
							;[]
								.concat(
									new_chosen
									,dismiss
									,new_highlighted
								)
								.slice(0,1)
								.map(
									function e$preventDefault(){
										e.preventDefault()
										return null;
									}
								)
						}
		}
 
		return config.renderRoot(config)
	}
 
	return Autocomplete
}
 
module.exports = BaseAutocomplete