1import { loadjs } from './loadjs.js'; 2import { i18n, MultiLang } from './js/i18n/ws-i18n.js'; 3 4MultiLang.importFile('/static/lang/ws-company-completion.json').subscribe(); 5 6try { 7 loadjs([ 8 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-3-typeahead/4.0.2/bootstrap3-typeahead.min.js' 9 ], 'typeahead'); 10} catch(e) {} 11 12export class WsCompanyCompletion { 13 constructor(element, { error = null, autoSelect = false } = {}, filter = []) { 14 this.observers = []; 15 this._element = $(element); 16 this._autoSelect = autoSelect; 17 this._error = error; 18 this._pristine = true; 19 this._first_focus = true; 20 this._toFilter = filter; 21 22 this._element.attr('autocomplete', 'off'); 23 this._element.attr('id', ''); 24 this._element.attr('name', ''); 25 this._element.attr('type', 'search'); 26 27 this._element.on('input', () => { 28 this._pristine = false; 29 30 if (this._validator != null) { 31 this._validator.validate(this._element.val(), this._error); 32 } 33 }); 34 35 this._element.on('blur', () => { 36 this._first_focus = false; 37 38 setTimeout(() => { 39 if (this._validator != null) { 40 this._validator.validate(this._element.val(), this._error); 41 } 42 }, 3); 43 }); 44 } 45 46 get start() { 47 const WsCompanyCompletionRef = this; 48 49 return Rx.Observable.create(observer => { 50 if (this.observers.length === 0) { 51 this._element.on('blur', () => { 52 this._timer = setTimeout(() => { 53 if ((this.value || {}).name !== this._element.val()) { 54 this.value = { id: null, name: this._element.val() }; 55 } 56 57 this.observers.forEach(o => o.next(this.value)); 58 this._timer = null; 59 }, 1); 60 }); 61 62 this._element.keydown(e => { 63 this._timer = setTimeout(() => { 64 if (e.which == 9 && this._element.val() != '') { 65 if ((this.value || {}).name !== this._element.val()) { 66 this.value = { id: null, name: this._element.val() }; 67 } 68 69 this.observers.forEach(o => o.next(this.value)); 70 this._timer = null; 71 } 72 }, 1); 73 }); 74 75 this._element.keyup(e => { 76 this._timer = setTimeout(() => { 77 if (e.which == 13 && this._element.val() != '') { 78 if ((this.value || {}).name !== this._element.val()) { 79 this.value = { id: null, name: this._element.val() }; 80 } 81 82 this.observers.forEach(o => o.next(this.value)); 83 this._timer = null; 84 } 85 }, 1); 86 }); 87 88 loadjs.ready('typeahead', () => { 89 $.fn.typeahead.Constructor.prototype.show = function () { 90 var pos = $.extend({}, this.$element.position(), { 91 height: this.$element[0].offsetHeight 92 }) 93 this.$menu 94 .insertAfter(this.$element) 95 .css({ 96 top: (pos.top + pos.height), 97 left: pos.left 98 }) 99 .show(); 100 101 this.shown = true; 102 return this; 103 }; 104 105 this._element.typeahead({ 106 autoSelect: this._autoSelect, 107 menu: '<ul class="typeahead dropdown-menu" role="listbox" style="z-index: 1050 !important; overflow: hidden"></ul>', 108 item: '<li style="margin-left: 0 !important"><a class="dropdown-item" href="#" role="option"></a></li>', 109 source: (query, result) => { 110 restCall('GET', '/company/api/suggest/'
110+ query).subscribe(r => result(r.suggestions)); 111 }, 112 render: function(items) { 113 var that = this; 114 var self = this; 115 var activeFound = false; 116 var data = []; 117 var _category = that.options.separator; 118 119 items.forEach((company) => { 120 function checkIfCompanyAlreadyExists(companyToRemove) { 121 return company.id == companyToRemove.id 122 }; 123 const hasCompany = WsCompanyCompletionRef._toFilter.filter(checkIfCompanyAlreadyExists); 124 125 if (hasCompany.length === 0) data.push(company); 126 }) 127 128 items = $(data).map(function (i, item) { 129 var text = self.displayText(item); 130 i = $(that.options.item).data('value', item); 131 i.find('a').html($(` 132 <div> 133 <span style="border-radius: 100%; display: inline-table; overflow: hidden; margin-right: 5px;"> 134 <img src="${item.photo}" style="width: 30px; height: 30px;"> 135 </span> 136 </div> 137 `).append(that.highlighter(text, item))); 138 i.addClass('notranslate'); 139 if (text == self.$element.val()) { 140 i.addClass('active'); 141 self.$element.data('active', item); 142 activeFound = true; 143 } 144 return i[0]; 145 }); 146 if (this.autoSelect && !activeFound) { 147 items.filter(':not(.dropdown-header)').first().addClass('active'); 148 this.$element.data('active', items.first().data('value')); 149 } 150 this.$menu.html(items); 151 return this; 152 }, 153 matcher: (item) => true, 154 afterSelect: item => { 155 if (this._timer != null) { 156 clearTimeout(this._timer); 157 this._timer = null; 158 } 159 160 this.observers.forEach(o => o.next(item)); 161 this.value = item; 162 return item; 163 } 164 }); 165 }); 166 } 167 168 this.observers.push(observer); 169 return () => { 170 this.observers = this.observers.filter(o => o !== observer); 171 if (this.observers.length === 0) { 172 this._element.typeahead('destroy'); 173 } 174 } 175 }); 176 } 177 178 set value(v) { 179 this._value = v; 180 this._element.val((this._value || {}).name || ''); 181 } 182 183 get value() { 184 return this._value || null; 185 } 186 187 clear() { 188 this._value = null; 189 this._element.val(''); 190 } 191 192 set visible(v) { 193 if (v) { 194 this._element.show(); 195 } else { 196 this._element.hide(); 197 } 198 } 199 200 validate(type, { exists = false } = {}) { 201 if (this._validator != null) 202 return this._validator; 203 204 let self = this; 205 this._validator = new function() { 206 const msgEmpty = i18n('ws-company-completion-empty-validator', type); 207 const msgExists = i18n('ws-company-completion-exists-validator', type); 208 209 this.destroy = () => {}; 210 this.trigger = () => { 211 self._pristine = false;
212 self._first_focus = false; 213 return this.validate(self._element.val(), self._error); 214 }; 215 216 this.type = type; 217 this.isVisible = () => self._element.is(':visible'); 218 219 this.validate = (val, errorContainer) => { 220 if (self._element.is(':disabled') || !self._element.is(':visible')) 221 return true; 222 223 if (self._pristine || self._first_focus) { 224 return false; 225 } 226 227 if (val.length === 0 && errorContainer != null) { 228 errorContainer.text(msgEmpty); 229 errorContainer.css('display', 'block'); 230 231 return false; 232 } 233 234 if (exists === true && self.value.id == null) { 235 errorContainer.text(msgExists); 236 errorContainer.css('display', 'block'); 237 238 return false; 239 } 240 241 errorContainer.text(''); 242 errorContainer.css('display', 'none'); 243 244 return true; 245 }; 246 } 247 248 return this._validator; 249 } 250}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.