vendor: 157,934 bytes, lines 1-5235
1/** 2 * DaData.ru Suggestions jQuery plugin, version 22.6.0 3 * 4 * DaData.ru Suggestions jQuery plugin is freely distributable under the terms of MIT-style license 5 * Built on DevBridge Autocomplete for jQuery (https://github.com/devbridge/jQuery-Autocomplete) 6 * For details, see https://github.com/hflabs/suggestions-jquery 7 */ 8(function (global, factory) { 9 typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) : 10 typeof define === 'function' && define.amd ? define(['jquery'], factory) : 11 (factory(global.jQuery)); 12}(this, (function ($) { 'use strict'; 13 14$ = $ && $.hasOwnProperty('default') ? $['default'] : $; 15 16/** 17 * УÑилиÑÑ Ð´Ð»Ñ ÑабоÑÑ Ñ Ñипами. 18 */ 19var lang_util = { 20 /** 21 * ÐÑовеÑÑеÑ, ÑвлÑеÑÑÑ Ð»Ð¸ аÑгÑÐ¼ÐµÐ½Ñ Ð¼Ð°ÑÑивом. 22 */ 23 isArray: function(array) { 24 return Array.isArray(array); 25 }, 26 27 /** 28 * ÐÑовеÑÑеÑ, ÑвлÑеÑÑÑ Ð»Ð¸ аÑгÑÐ¼ÐµÐ½Ñ ÑÑнкÑией. 29 */ 30 isFunction: function(it) { 31 return Object.prototype.toString.call(it) === "[object Function]"; 32 }, 33 34 /** 35 * ÐÑовеÑÑеÑ, ÑвлÑеÑÑÑ Ð»Ð¸ аÑгÑÐ¼ÐµÐ½Ñ Ð¿ÑÑÑÑм обÑекÑом ({}). 36 */ 37 isEmptyObject: function(obj) { 38 return Object.keys(obj).length === 0 && obj.constructor === Object; 39 }, 40 41 /** 42 * ÐÑовеÑÑеÑ, ÑвлÑеÑÑÑ Ð»Ð¸ аÑгÑÐ¼ÐµÐ½Ñ Â«Ð¾Ð±ÑÑнÑм» обÑекÑом 43 * (не undefiend, не null, не DOM-ÑлеменÑ) 44 */ 45 isPlainObject: function(obj) { 46 if ( 47 obj === undefined || 48 typeof obj !== "object" || 49 obj === null || 50 obj.nodeType || 51 obj === obj.window 52 ) { 53 return false; 54 } 55 if ( 56 obj.constructor && 57 !Object.prototype.hasOwnProperty.call( 58 obj.constructor.prototype, 59 "isPrototypeOf" 60 ) 61 ) { 62 return false; 63 } 64 return true; 65 } 66}; 67 68/** 69 * УÑилиÑÑ Ð´Ð»Ñ ÑабоÑÑ Ñ ÐºÐ¾Ð»Ð»ÐµÐºÑиÑми. 70 */ 71var collection_util = { 72 /** 73 * ÐозвÑаÑÐ°ÐµÑ Ð¼Ð°ÑÑив без пÑÑÑÑÑ ÑлеменÑов 74 */ 75 compact: function(array) { 76 return array.filter(function(el) { 77 return !!el; 78 }); 79 }, 80 81 /** 82 * ÐÑеÑиÑÑÐµÑ Ð¿Ð¾ ÑлеменÑам маÑÑива или полÑм обÑекÑа. 83 * ÐедÑÑ ÑÐµÐ±Ñ ÐºÐ°Ðº $.each() - пÑеÑÑÐ²Ð°ÐµÑ Ð²Ñполнение, еÑли ÑÑнкÑиÑ-обÑабоÑÑик возвÑаÑÐ°ÐµÑ false. 84 * @param {Object|Array} obj - маÑÑив или обÑÐµÐºÑ 85 * @param {eachCallback} callback - ÑÑнкÑиÑ-обÑабоÑÑик 86 */ 87 each: function(obj, callback) { 88 if (Array.isArray(obj)) { 89 obj.some(function(el, idx) { 90 return callback(el, idx) === false; 91 }); 92 return; 93 } 94 Object.keys(obj).some(function(key) { 95 var value = obj[key]; 96 return callback(value, key) === false; 97 }); 98 }, 99 100 /** 101 * ÐеÑеÑеÑение маÑÑивов: ([1,2,3,4], [2,4,5,6]) => [2,4] 102 * ÐÑÑ Ð¾Ð´Ð½Ñе маÑÑÐ¸Ð²Ñ Ð½Ðµ менÑÑÑÑÑ. 103 */ 104 intersect: function(array1, array2) { 105 var result = []; 106 if (!Array.isArray(array1) || !Array.isArray(array2)) { 107 return result; 108 } 109 return array1.filter(function(el) { 110 return array2.indexOf(el) !== -1; 111 }); 112 }, 113 114 /** 115 * РазноÑÑÑ Ð¼Ð°ÑÑивов: ([1,2,3,4], [2,4,5,6]) => [1,3] 116 * ÐÑÑ Ð¾Ð´Ð½Ñе маÑÑÐ¸Ð²Ñ Ð½Ðµ менÑÑÑÑÑ. 117 */ 118 minus: function(array1, array2) { 119 if (!array2 || array2.length === 0) { 120 return array1; 121 } 122 return array1.filter(function(el) { 123 return array2.indexOf(el) === -1; 124 }); 125 }, 126 127 /** 128 * ÐбÑаÑÐ¸Ð²Ð°ÐµÑ Ð¿ÐµÑеданнÑй обÑÐµÐºÑ Ð² маÑÑив. 129 * ÐÑли пеÑедан маÑÑив, возвÑаÑÐ°ÐµÑ ÐµÐ³Ð¾ копиÑ. 130 */ 131 makeArray: function(arrayLike) { 132 if (lang_util.isArray(arrayLike)) { 133 return Array.prototype.slice.call(arrayLike); 134 } else { 135 return [arrayLike]; 136 } 137 }, 138 139 /** 140 * РазноÑÑÑ Ð¼Ð°ÑÑивов Ñ ÑаÑÑиÑнÑм Ñовпадением ÑлеменÑов. 141 * ÐÑли ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð²ÑоÑого маÑÑива вклÑÑÐ°ÐµÑ Ð² ÑÐµÐ±Ñ ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¿ÐµÑвого, 142 * ÑлеменÑÑ ÑÑиÑаÑÑÑÑ ÑавнÑми. 143 */ 144 minusWithPartialMatching: function(array1, array2) { 145 if (!array2 || array2.length === 0) { 146 return array1; 147 } 148 return array1.filter(function(el) { 149 return !array2.some(function(el2) { 150 return el2.indexOf(el) === 0; 151 }); 152 }); 153 }, 154 155 /** 156 * ÐопиÑÑÐµÑ Ð¼Ð°ÑÑив, наÑÐ¸Ð½Ð°Ñ Ñ Ñказанного ÑлеменÑа. 157 * @param obj - маÑÑив 158 * @param start - индекÑ, наÑÐ¸Ð½Ð°Ñ Ñ ÐºÐ¾ÑоÑого надо ÑкопиÑоваÑÑ 159 */ 160 slice: function(obj, start) { 161 return Array.prototype.slice.call(obj, start); 162 } 163}; 164 165/** 166 * УÑилиÑÑ Ð´Ð»Ñ ÑабоÑÑ Ñ ÑÑнкÑиÑми. 167 */ 168var func_util = { 169 /** 170 * ÐÑполнÑÐµÑ ÑÑнкÑÐ¸Ñ Ñ Ñказанной задеÑжкой. 171 * @param {Function} handler - ÑÑнкÑÐ¸Ñ 172 * @param {number} delay - задеÑжка в миллиÑекÑÐ½Ð´Ð°Ñ 173 */ 174 delay: function(handler, delay) { 175 return setTimeout(handler, delay || 0); 176 } 177}; 178 179/** 180 * УÑилиÑÑ Ð´Ð»Ñ ÑабоÑÑ Ñ Ð¾Ð±ÑекÑами. 181 */ 182var object_util = { 183 /** 184 * СÑÐ°Ð²Ð½Ð¸Ð²Ð°ÐµÑ Ð´Ð²Ð° обÑекÑа по полÑм, коÑоÑÑе пÑиÑÑÑÑÑвÑÑÑ Ð² Ð¾Ð±Ð¾Ð¸Ñ 185 * @returns {boolean} true, еÑли Ð¿Ð¾Ð»Ñ ÑовпадаÑÑ, false в пÑоÑивном ÑлÑÑае 186 */ 187 areSame: function self(a, b) { 188 var same = true; 189 190 if (typeof a != typeof b) { 191 return false; 192 } 193 194 if (typeof a == "object" && a != null && b != null) { 195 collection_util.each(a, function(value, i) { 196 return (same = self(value, b[i])); 197 }); 198 return same; 199 } 200 201 return a === b; 202 }, 203 204 /** 205 * ÐопиÑÑÐµÑ ÑвойÑÑва и Ð¸Ñ Ð·Ð½Ð°ÑÐµÐ½Ð¸Ñ Ð¸Ð· иÑÑ Ð¾Ð´Ð½ÑÑ Ð¾Ð±ÑекÑов в Ñелевой 206 */ 207 assign: function(target, varArgs) { 208 if (typeof Object.assign === "function") { 209 return Object.assign.apply(null, arguments); 210 } 211 if (target == null) { 212 // TypeError if undefined or null 213 throw new TypeError("Cannot convert undefined or null to object"); 214 } 215 216 var to = Object(target); 217 218 for (var index = 1; index < arguments.length; index++) { 219 var nextSource = arguments[index]; 220 221 if (nextSource != null) { 222 // Skip over if undefined or null 223 for (var nextKey in nextSource) { 224 // Avoid bugs when hasOwnProperty is shadowed 225 if ( 226 Object.prototype.hasOwnProperty.call( 227 nextSource, 228 nextKey 229 ) 230 ) { 231 to[nextKey] = nextSource[nextKey]; 232 } 233 } 234 } 235 } 236 return to; 237 }, 238 239 /** 240 * ÐлониÑÑÐµÑ Ð¾Ð±ÑÐµÐºÑ Ð³Ð»Ñбоким копиÑованием 241 */ 242 clone: function(obj) { 243 return JSON.parse(JSON.stringify(obj)); 244 }, 245 246 /** 247 * ÐозвÑаÑÐ°ÐµÑ ÐºÐ¾Ð¿Ð¸Ñ Ð¾Ð±ÑекÑа без пÑÑÑÑÑ Ð¿Ð¾Ð»ÐµÐ¹ 248 * (без undefined, null и '') 249 * @param obj 250 */ 251 compact: function(obj) { 252 var copy = object_util.clone(obj); 253 254 collection_util.each(copy, function(val, key) { 255 if (val === null || val === undefined || val === "") { 256 delete copy[key]; 257 } 258 }); 259 260 return copy; 261 }, 262 263 /** 264 * ÐÑовеÑÑеÑ, ÑÑо ÑказаннÑе Ð¿Ð¾Ð»Ñ Ð² обÑекÑе заполненÑ. 265 * @param {Object} obj - пÑовеÑÑемÑй обÑÐµÐºÑ 266 * @param {Array} fields - ÑпиÑок названий полей, коÑоÑÑе надо пÑовеÑиÑÑ 267 * @returns {boolean} 268 */ 269 fieldsAreNotEmpty: function(obj, fields) { 270 if (!lang_util.isPlainObject(obj)) { 271 return false; 272 } 273 var result = true; 274 collection_util.each(fields, function(field, i) { 275 result = !!obj[field]; 276 return result; 277 }); 278 return result; 279 }, 280 281 /** 282 * ÐозвÑаÑÐ°ÐµÑ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð¾Ðµ знаÑение по ÑÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð¼Ñ Ð¿ÑÑи 283 * напÑимеÑ, 'data.address.value' 284 */ 285 getDeepValue: function self(obj, name) { 286 var path = name.split("."), 287 step = path.shift(); 288 289 return ( 290 obj && (path.length ? self(obj[step], path.join(".")) : obj[step]) 291 ); 292 }, 293 294 /** 295 * ÐозвÑаÑÐ°ÐµÑ ÐºÐ°ÑÑÑ Ð¾Ð±ÑекÑов по Ð¸Ñ Ð¸Ð´ÐµÐ½ÑиÑикаÑоÑам. 296 * ÐÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ Ð½Ð° Ð²Ñ Ð¾Ð´ маÑÑив обÑекÑов и иденÑиÑиÑиÑÑÑÑее поле. 297 * ÐозвÑаÑÐ°ÐµÑ ÐºÐ°ÑÑÑ, клÑÑом в коÑоÑой ÑвлÑеÑÑÑ Ð·Ð½Ð°Ñение иденÑиÑиÑиÑÑÑÑего полÑ, 298 * а знаÑением â иÑÑ Ð¾Ð´Ð½Ñй обÑекÑ. 299 * Ðаодно добавлÑÐµÑ Ð¾Ð±ÑекÑам поле Ñ Ð¿Ð¾ÑÑдковÑм номеÑом. 300 * @param {Array} objectsArray - маÑÑив обÑекÑов 301 * @param {string} idField - название иденÑиÑиÑиÑÑÑÑего Ð¿Ð¾Ð»Ñ 302 * @param {string} indexField - название Ð¿Ð¾Ð»Ñ Ñ Ð¿Ð¾ÑÑдковÑм номеÑом 303 * @return {Object} каÑÑа обÑекÑов по Ð¸Ñ Ð¸Ð´ÐµÐ½ÑиÑикаÑоÑам 304 */ 305 indexObjectsById: function(objectsArray, idField, indexField) { 306 var result = {}; 307 308 collection_util.each(objectsArray, function(obj, idx) { 309 var key = obj[idField]; 310 var val = {}; 311 312 if (indexField) { 313 val[indexField] = idx; 314 } 315 316 result[key] = object_util.assign(val, obj); 317 }); 318 319 return result; 320 } 321}; 322 323var KEYS = { 324 ENTER: 13, 325 ESC: 27, 326 TAB: 9, 327 SPACE: 32, 328 UP: 38, 329 DOWN: 40 330}; 331 332var CLASSES = { 333 hint: "suggestions-hint", 334 mobile: "suggestions-mobile", 335 nowrap: "suggestions-nowrap", 336 promo: "suggestions-promo", 337 selected: "suggestions-selected", 338 suggestion: "suggestions-suggestion", 339 subtext: "suggestions-subtext", 340 subtext_inline: "suggestions-subtext suggestions-subtext_inline", 341 subtext_delimiter: "suggestions-subtext-delimiter", 342 subtext_label: "suggestions-subtext suggestions-subtext_label", 343 removeConstraint: "suggestions-remove", 344 value: "suggestions-value" 345}; 346 347var EVENT_NS = ".suggestions"; 348var DATA_ATTR_KEY = "suggestions"; 349var WORD_DELIMITERS = "\\s\"'~\\*\\.,:\\|\\[\\]\\(\\)\\{\\}<>â"; 350var WORD_PARTS_DELIMITERS = "\\-\\+\\\\\\?!@#$%^&"; 351 352/** 353 * УÑилиÑÑ Ð´Ð»Ñ ÑабоÑÑ Ñ ÑекÑÑом. 354 */ 355 356var WORD_SPLITTER = new RegExp("[" + WORD_DELIMITERS + "]+", "g"); 357var WORD_PARTS_SPLITTER = new RegExp("[" + WORD_PARTS_DELIMITERS + "]+", "g"); 358 359var text_util = { 360 /** 361 * ÐаменÑÐµÑ Ð°Ð¼Ð¿ÐµÑÑанд, ÑгловÑе Ñкобки и дÑÑгие подобнÑе ÑÐ¸Ð¼Ð²Ð¾Ð»Ñ 362 * на HTML-ÐºÐ¾Ð´Ñ 363 */ 364 escapeHtml: function(str) { 365 var map = { 366 "&": "&", 367 "<": "<", 368 ">": ">", 369 '"': """, 370 "'": "'", 371 "/": "/" 372 }; 373 374 if (str) { 375 collection_util.each(map, function(html, ch) { 376 str = str.replace(new RegExp(ch, "g"), html); 377 }); 378 } 379 return str; 380 }, 381 382 /** 383 * ÐÑкейпиÑÑÐµÑ ÑÐ¸Ð¼Ð²Ð¾Ð»Ñ RegExp-Ñаблона обÑаÑнÑм ÑлеÑем 384 * (Ð´Ð»Ñ Ð¿ÐµÑедаÑи в конÑÑÑÑкÑÐ¾Ñ ÑегÑлÑÑнÑÑ Ð²ÑÑажений) 385 */ 386 escapeRegExChars: function(value) { 387 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 388 }, 389 390 /** 391 * ÐÑÐ¸Ð²Ð¾Ð´Ð¸Ñ Ñлово к Ð½Ð¸Ð¶Ð½ÐµÐ¼Ñ ÑегиÑÑÑÑ Ð¸ заменÑÐµÑ Ñ â е 392 */ 393 formatToken: function(token) { 394 return token && token.toLowerCase().replace(/[ÑÐ]/g, "е"); 395 }, 396 397 /** 398 * ÐозвÑаÑÐ°ÐµÑ ÑегÑлÑÑное вÑÑажение Ð´Ð»Ñ Ñазбивки ÑÑÑоки на Ñлова 399 */ 400 getWordExtractorRegExp: function() { 401 return new RegExp( 402 "([^" + WORD_DELIMITERS + "]*)([" + WORD_DELIMITERS + "]*)", 403 "g" 404 ); 405 }, 406 407 /** 408 * ÐÑÑÐµÐ·Ð°ÐµÑ Ð¸Ð· ÑÑÑоки ÑÑоп-Ñлова 409 */ 410 normalize: function(str, stopwords) { 411 return text_util.split(str, stopwords).join(" "); 412 }, 413 414 /** 415 * ÐÐ¾Ð±Ð¸Ð²Ð°ÐµÑ ÑÑÑÐ¾ÐºÑ ÑказаннÑм Ñимволов ÑпÑава до Ñказанной Ð´Ð»Ð¸Ð½Ñ 416 * @param sourceString иÑÑ Ð¾Ð´Ð½Ð°Ñ ÑÑÑока 417 * @param targetLength до какой Ð´Ð»Ð¸Ð½Ñ Ð´Ð¾Ð±Ð¸Ð²Ð°ÑÑ 418 * @param padString каким Ñимволом добиваÑÑ 419 * @returns ÑÑÑока Ñказанной Ð´Ð»Ð¸Ð½Ñ 420 */ 421 padEnd: function(sourceString, targetLength, padString) { 422 if (String.prototype.padEnd) { 423 return sourceString.padEnd(targetLength, padString); 424 } 425 targetLength = targetLength >> 0; //floor if number or convert non-number to 0; 426 padString = String(typeof padString !== "undefined" ? padString : " "); 427 if (sourceString.length > targetLength) { 428 return String(sourceString); 429 } else { 430 targetLength = targetLength - sourceString.length; 431 if (targetLength > padString.length) { 432 padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed 433 } 434 return String(sourceString) + padString.slice(0, targetLength); 435 } 436 }, 437 438 /** 439 * ÐоÑмализÑÐµÑ ÑÑÑокÑ, ÑÐ°Ð·Ð±Ð¸Ð²Ð°ÐµÑ Ð½Ð° Ñлова, 440 * оÑÑÐµÐ¸Ð²Ð°ÐµÑ ÑÑоп-Ñлова из ÑпиÑка. 441 * РаÑÐºÐ»ÐµÐ¸Ð²Ð°ÐµÑ Ð±ÑÐºÐ²Ñ Ð¸ ÑиÑÑÑ, напиÑаннÑе ÑлиÑно. 442 */ 443 split: function(str, stopwords) { 444 var cleanStr = str 445 .toLowerCase() 446 .replace("Ñ", "е") 447 .replace(/(\d+)([а-Ñ]{2,})/g, "$1 $2") 448 .replace(/([а-Ñ]+)(\d+)/g, "$1 $2"); 449 450 var words = collection_util.compact(cleanStr.split(WORD_SPLITTER)); 451 if (!words.length) { 452 return []; 453 } 454 var lastWord = words.pop(); 455 var goodWords = collection_util.minus(words, stopwords); 456 goodWords.push(lastWord); 457 return goodWords; 458 }, 459 460 /** 461 * ÐаменÑÐµÑ Ñлова на ÑоÑÑавнÑе ÑаÑÑи. 462 * РоÑлиÑие Ð¾Ñ withSubTokens, не ÑÐ¾Ñ ÑанÑÐµÑ Ð¸ÑÑ Ð¾Ð´Ð½Ñе Ñлова. 463 */ 464 splitTokens: function(tokens) { 465 var result = []; 466 collection_util.each(tokens, function(token, i) { 467 var subtokens = token.split(WORD_PARTS_SPLITTER); 468 result = result.concat(collection_util.compact(subtokens)); 469 }); 470 return result; 471 }, 472 473 /** 474 * ÐÑовеÑÑеÑ, вклÑÑÐ°ÐµÑ Ð»Ð¸ ÑÑÑока 1 ÑÑÑÐ¾ÐºÑ 2. 475 * ÐÑли ÑÑÑоки ÑавнÑ, возвÑаÑÐ°ÐµÑ false. 476 */ 477 stringEncloses: function(str1, str2) { 478 return ( 479 str1.length > str2.length && 480 str1.toLowerCase().indexOf(str2.toLowerCase()) !== -1 481 ); 482 }, 483 484 /** 485 * ÐозвÑаÑÐ°ÐµÑ ÑпиÑок Ñлов из ÑÑÑоки. 486 * ÐÑи ÑÑом пеÑвÑми по поÑÑÐ´ÐºÑ Ð¸Ð´ÑÑ Â«Ð¿ÑедпоÑÑиÑелÑнÑе» Ñлова 487 * (Ñе, ÑÑо не Ð²Ñ Ð¾Ð´ÑÑ Ð² ÑпиÑок «нежелаÑелÑнÑÑ Â»). 488 * СоÑÑавнÑе Ñлова Ñоже ÑÐ°Ð·Ð±Ð¸Ð²Ð°ÐµÑ Ð½Ð° ÑаÑÑи. 489 * @param {string} value - ÑÑÑока 490 * @param {Array} unformattableTokens - «нежелаÑелÑнÑе» Ñлова 491 * @return {Array} ÐаÑÑив аÑомаÑнÑÑ Ñлов 492 */ 493 tokenize: function(value, unformattableTokens) { 494 var tokens = collection_util.compact( 495 text_util.formatToken(value).split(WORD_SPLITTER) 496 ); 497 // Move unformattableTokens to the end. 498 // This will help to apply them only if no other tokens match 499 var preferredTokens = collection_util.minus( 500 tokens, 501 unformattableTokens 502 ); 503 var otherTokens = collection_util.minus(tokens, preferredTokens); 504 tokens = text_util.withSubTokens(preferredTokens.concat(otherTokens)); 505 return tokens; 506 }, 507 508 /** 509 * Ð Ð°Ð·Ð±Ð¸Ð²Ð°ÐµÑ ÑоÑÑавнÑе Ñлова на ÑаÑÑи 510 * и допиÑÑÐ²Ð°ÐµÑ Ð¸Ñ Ðº иÑÑ Ð¾Ð´Ð½Ð¾Ð¼Ñ Ð¼Ð°ÑÑивÑ. 511 * @param {Array} tokens - Ñлова 512 * @return {Array} ÐаÑÑив аÑомаÑнÑÑ Ñлов 513 */ 514 withSubTokens: function(tokens) { 515 var result = []; 516 collection_util.each(tokens, function(token, i) { 517 var subtokens = token.split(WORD_PARTS_SPLITTER); 518 result.push(token); 519 if (subtokens.length > 1) { 520 result = result.concat(collection_util.compact(subtokens)); 521 } 522 }); 523 return result; 524 } 525}; 526 527/** 528 * jQuery API. 529 */ 530var jqapi = { 531 Deferred: function() { 532 return $.Deferred(); 533 }, 534 535 ajax: function(settings) { 536 return $.ajax(settings); 537 }, 538 539 extend: function() { 540 return $.extend.apply(null, arguments); 541 }, 542 543 isJqObject: function(obj) { 544 return obj instanceof $; 545 }, 546 547 param: function(obj) { 548 return $.param(obj); 549 }, 550 551 proxy: function(func, context) { 552 return $.proxy(func, context); 553 }, 554 555 select: function(selector) { 556 return $(selector); 557 }, 558 559 supportsCors: function() { 560 return $.support.cors; 561 } 562}; 563 564/** 565 * УÑилиÑÑ Ð´Ð»Ñ ÑабоÑÑ ÑеÑез AJAX 566 */ 567var ajax = { 568 /** 569 * HTTP-меÑод, коÑоÑÑй поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ Ð±ÑаÑÐ·ÐµÑ 570 */ 571 getDefaultType: function() { 572 return jqapi.supportsCors() ? "POST" : "GET"; 573 }, 574 575 /** 576 * Content-type, коÑоÑÑй поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ Ð±ÑаÑÐ·ÐµÑ 577 */ 578 getDefaultContentType: function() { 579 return jqapi.supportsCors() 580 ? "application/json" 581 : "application/x-www-form-urlencoded"; 582 }, 583 584 /** 585 * ÐенÑÐµÑ HTTPS на пÑоÑокол ÑÑÑаниÑÑ, еÑли бÑаÑÐ·ÐµÑ Ð½Ðµ поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ CORS 586 */ 587 fixURLProtocol: function(url) { 588 return jqapi.supportsCors() 589 ? url 590 : url.replace(/^https?:/, location.protocol); 591 }, 592 593 /** 594 * ÐапиÑÑÐ²Ð°ÐµÑ Ð¿Ð°ÑамеÑÑÑ Ð² GET-ÑÑÑÐ¾ÐºÑ 595 */ 596 addUrlParams: function(url, params) { 597 return url + (/\?/.test(url) ? "&" : "?") + jqapi.param(params); 598 }, 599 600 /** 601 * СеÑиализÑÐµÑ Ð¾Ð±ÑÐµÐºÑ Ð´Ð»Ñ Ð¿ÐµÑедаÑи по ÑеÑи. 602 * Ðибо в JSON-ÑÑÑÐ¾ÐºÑ (еÑли бÑаÑÐ·ÐµÑ Ð¿Ð¾Ð´Ð´ÐµÑÐ¶Ð¸Ð²Ð°ÐµÑ CORS), 603 * либо в GET-ÑÑÑокÑ. 604 */ 605 serialize: function(data) { 606 if (jqapi.supportsCors()) { 607 return JSON.stringify(data, function(key, value) { 608 return value === null ? undefined : value; 609 }); 610 } else { 611 data = object_util.compact(data); 612 return jqapi.param(data, true); 613 } 614 } 615}; 616 617/** 618 * ÐозвÑаÑÐ°ÐµÑ Ð°Ð²ÑоинкÑеменÑнÑй иденÑиÑикаÑоÑ. 619 * @param {string} prefix - пÑеÑÐ¸ÐºÑ Ð´Ð»Ñ Ð¸Ð´ÐµÐ½ÑиÑикаÑоÑа 620 */ 621var generateId = (function() { 622 var counter = 0; 623 return function(prefix) { 624 return (prefix || "") + ++counter; 625 }; 626})(); 627 628/** 629 * УÑилиÑÑ Ð½Ð° вÑе ÑлÑÑаи жизни. 630 */ 631var utils = { 632 escapeRegExChars: text_util.escapeRegExChars, 633 escapeHtml: text_util.escapeHtml, 634 formatToken: text_util.formatToken, 635 normalize: text_util.normalize, 636 reWordExtractor: text_util.getWordExtractorRegExp, 637 stringEncloses: text_util.stringEncloses, 638 639 addUrlParams: ajax.addUrlParams, 640 getDefaultContentType: ajax.getDefaultContentType, 641 getDefaultType: ajax.getDefaultType, 642 fixURLProtocol: ajax.fixURLProtocol, 643 serialize: ajax.serialize, 644 645 arrayMinus: collection_util.minus, 646 arrayMinusWithPartialMatching: collection_util.minusWithPartialMatching, 647 arraysIntersection: collection_util.intersect, 648 compact: collection_util.compact, 649 each: collection_util.each, 650 makeArray: collection_util.makeArray, 651 slice: collection_util.slice, 652 653 delay: func_util.delay, 654 655 areSame: object_util.areSame, 656 compactObject: object_util.compact, 657 getDeepValue: object_util.getDeepValue, 658 fieldsNotEmpty: object_util.fieldsAreNotEmpty, 659 indexBy: object_util.indexObjectsById, 660 661 isArray: lang_util.isArray, 662 isEmptyObject: lang_util.isEmptyObject, 663 isFunction: lang_util.isFunction, 664 isPlainObject: lang_util.isPlainObject, 665 666 uniqueId: generateId 667}; 668 669var DEFAULT_OPTIONS = { 670 $helpers: null, 671 autoSelectFirst: false, 672 containerClass: "suggestions-suggestions", 673 count: 5, 674 deferRequestBy: 100, 675 enrichmentEnabled: true, 676 formatResult: null, 677 formatSelected: null, 678 headers: null, 679 hint: "ÐÑбеÑиÑе ваÑÐ¸Ð°Ð½Ñ Ð¸Ð»Ð¸ пÑодолжиÑе ввод", 680 initializeInterval: 100, 681 language: null, 682 minChars: 1, 683 mobileWidth: 600, 684 noCache: false, 685 noSuggestionsHint: null, 686 onInvalidateSelection: null, 687 onSearchComplete: $.noop, 688 onSearchError: $.noop, 689 onSearchStart: $.noop, 690 onSelect: null, 691 onSelectNothing: null, 692 onSuggestionsFetch: null, 693 paramName: "query", 694 params: {}, 695 preventBadQueries: false, 696 requestMode: "suggest", 697 scrollOnFocus: false, 698 // оÑновной url, Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÐµÑеопÑеделен 699 serviceUrl: "https://suggestions.dadata.ru/suggestions/api/4_1/rs", 700 tabDisabled: false, 701 timeout: 3000, 702 triggerSelectOnBlur: true, 703 triggerSelectOnEnter: true, 704 triggerSelectOnSpace: false, 705 type: null, 706 // url, коÑоÑÑй заменÑÐµÑ serviceUrl + method + type 707 // Ñо еÑÑÑ, еÑли он задан, Ñо Ð´Ð»Ñ Ð²ÑÐµÑ Ð·Ð°Ð¿ÑоÑов бÑÐ´ÐµÑ Ð¸ÑполÑзоваÑÑÑÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ он 708 // еÑли не поддеÑживаеÑÑÑ cors Ñо к url бÑдÑÑ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ñ Ð¿Ð°ÑамеÑÑÑ ?token=...&version=... 709 // и заменен пÑоÑокол на пÑоÑокол ÑекÑÑей ÑÑÑаниÑÑ 710 url: null 711}; 712 713/** 714 * Factory to create same parent checker function 715 * @param preprocessFn called on each value before comparison 716 * @returns {Function} same parent checker function 717 */ 718function sameParentChecker(preprocessFn) { 719 return function(suggestions) { 720 if (suggestions.length === 0) { 721 return false; 722 } 723 if (suggestions.length === 1) { 724 return true; 725 } 726 727 var parentValue = preprocessFn(suggestions[0].value), 728 aliens = suggestions.filter(function(suggestion) { 729 return ( 730 preprocessFn(suggestion.value).indexOf(parentValue) !== 0 731 ); 732 }); 733 734 return aliens.length === 0; 735 }; 736} 737 738/** 739 * Default same parent checker. Compares raw values. 740 * @type {Function} 741 */ 742var haveSameParent = sameParentChecker(function(val) { 743 return val; 744}); 745 746/** 747 * СÑÐ°Ð²Ð½Ð¸Ð²Ð°ÐµÑ Ð·Ð°Ð¿ÑÐ¾Ñ c подÑказками, по Ñловам. 748 * СÑабаÑÑваеÑ, ÑолÑко еÑли Ñ Ð²ÑÐµÑ Ð¿Ð¾Ð´Ñказок обÑий ÑодиÑÐµÐ»Ñ 749 * (ÑÑнкÑÐ¸Ñ ÑвеÑки пеÑедаÑÑÑÑ Ð¿Ð°ÑамеÑÑом). 750 * ÐгноÑиÑÑÐµÑ ÑÑоп-Ñлова. 751 * ÐозвÑаÑÐ°ÐµÑ Ð¸Ð½Ð´ÐµÐºÑ ÐµÐ´Ð¸Ð½ÑÑвенной Ð¿Ð¾Ð´Ñ Ð¾Ð´ÑÑей подÑказки 752 * или -1, еÑли Ð¿Ð¾Ð´Ñ Ð¾Ð´ÑÑÐ¸Ñ Ð½ÐµÑ Ð¸Ð»Ð¸ неÑколÑко. 753 */ 754function _matchByWords(stopwords, parentCheckerFn) { 755 return function(query, suggestions) { 756 var queryTokens; 757 var matches = []; 758 759 if (parentCheckerFn(suggestions)) { 760 queryTokens = text_util.splitTokens( 761 text_util.split(query, stopwords) 762 ); 763 764 collection_util.each(suggestions, function(suggestion, i) { 765 var suggestedValue = suggestion.value; 766 767 if (text_util.stringEncloses(query, suggestedValue)) { 768 return false; 769 } 770 771 // check if query words are a subset of suggested words 772 var suggestionWords = text_util.splitTokens( 773 text_util.split(suggestedValue, stopwords) 774 ); 775 776 if ( 777 collection_util.minus(queryTokens, suggestionWords) 778 .length === 0 779 ) { 780 matches.push(i); 781 } 782 }); 783 } 784 785 return matches.length === 1 ? matches[0] : -1; 786 }; 787} 788 789/** 790 * Matchers return index of suitable suggestion 791 * Context inside is optionally set in types.js 792 */ 793var matchers = { 794 /** 795 * Matches query against suggestions, removing all the stopwords. 796 */ 797 matchByNormalizedQuery: function(stopwords) { 798 return function(query, suggestions) { 799 var normalizedQuery = text_util.normalize(query, stopwords); 800 var matches = []; 801 802 collection_util.each(suggestions, function(suggestion, i) { 803 var suggestedValue = suggestion.value.toLowerCase(); 804 // if query encloses suggestion, than it has already been selected 805 // so we should not select it anymore 806 if (text_util.stringEncloses(query, suggestedValue)) { 807 return false; 808 } 809 // if there is suggestion that contains query as its part 810 // than we should ignore all other matches, even full ones 811 if (suggestedValue.indexOf(normalizedQuery) > 0) { 812 return false; 813 } 814 if ( 815 normalizedQuery === 816 text_util.normalize(suggestedValue, stopwords) 817 ) { 818 matches.push(i); 819 } 820 }); 821 822 return matches.length === 1 ? matches[0] : -1; 823 }; 824 }, 825 826 matchByWords: function(stopwords) { 827 return _matchByWords(stopwords, haveSameParent); 828 }, 829 830 matchByWordsAddress: function(stopwords) { 831 return _matchByWords(stopwords, haveSameParent); 832 }, 833 834 /** 835 * Matches query against values contained in suggestion fields 836 * for cases, when there is only one suggestion 837 * only considers fields specified in fields map 838 * uses partial matching: 839 * "0445" vs { value: "ALFA-BANK", data: { "bic": "044525593" }} is a match 840 */ 841 matchByFields: function(fields) { 842 return function(query, suggestions) { 843 var tokens = text_util.splitTokens(text_util.split(query)); 844 var suggestionWords = []; 845 846 if (suggestions.length === 1) { 847 if (fields) { 848 collection_util.each(fields, function(stopwords, field) { 849 var fieldValue = object_util.getDeepValue( 850 suggestions[0], 851 field 852 ); 853 var fieldWords = 854 fieldValue && 855 text_util.splitTokens( 856 text_util.split(fieldValue, stopwords) 857 ); 858 859 if (fieldWords && fieldWords.length) { 860 suggestionWords = suggestionWords.concat( 861 fieldWords 862 ); 863 } 864 }); 865 } 866 867 if ( 868 collection_util.minusWithPartialMatching( 869 tokens, 870 suggestionWords 871 ).length === 0 872 ) { 873 return 0; 874 } 875 } 876 877 return -1; 878 }; 879 } 880}; 881 882var ADDRESS_STOPWORDS = [ 883 "ао", 884 "аобл", 885 "дом", 886 "ÑеÑп", 887 "а/Ñ", 888 "аал", 889 "авÑодоÑога", 890 "аллеÑ", 891 "аÑбан", 892 "аÑл", 893 "б-Ñ", 894 "беÑег", 895 "бÑгоÑ", 896 "вал", 897 "вл", 898 "волоÑÑÑ", 899 "вÑезд", 900 "вÑÑел", 901 "г", 902 "гоÑодок", 903 "гÑк", 904 "д", 905 "двлд", 906 "днп", 907 "доÑ", 908 "дп", 909 "ж/д_бÑдка", 910 "ж/д_казаÑм", 911 "ж/д_оп", 912 "ж/д_плаÑÑ", 913 "ж/д_поÑÑ", 914 "ж/д_Ñзд", 915 "ж/д_ÑÑ", 916 "жилзона", 917 "жилÑайон", 918 "жÑ", 919 "заезд", 920 "заимка", 921 "зона", 922 "к", 923 "казаÑма", 924 "канал", 925 "кв", 926 "кв-л", 927 "км", 928 "колÑÑо", 929 "комн", 930 "коÑдон", 931 "коÑа", 932 "кп", 933 "кÑай", 934 "линиÑ", 935 "Ð»Ð¿Ñ ", 936 "м", 937 "маÑÑив", 938 "меÑÑноÑÑÑ", 939 "мкÑ", 940 "моÑÑ", 941 "н/п", 942 "наб", 943 "нп", 944 "обл", 945 "окÑÑг", 946 "оÑÑÑов", 947 "оÑ", 948 "п", 949 "п/о", 950 "п/Ñ", 951 "п/ÑÑ", 952 "паÑк", 953 "пгÑ", 954 "пеÑ", 955 "пеÑеезд", 956 "пл", 957 "пл-ка", 958 "плаÑÑ", 959 "погоÑÑ", 960 "полÑÑÑанок", 961 "поÑинок", 962 "пÑ-кÑ", 963 "пÑоезд", 964 "пÑомзона", 965 "пÑоÑек", 966 "пÑоÑека", 967 "пÑоÑелок", 968 "пÑоÑок", 969 "пÑоÑока", 970 "пÑоÑлок", 971 "Ñ-н", 972 "Ñзд", 973 "ÑоÑÑиÑ", 974 "Ñп", 975 "ÑÑдÑ", 976 "Ñ", 977 "Ñ/а", 978 "Ñ/мо", 979 "Ñ/о", 980 "Ñ/п", 981 "Ñ/Ñ", 982 "Ñад", 983 "ÑквеÑ", 984 "Ñл", 985 "ÑнÑ", 986 "ÑпÑÑк", 987 "ÑÑ", 988 "ÑÑ-Ñа", 989 "ÑÑÑ", 990 "ÑеÑ", 991 "ÑÑакÑ", 992 "ÑÑп", 993 "Ñ", 994 "Ñл", 995 "ÑÑ-к", 996 "Ñ/Ñ ", 997 "ÑеÑма", 998 "Ñ ", 999 "Ñ", 1000 "бÑлÑваÑ", 1001 "владение", 1002 "вÑÑелки", 1003 "гаÑажно-ÑÑÑоиÑелÑнÑй", 1004 "гоÑод", 1005 "деÑевнÑ", 1006 "домовладение", 1007 "доÑога", 1008 "кваÑÑал", 1009 "киломеÑÑ", 1010 "комнаÑа", 1011 "коÑпÑÑ", 1012 "лиÑеÑ", 1013 "леÑпÑÐ¾Ð¼Ñ Ð¾Ð·", 1014 "меÑÑеÑко", 1015 "микÑоÑайон", 1016 "набеÑежнаÑ", 1017 "облаÑÑÑ", 1018 "пеÑеÑлок", 1019 "плаÑÑоÑма", 1020 "плоÑадка", 1021 "плоÑадÑ", 1022 "поÑеление", 1023 "поÑелок", 1024 "пÑоÑпекÑ", 1025 "ÑазÑезд", 1026 "Ñайон", 1027 "ÑеÑпÑблика", 1028 "Ñело", 1029 "ÑелÑÑовеÑ", 1030 "Ñлобода", 1031 "ÑооÑÑжение", 1032 "ÑÑаниÑа", 1033 "ÑÑанÑиÑ", 1034 "ÑÑÑоение", 1035 "ÑеÑÑиÑоÑиÑ", 1036 "ÑÑпик", 1037 "ÑлиÑа", 1038 "ÑлÑÑ", 1039 "ÑÑаÑÑок", 1040 "Ñ ÑÑоÑ", 1041 "ÑоÑÑе" 1042]; 1043 1044/** 1045 * ÐомпоненÑÑ Ð°Ð´ÑеÑа 1046 * @type {*[]} 1047 * id {String} Ðаименование Ñипа 1048 * fields {Array of Strings} 1049 * forBounds {Boolean} Ð¼Ð¾Ð¶ÐµÑ Ð¸ÑполÑзоваÑÑÑÑ Ð² огÑаниÑениÑÑ 1050 * forLocations {Boolean} 1051 * kladrFormat {Object} 1052 * fiasType {String} Ðаименование ÑооÑвеÑÑÑвÑÑÑего ФÐÐС Ñипа 1053 */ 1054var ADDRESS_COMPONENTS = [ 1055 { 1056 id: "kladr_id", 1057 fields: ["kladr_id"], 1058 forBounds: false, 1059 forLocations: true 1060 }, 1061 { 1062 id: "postal_code", 1063 fields: ["postal_code"], 1064 forBounds: false, 1065 forLocations: true 1066 }, 1067 { 1068 id: "country_iso_code", 1069 fields: ["country_iso_code"], 1070 forBounds: false, 1071 forLocations: true 1072 }, 1073 { 1074 id: "country", 1075 fields: ["country"], 1076 forBounds: true, 1077 forLocations: true, 1078 kladrFormat: { digits: 0, zeros: 13 }, 1079 fiasType: "country_iso_code" 1080 }, 1081 { 1082 id: "region_iso_code", 1083 fields: ["region_iso_code"], 1084 forBounds: false, 1085 forLocations: true 1086 }, 1087 { 1088 id: "region_fias_id", 1089 fields: ["region_fias_id"], 1090 forBounds: false, 1091 forLocations: true 1092 }, 1093 { 1094 id: "region_type_full", 1095 fields: ["region_type_full"], 1096 forBounds: false, 1097 forLocations: true, 1098 kladrFormat: { digits: 2, zeros: 11 }, 1099 fiasType: "region_fias_id" 1100 }, 1101 { 1102 id: "region", 1103 fields: [ 1104 "region", 1105 "region_type", 1106 "region_type_full", 1107 "region_with_type" 1108 ], 1109 forBounds: true, 1110 forLocations: true, 1111 kladrFormat: { digits: 2, zeros: 11 }, 1112 fiasType: "region_fias_id" 1113 }, 1114 { 1115 id: "area_fias_id", 1116 fields: ["area_fias_id"], 1117 forBounds: false, 1118 forLocations: true 1119 }, 1120 { 1121 id: "area_type_full", 1122 fields: ["area_type_full"], 1123 forBounds: false, 1124 forLocations: true, 1125 kladrFormat: { digits: 5, zeros: 8 }, 1126 fiasType: "area_fias_id" 1127 }, 1128 { 1129 id: "area", 1130 fields: ["area", "area_type", "area_type_full", "area_with_type"], 1131 forBounds: true, 1132 forLocations: true, 1133 kladrFormat: { digits: 5, zeros: 8 }, 1134 fiasType: "area_fias_id" 1135 }, 1136 { 1137 id: "city_fias_id", 1138 fields: ["city_fias_id"], 1139 forBounds: false, 1140 forLocations: true 1141 }, 1142 { 1143 id: "city_type_full", 1144 fields: ["city_type_full"], 1145 forBounds: false, 1146 forLocations: true, 1147 kladrFormat: { digits: 8, zeros: 5 }, 1148 fiasType: "city_fias_id" 1149 }, 1150 { 1151 id: "city", 1152 fields: ["city", "city_type", "city_type_full", "city_with_type"], 1153 forBounds: true, 1154 forLocations: true, 1155 kladrFormat: { digits: 8, zeros: 5 }, 1156 fiasType: "city_fias_id" 1157 }, 1158 { 1159 id: "city_district_fias_id", 1160 fields: ["city_district_fias_id"], 1161 forBounds: false, 1162 forLocations: true 1163 }, 1164 { 1165 id: "city_district_type_full", 1166 fields: ["city_district_type_full"], 1167 forBounds: false, 1168 forLocations: true, 1169 kladrFormat: { digits: 11, zeros: 2 }, 1170 fiasType: "city_district_fias_id" 1171 }, 1172 { 1173 id: "city_district", 1174 fields: [ 1175 "city_district", 1176 "city_district_type", 1177 "city_district_type_full", 1178 "city_district_with_type" 1179 ], 1180 forBounds: true, 1181 forLocations: true, 1182 kladrFormat: { digits: 11, zeros: 2 }, 1183 fiasType: "city_district_fias_id" 1184 }, 1185 { 1186 id: "settlement_fias_id", 1187 fields: ["settlement_fias_id"], 1188 forBounds: false, 1189 forLocations: true 1190 }, 1191 { 1192 id: "settlement_type_full", 1193 fields: ["settlement_type_full"], 1194 forBounds: false, 1195 forLocations: true, 1196 kladrFormat: { digits: 11, zeros: 2 }, 1197 fiasType: "settlement_fias_id" 1198 }, 1199 { 1200 id: "settlement", 1201 fields: [ 1202 "settlement", 1203 "settlement_type", 1204 "settlement_type_full", 1205 "settlement_with_type" 1206 ], 1207 forBounds: true, 1208 forLocations: true, 1209 kladrFormat: { digits: 11, zeros: 2 }, 1210 fiasType: "settlement_fias_id" 1211 }, 1212 { 1213 id: "street_fias_id", 1214 fields: ["street_fias_id"], 1215 forBounds: false, 1216 forLocations: true 1217 }, 1218 { 1219 id: "street_type_full", 1220 fields: ["street_type_full"], 1221 forBounds: false, 1222 forLocations: true, 1223 kladrFormat: { digits: 15, zeros: 2 }, 1224 fiasType: "street_fias_id" 1225 }, 1226 { 1227 id: "street", 1228 fields: [ 1229 "street", 1230 "street_type", 1231 "street_type_full", 1232 "street_with_type" 1233 ], 1234 forBounds: true, 1235 forLocations: true, 1236 kladrFormat: { digits: 15, zeros: 2 }, 1237 fiasType: "street_fias_id" 1238 }, 1239 { 1240 id: "house", 1241 fields: [ 1242 "house", 1243 "house_type", 1244 "house_type_full", 1245 "block", 1246 "block_type" 1247 ], 1248 forBounds: true, 1249 forLocations: true, 1250 kladrFormat: { digits: 19 }, 1251 fiasType: "house_fias_id" 1252 }, 1253 { 1254 id: "flat", 1255 fields: ["flat", "flat_type", "flat_type_full"], 1256 forBounds: true, 1257 forLocations: false, 1258 kladrFormat: { digits: 19 }, 1259 fiasType: "flat_fias_id" 1260 } 1261]; 1262 1263var ADDRESS_TYPE = { 1264 urlSuffix: "address", 1265 noSuggestionsHint: "ÐеизвеÑÑнÑй адÑеÑ", 1266 matchers: [ 1267 matchers.matchByNormalizedQuery(ADDRESS_STOPWORDS), 1268 matchers.matchByWordsAddress(ADDRESS_STOPWORDS) 1269 ], 1270 dataComponents: ADDRESS_COMPONENTS, 1271 dataComponentsById: object_util.indexObjectsById( 1272 ADDRESS_COMPONENTS, 1273 "id", 1274 "index" 1275 ), 1276 unformattableTokens: ADDRESS_STOPWORDS, 1277 enrichmentEnabled: true, 1278 enrichmentMethod: "suggest", 1279 enrichmentParams: { 1280 count: 1, 1281 locations: null, 1282 locations_boost: null, 1283 from_bound: null, 1284 to_bound: null 1285 }, 1286 getEnrichmentQuery: function(suggestion) { 1287 return suggestion.unrestricted_value; 1288 }, 1289 geoEnabled: true, 1290 isDataComplete: function(suggestion) { 1291 var fields = [this.bounds.to || "flat"], 1292 data = suggestion.data; 1293 1294 return ( 1295 !lang_util.isPlainObject(data) || 1296 object_util.fieldsAreNotEmpty(data, fields) 1297 ); 1298 }, 1299 composeValue: function(data, options) { 1300 var country = data.country, 1301 region = 1302 data.region_with_type || 1303 collection_util 1304 .compact([data.region, data.region_type]) 1305 .join(" ") || 1306 data.region_type_full, 1307 area = 1308 data.area_with_type || 1309 collection_util 1310 .compact([data.area_type, data.area]) 1311 .join(" ") || 1312 data.area_type_full, 1313 city = 1314 data.city_with_type || 1315 collection_util 1316 .compact([data.city_type, data.city]) 1317 .join(" ") || 1318 data.city_type_full, 1319 settelement = 1320 data.settlement_with_type || 1321 collection_util 1322 .compact([data.settlement_type, data.settlement]) 1323 .join(" ") || 1324 data.settlement_type_full, 1325 cityDistrict = 1326 data.city_district_with_type || 1327 collection_util 1328 .compact([data.city_district_type, data.city_district]) 1329 .join(" ") || 1330 data.city_district_type_full, 1331 street = 1332 data.street_with_type || 1333 collection_util 1334 .compact([data.street_type, data.street]) 1335 .join(" ") || 1336 data.street_type_full, 1337 house = collection_util 1338 .compact([ 1339 data.stead_type, 1340 data.stead, 1341 data.house_type, 1342 data.house, 1343 data.block_type, 1344 data.block 1345 ]) 1346 .join(" "), 1347 flat = collection_util 1348 .compact([data.flat_type, data.flat]) 1349 .join(" "), 1350 postal_box = data.postal_box && "а/Ñ " + data.postal_box, 1351 result; 1352 1353 // еÑли Ñегион ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ Ñ Ð³Ð¾Ñодом 1354 // напÑÐ¸Ð¼ÐµÑ Ð³ ÐоÑква, г ÐоÑква 1355 // Ñо не показÑваем Ñегион 1356 if (region === city) { 1357 region = ""; 1358 } 1359 1360 // иногда не показÑваем Ñайон 1361 if (!(options && options.saveCityDistrict)) { 1362 if (options && options.excludeCityDistrict) { 1363 // еÑли Ñайон Ñвно запÑеÑен 1364 cityDistrict = ""; 1365 } else if (cityDistrict && !data.city_district_fias_id) { 1366 // еÑли Ñайон взÑÑ Ð¸Ð· ÐÐÐТР(Ñ Ð½ÐµÐ³Ð¾ пÑÑÑой city_district_fias_id) 1367 cityDistrict = ""; 1368 } 1369 } 1370 1371 result = collection_util 1372 .compact([ 1373 country, 1374 region, 1375 area, 1376 city, 1377 cityDistrict, 1378 settelement, 1379 street, 1380 house, 1381 flat, 1382 postal_box 1383 ]) 1384 .join(", "); 1385 1386 return result; 1387 }, 1388 formatResult: (function() { 1389 var componentsUnderCityDistrict = [], 1390 _underCityDistrict = false; 1391 1392 ADDRESS_COMPONENTS.forEach(function(component) { 1393 if (_underCityDistrict) 1394 componentsUnderCityDistrict.push(component.id); 1395 if (component.id === "city_district") _underCityDistrict = true; 1396 }); 1397 1398 return function(value, currentValue, suggestion, options) { 1399 var that = this, 1400 district = 1401 suggestion.data && suggestion.data.city_district_with_type, 1402 unformattableTokens = options && options.unformattableTokens, 1403 historyValues = 1404 suggestion.data && suggestion.data.history_values, 1405 tokens, 1406 unusedTokens, 1407 formattedHistoryValues; 1408 1409 // добавлÑем иÑÑоÑиÑеÑкие знаÑÐµÐ½Ð¸Ñ 1410 if (historyValues && historyValues.length > 0) { 1411 tokens = text_util.tokenize(currentValue, unformattableTokens); 1412 unusedTokens = this.type.findUnusedTokens(tokens, value); 1413 formattedHistoryValues = this.type.getFormattedHistoryValues( 1414 unusedTokens, 1415 historyValues 1416 ); 1417 if (formattedHistoryValues) { 1418 value += formattedHistoryValues; 1419 } 1420 } 1421 1422 value = that.highlightMatches( 1423 value, 1424 currentValue, 1425 suggestion, 1426 options 1427 ); 1428 value = that.wrapFormattedValue(value, suggestion); 1429 1430 if ( 1431 district && 1432 (!that.bounds.own.length || 1433 that.bounds.own.indexOf("street") >= 0) && 1434 !lang_util.isEmptyObject( 1435 that.copyDataComponents( 1436 suggestion.data, 1437 componentsUnderCityDistrict 1438 ) 1439 ) 1440 ) { 1441 value += 1442 '<div class="' + 1443 that.classes.subtext + 1444 '">' + 1445 that.highlightMatches(district, currentValue, suggestion) + 1446 "</div>"; 1447 } 1448 1449 return value; 1450 }; 1451 })(), 1452 1453 /** 1454 * ÐозвÑаÑÐ°ÐµÑ ÑпиÑок Ñлов в запÑоÑе, 1455 * коÑоÑÑе не вÑÑÑеÑаÑÑÑÑ Ð² подÑказке 1456 */ 1457 findUnusedTokens: function(tokens, value) { 1458 var tokenIndex, 1459 token, 1460 unused = []; 1461 1462 unused = tokens.filter(function(token) { 1463 return value.indexOf(token) === -1; 1464 }); 1465 1466 return unused; 1467 }, 1468 1469 /** 1470 * ÐозвÑаÑÐ°ÐµÑ Ð¸ÑÑоÑиÑеÑкие Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ñлов запÑоÑа, 1471 * Ð´Ð»Ñ ÐºÐ¾ÑоÑÑÑ Ð½Ðµ найдено ÑÐ¾Ð²Ð¿Ð°Ð´ÐµÐ½Ð¸Ñ Ð² оÑновном знаÑении подÑказки 1472 */ 1473 getFormattedHistoryValues: function(unusedTokens, historyValues) { 1474 var tokenIndex, 1475 token, 1476 historyValueIndex, 1477 historyValue, 1478 values = [], 1479 formatted = ""; 1480 1481 historyValues.forEach(function(historyValue) { 1482 collection_util.each(unusedTokens, function(token) { 1483 if (historyValue.toLowerCase().indexOf(token) >= 0) { 1484 values.push(historyValue); 1485 return false; 1486 } 1487 }); 1488 }); 1489 1490 if (values.length > 0) { 1491 formatted = " (бÑвÑ. " + values.join(", ") + ")"; 1492 } 1493 1494 return formatted; 1495 }, 1496 1497 /** 1498 * @param instance 1499 * @param options 1500 * @param options.suggestion 1501 * @param options.hasSameValues 1502 * @param options.hasBeenEnreached 1503 */ 1504 getSuggestionValue: function(instance, options) { 1505 var formattedValue = null; 1506 1507 if (options.hasSameValues) { 1508 if (instance.options.restrict_value) { 1509 // Can not use unrestricted address, 1510 // because some components (from constraints) must be omitted 1511 formattedValue = this.getValueWithinConstraints( 1512 instance, 1513 options.suggestion 1514 ); 1515 } else if (instance.bounds.own.length) { 1516 // Can not use unrestricted address, 1517 // because only components from bounds must be included 1518 formattedValue = this.getValueWithinBounds( 1519 instance, 1520 options.suggestion 1521 ); 1522 } else { 1523 // Can use full unrestricted address 1524 formattedValue = options.suggestion.unrestricted_value; 1525 } 1526 } else if (options.hasBeenEnriched) { 1527 if (instance.options.restrict_value) { 1528 formattedValue = this.getValueWithinConstraints( 1529 instance, 1530 options.suggestion, 1531 { excludeCityDistrict: true } 1532 ); 1533 } 1534 } 1535 1536 return formattedValue; 1537 }, 1538 /* 1539 * Compose suggestion value with respect to constraints 1540 */ 1541 getValueWithinConstraints: function(instance, suggestion, options) { 1542 return this.composeValue( 1543 instance.getUnrestrictedData(suggestion.data), 1544 options 1545 ); 1546 }, 1547 /* 1548 * Compose suggestion value with respect to bounds 1549 */ 1550 getValueWithinBounds: function(instance, suggestion, options) { 1551 // Ð´Ð»Ñ ÐºÐ¾ÑÑекÑного ÑоÑÑÐ°Ð²Ð»ÐµÐ½Ð¸Ñ Ð°Ð´ÑеÑа нÑжен city_district_fias_id 1552 var data = instance.copyDataComponents( 1553 suggestion.data, 1554 instance.bounds.own.concat(["city_district_fias_id"]) 1555 ); 1556 1557 return this.composeValue(data, options); 1558 } 1559}; 1560 1561/** 1562 * ÐомпоненÑÑ Ð°Ð´ÑеÑа 1563 * @type {*[]} 1564 * id {String} Ðаименование Ñипа 1565 * fields {Array of Strings} 1566 * forBounds {Boolean} Ð¼Ð¾Ð¶ÐµÑ Ð¸ÑполÑзоваÑÑÑÑ Ð² огÑаниÑениÑÑ 1567 * forLocations {Boolean} 1568 * kladrFormat {Object} 1569 * fiasType {String} Ðаименование ÑооÑвеÑÑÑвÑÑÑего ФÐÐС Ñипа 1570 */ 1571var FIAS_COMPONENTS = [ 1572 { 1573 id: "kladr_id", 1574 fields: ["kladr_id"], 1575 forBounds: false, 1576 forLocations: true 1577 }, 1578 { 1579 id: "region_fias_id", 1580 fields: ["region_fias_id"], 1581 forBounds: false, 1582 forLocations: true 1583 }, 1584 { 1585 id: "region_type_full", 1586 fields: ["region_type_full"], 1587 forBounds: false, 1588 forLocations: true, 1589 kladrFormat: { digits: 2, zeros: 11 }, 1590 fiasType: "region_fias_id" 1591 }, 1592 { 1593 id: "region", 1594 fields: [ 1595 "region", 1596 "region_type", 1597 "region_type_full", 1598 "region_with_type" 1599 ], 1600 forBounds: true, 1601 forLocations: true, 1602 kladrFormat: { digits: 2, zeros: 11 }, 1603 fiasType: "region_fias_id" 1604 }, 1605 { 1606 id: "area_fias_id", 1607 fields: ["area_fias_id"], 1608 forBounds: false, 1609 forLocations: true 1610 }, 1611 { 1612 id: "area_type_full", 1613 fields: ["area_type_full"], 1614 forBounds: false, 1615 forLocations: true, 1616 kladrFormat: { digits: 5, zeros: 8 }, 1617 fiasType: "area_fias_id" 1618 }, 1619 { 1620 id: "area", 1621 fields: ["area", "area_type", "area_type_full", "area_with_type"], 1622 forBounds: true, 1623 forLocations: true, 1624 kladrFormat: { digits: 5, zeros: 8 }, 1625 fiasType: "area_fias_id" 1626 }, 1627 { 1628 id: "city_fias_id", 1629 fields: ["city_fias_id"], 1630 forBounds: false, 1631 forLocations: true 1632 }, 1633 { 1634 id: "city_type_full", 1635 fields: ["city_type_full"], 1636 forBounds: false, 1637 forLocations: true, 1638 kladrFormat: { digits: 8, zeros: 5 }, 1639 fiasType: "city_fias_id" 1640 }, 1641 { 1642 id: "city", 1643 fields: ["city", "city_type", "city_type_full", "city_with_type"], 1644 forBounds: true, 1645 forLocations: true, 1646 kladrFormat: { digits: 8, zeros: 5 }, 1647 fiasType: "city_fias_id" 1648 }, 1649 { 1650 id: "city_district_fias_id", 1651 fields: ["city_district_fias_id"], 1652 forBounds: false, 1653 forLocations: true 1654 }, 1655 { 1656 id: "city_district_type_full", 1657 fields: ["city_district_type_full"], 1658 forBounds: false, 1659 forLocations: true, 1660 kladrFormat: { digits: 11, zeros: 2 }, 1661 fiasType: "city_district_fias_id" 1662 }, 1663 { 1664 id: "city_district", 1665 fields: [ 1666 "city_district", 1667 "city_district_type", 1668 "city_district_type_full", 1669 "city_district_with_type" 1670 ], 1671 forBounds: true, 1672 forLocations: true, 1673 kladrFormat: { digits: 11, zeros: 2 }, 1674 fiasType: "city_district_fias_id" 1675 }, 1676 { 1677 id: "settlement_fias_id", 1678 fields: ["settlement_fias_id"], 1679 forBounds: false, 1680 forLocations: true 1681 }, 1682 { 1683 id: "settlement_type_full", 1684 fields: ["settlement_type_full"], 1685 forBounds: false, 1686 forLocations: true, 1687 kladrFormat: { digits: 11, zeros: 2 }, 1688 fiasType: "settlement_fias_id" 1689 }, 1690 { 1691 id: "settlement", 1692 fields: [ 1693 "settlement", 1694 "settlement_type", 1695 "settlement_type_full", 1696 "settlement_with_type" 1697 ], 1698 forBounds: true, 1699 forLocations: true, 1700 kladrFormat: { digits: 11, zeros: 2 }, 1701 fiasType: "settlement_fias_id" 1702 }, 1703 { 1704 id: "planning_structure_fias_id", 1705 fields: ["planning_structure_fias_id"], 1706 forBounds: false, 1707 forLocations: true 1708 }, 1709 { 1710 id: "planning_structure_type_full", 1711 fields: ["planning_structure_type_full"], 1712 forBounds: false, 1713 forLocations: true, 1714 kladrFormat: { digits: 15, zeros: 2 }, 1715 fiasType: "planning_structure_fias_id" 1716 }, 1717 { 1718 id: "planning_structure", 1719 fields: [ 1720 "planning_structure", 1721 "planning_structure_type", 1722 "planning_structure_type_full", 1723 "planning_structure_with_type" 1724 ], 1725 forBounds: true, 1726 forLocations: true, 1727 kladrFormat: { digits: 15, zeros: 2 }, 1728 fiasType: "planning_structure_fias_id" 1729 }, 1730 { 1731 id: "street_fias_id", 1732 fields: ["street_fias_id"], 1733 forBounds: false, 1734 forLocations: true 1735 }, 1736 { 1737 id: "street_type_full", 1738 fields: ["street_type_full"], 1739 forBounds: false, 1740 forLocations: true, 1741 kladrFormat: { digits: 15, zeros: 2 }, 1742 fiasType: "street_fias_id" 1743 }, 1744 { 1745 id: "street", 1746 fields: [ 1747 "street", 1748 "street_type", 1749 "street_type_full", 1750 "street_with_type" 1751 ], 1752 forBounds: true, 1753 forLocations: true, 1754 kladrFormat: { digits: 15, zeros: 2 }, 1755 fiasType: "street_fias_id" 1756 }, 1757 { 1758 id: "house", 1759 fields: ["house", "house_type", "block", "building_type", "building"], 1760 forBounds: true, 1761 forLocations: false, 1762 kladrFormat: { digits: 19 } 1763 } 1764]; 1765 1766var FIAS_TYPE = { 1767 urlSuffix: "fias", 1768 noSuggestionsHint: "ÐеизвеÑÑнÑй адÑеÑ", 1769 matchers: [ 1770 matchers.matchByNormalizedQuery(ADDRESS_STOPWORDS), 1771 matchers.matchByWordsAddress(ADDRESS_STOPWORDS) 1772 ], 1773 dataComponents: FIAS_COMPONENTS, 1774 dataComponentsById: object_util.indexObjectsById( 1775 FIAS_COMPONENTS, 1776 "id", 1777 "index" 1778 ), 1779 unformattableTokens: ADDRESS_STOPWORDS, 1780 isDataComplete: function(suggestion) { 1781 var fields = [this.bounds.to || "house"], 1782 data = suggestion.data; 1783 1784 return ( 1785 !lang_util.isPlainObject(data) || 1786 object_util.fieldsAreNotEmpty(data, fields) 1787 ); 1788 }, 1789 composeValue: function(data, options) { 1790 var country = data.country, 1791 region = 1792 data.region_with_type || 1793 collection_util 1794 .compact([data.region, data.region_type]) 1795 .join(" ") || 1796 data.region_type_full, 1797 area = 1798 data.area_with_type || 1799 collection_util 1800 .compact([data.area_type, data.area]) 1801 .join(" ") || 1802 data.area_type_full, 1803 city = 1804 data.city_with_type || 1805 collection_util 1806 .compact([data.city_type, data.city]) 1807 .join(" ") || 1808 data.city_type_full, 1809 settelement = 1810 data.settlement_with_type || 1811 collection_util 1812 .compact([data.settlement_type, data.settlement]) 1813 .join(" ") || 1814 data.settlement_type_full, 1815 cityDistrict = 1816 data.city_district_with_type || 1817 collection_util 1818 .compact([data.city_district_type, data.city_district]) 1819 .join(" ") || 1820 data.city_district_type_full, 1821 planning_structure = 1822 data.planning_structure_with_type || 1823 collection_util 1824 .compact([ 1825 data.planning_structure_type, 1826 data.planning_structure 1827 ]) 1828 .join(" ") || 1829 data.planning_structure_type_full, 1830 street = 1831 data.street_with_type || 1832 collection_util 1833 .compact([data.street_type, data.street]) 1834 .join(" ") || 1835 data.street_type_full, 1836 house = collection_util 1837 .compact([ 1838 data.house_type, 1839 data.house, 1840 data.block_type, 1841 data.block 1842 ]) 1843 .join(" "), 1844 flat = collection_util 1845 .compact([data.flat_type, data.flat]) 1846 .join(" "), 1847 postal_box = data.postal_box && "а/Ñ " + data.postal_box, 1848 result; 1849 1850 // еÑли Ñегион ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ Ñ Ð³Ð¾Ñодом 1851 // напÑÐ¸Ð¼ÐµÑ Ð³ ÐоÑква, г ÐоÑква 1852 // Ñо не показÑваем Ñегион 1853 if (region === city) { 1854 region = ""; 1855 } 1856 1857 // иногда не показÑваем Ñайон 1858 if (!(options && options.saveCityDistrict)) { 1859 if (options && options.excludeCityDistrict) { 1860 // еÑли Ñайон Ñвно запÑеÑен 1861 cityDistrict = ""; 1862 } else if (cityDistrict && !data.city_district_fias_id) { 1863 // еÑли Ñайон взÑÑ Ð¸Ð· ÐÐÐТР(Ñ Ð½ÐµÐ³Ð¾ пÑÑÑой city_district_fias_id) 1864 cityDistrict = ""; 1865 } 1866 } 1867 1868 result = collection_util 1869 .compact([ 1870 country, 1871 region, 1872 area, 1873 city, 1874 cityDistrict, 1875 settelement, 1876 planning_structure, 1877 street, 1878 house, 1879 flat, 1880 postal_box 1881 ]) 1882 .join(", "); 1883 1884 return result; 1885 }, 1886 formatResult: (function() { 1887 return function(value, currentValue, suggestion, options) { 1888 var that = this; 1889 1890 value = that.highlightMatches( 1891 value, 1892 currentValue, 1893 suggestion, 1894 options 1895 ); 1896 value = that.wrapFormattedValue(value, suggestion); 1897 return value; 1898 }; 1899 })(), 1900 1901 getSuggestionValue: ADDRESS_TYPE.getSuggestionValue, 1902 getValueWithinConstraints: ADDRESS_TYPE.getValueWithinConstraints, 1903 getValueWithinBounds: ADDRESS_TYPE.getValueWithinBounds 1904}; 1905 1906function valueStartsWith(suggestion, field) { 1907 var fieldValue = suggestion.data && suggestion.data[field]; 1908 1909 return ( 1910 fieldValue && 1911 new RegExp( 1912 "^" + 1913 text_util.escapeRegExChars(fieldValue) + 1914 "([" + 1915 WORD_DELIMITERS + 1916 "]|$)", 1917 "i" 1918 ).test(suggestion.value) 1919 ); 1920} 1921 1922var NAME_TYPE = { 1923 urlSuffix: "fio", 1924 noSuggestionsHint: false, 1925 matchers: [matchers.matchByNormalizedQuery(), matchers.matchByWords()], 1926 // names for labels, describing which fields are displayed 1927 fieldNames: { 1928 surname: "ÑамилиÑ", 1929 name: "имÑ", 1930 patronymic: "оÑÑеÑÑво" 1931 }, 1932 isDataComplete: function(suggestion) { 1933 var that = this, 1934 params = that.options.params, 1935 data = suggestion.data, 1936 fields; 1937 1938 if (lang_util.isFunction(params)) { 1939 params = params.call(that.element, suggestion.value); 1940 } 1941 if (params && params.parts) { 1942 fields = params.parts.map(function(part) { 1943 return part.toLowerCase(); 1944 }); 1945 } else { 1946 // when NAME is first, patronymic is mot mandatory 1947 fields = ["surname", "name"]; 1948 // when SURNAME is first, it is 1949 if (valueStartsWith(suggestion, "surname")) { 1950 fields.push("patronymic"); 1951 } 1952 } 1953 return object_util.fieldsAreNotEmpty(data, fields); 1954 }, 1955 composeValue: function(data) { 1956 return collection_util 1957 .compact([data.surname, data.name, data.patronymic]) 1958 .join(" "); 1959 } 1960}; 1961 1962var innPartsLengths = { 1963 LEGAL: [2, 2, 5, 1], 1964 INDIVIDUAL: [2, 2, 6, 2] 1965}; 1966 1967function chooseFormattedField(formattedMain, formattedAlt) { 1968 var rHasMatch = /<strong>/; 1969 return rHasMatch.test(formattedAlt) && !rHasMatch.test(formattedMain) 1970 ? formattedAlt 1971 : formattedMain; 1972} 1973 1974function formattedField(main, alt, currentValue, suggestion, options) { 1975 var that = this, 1976 formattedMain = that.highlightMatches( 1977 main, 1978 currentValue, 1979 suggestion, 1980 options 1981 ), 1982 formattedAlt = that.highlightMatches( 1983 alt, 1984 currentValue, 1985 suggestion, 1986 options 1987 ); 1988 1989 return chooseFormattedField(formattedMain, formattedAlt); 1990} 1991 1992var PARTY_TYPE = { 1993 urlSuffix: "party", 1994 noSuggestionsHint: "ÐеизвеÑÑÐ½Ð°Ñ Ð¾ÑганизаÑиÑ", 1995 matchers: [ 1996 matchers.matchByFields( 1997 // These fields of suggestion's `data` used by by-words matcher 1998 { 1999 value: null, 2000 "data.address.value": ADDRESS_STOPWORDS, 2001 "data.inn": null, 2002 "data.ogrn": null 2003 } 2004 ) 2005 ], 2006 dataComponents: ADDRESS_COMPONENTS, 2007 enrichmentEnabled: true, 2008 enrichmentMethod: "findById", 2009 enrichmentParams: { 2010 count: 1, 2011 locations_boost: null 2012 }, 2013 getEnrichmentQuery: function(suggestion) { 2014 return suggestion.data.hid; 2015 }, 2016 geoEnabled: true, 2017 formatResult: function(value, currentValue, suggestion, options) { 2018 var that = this, 2019 formattedInn = that.type.formatResultInn.call( 2020 that, 2021 suggestion, 2022 currentValue 2023 ), 2024 formatterOGRN = that.highlightMatches( 2025 object_util.getDeepValue(suggestion.data, "ogrn"), 2026 currentValue, 2027 suggestion 2028 ), 2029 formattedInnOGRN = chooseFormattedField( 2030 formattedInn, 2031 formatterOGRN 2032 ), 2033 formattedFIO = that.highlightMatches( 2034 object_util.getDeepValue(suggestion.data, "management.name"), 2035 currentValue, 2036 suggestion 2037 ), 2038 address = 2039 object_util.getDeepValue(suggestion.data, "address.value") || 2040 ""; 2041 2042 if (that.isMobile) { 2043 (options || (options = {})).maxLength = 50; 2044 } 2045 2046 value = formattedField.call( 2047 that, 2048 value, 2049 object_util.getDeepValue(suggestion.data, "name.latin"), 2050 currentValue, 2051 suggestion, 2052 options 2053 ); 2054 value = that.wrapFormattedValue(value, suggestion); 2055 2056 if (address) { 2057 address = address.replace(/^(\d{6}|РоÑÑиÑ),\s+/i, ""); 2058 if (that.isMobile) { 2059 // keep only two first words 2060 address = address.replace( 2061 new RegExp( 2062 "^([^" + 2063 WORD_DELIMITERS + 2064 "]+[" + 2065 WORD_DELIMITERS + 2066 "]+[^" + 2067 WORD_DELIMITERS + 2068 "]+).*" 2069 ), 2070 "$1" 2071 ); 2072 } else { 2073 address = that.highlightMatches( 2074 address, 2075 currentValue, 2076 suggestion, 2077 { 2078 unformattableTokens: ADDRESS_STOPWORDS 2079 } 2080 ); 2081 } 2082 } 2083 2084 if (formattedInnOGRN || address || formattedFIO) { 2085 value += 2086 '<div class="' + 2087 that.classes.subtext + 2088 '">' + 2089 '<span class="' + 2090 that.classes.subtext_inline + 2091 '">' + 2092 (formattedInnOGRN || "") + 2093 "</span>" + 2094 (chooseFormattedField(address, formattedFIO) || "") + 2095 "</div>"; 2096 } 2097 return value; 2098 }, 2099 formatResultInn: function(suggestion, currentValue) { 2100 var that = this, 2101 inn = suggestion.data && suggestion.data.inn, 2102 innPartsLength = 2103 innPartsLengths[suggestion.data && suggestion.data.type], 2104 innParts, 2105 formattedInn, 2106 rDigit = /\d/; 2107 2108 if (inn) { 2109 formattedInn = that.highlightMatches(inn, currentValue, suggestion); 2110 if (innPartsLength) { 2111 formattedInn = formattedInn.split(""); 2112 innParts = innPartsLength.map(function(partLength) { 2113 var formattedPart = "", 2114 ch; 2115 2116 while (partLength && (ch = formattedInn.shift())) { 2117 formattedPart += ch; 2118 if (rDigit.test(ch)) partLength--; 2119 } 2120 2121 return formattedPart; 2122 }); 2123 formattedInn = 2124 innParts.join( 2125 '<span class="' + 2126 that.classes.subtext_delimiter + 2127 '"></span>' 2128 ) + formattedInn.join(""); 2129 } 2130 2131 return formattedInn; 2132 } 2133 } 2134}; 2135 2136var EMAIL_TYPE = { 2137 urlSuffix: "email", 2138 noSuggestionsHint: false, 2139 matchers: [matchers.matchByNormalizedQuery()], 2140 isQueryRequestable: function(query) { 2141 return this.options.suggest_local || query.indexOf("@") >= 0; 2142 } 2143}; 2144 2145var BANK_TYPE = { 2146 urlSuffix: "bank", 2147 noSuggestionsHint: "ÐеизвеÑÑнÑй банк", 2148 matchers: [ 2149 matchers.matchByFields( 2150 // These fields of suggestion's `data` used by by-words matcher 2151 { 2152 value: null, 2153 "data.bic": null, 2154 "data.swift": null, 2155 } 2156 ), 2157 ], 2158 dataComponents: ADDRESS_COMPONENTS, 2159 enrichmentEnabled: true, 2160 enrichmentMethod: "findById", 2161 enrichmentParams: { 2162 count: 1, 2163 }, 2164 getEnrichmentQuery: function(suggestion) { 2165 return suggestion.data.bic; 2166 }, 2167 geoEnabled: true, 2168 formatResult: function(value, currentValue, suggestion, options) { 2169 var that = this, 2170 formattedBIC = that.highlightMatches( 2171 object_util.getDeepValue(suggestion.data, "bic"), 2172 currentValue, 2173 suggestion 2174 ), 2175 address = 2176 object_util.getDeepValue(suggestion.data, "address.value") || 2177 ""; 2178 2179 value = that.highlightMatches(value, currentValue, suggestion, options); 2180 value = that.wrapFormattedValue(value, suggestion); 2181 2182 if (address) { 2183 address = address.replace(/^\d{6}( Ð ÐССÐЯ)?, /i, ""); 2184 if (that.isMobile) { 2185 // keep only two first words 2186 address = address.replace( 2187 new RegExp( 2188 "^([^" + 2189 WORD_DELIMITERS + 2190 "]+[" + 2191 WORD_DELIMITERS + 2192 "]+[^" + 2193 WORD_DELIMITERS + 2194 "]+).*" 2195 ), 2196 "$1" 2197 ); 2198 } else { 2199 address = that.highlightMatches( 2200 address, 2201 currentValue, 2202 suggestion, 2203 { 2204 unformattableTokens: ADDRESS_STOPWORDS, 2205 } 2206 ); 2207 } 2208 } 2209 2210 if (formattedBIC || address) { 2211 value += 2212 '<div class="' + 2213 that.classes.subtext + 2214 '">' + 2215 '<span class="' + 2216 that.classes.subtext_inline + 2217 '">' + 2218 formattedBIC + 2219 "</span>" + 2220 address + 2221 "</div>"; 2222 } 2223 return value; 2224 }, 2225 formatSelected: function(suggestion) { 2226 return ( 2227 object_util.getDeepValue(suggestion, "data.name.payment") || null 2228 ); 2229 }, 2230}; 2231 2232function Outward(name) { 2233 this.urlSuffix = name.toLowerCase(); 2234 this.noSuggestionsHint = "ÐеизвеÑÑное знаÑение"; 2235 this.matchers = [ 2236 matchers.matchByNormalizedQuery(), 2237 matchers.matchByWords() 2238 ]; 2239} 2240 2241/** 2242 * Type is a bundle of properties: 2243 * - urlSuffix Mandatory. String 2244 * - matchers Mandatory. Array of functions (with optional data bound as a context) that find appropriate suggestion to select 2245 * - `fieldNames` Map fields of suggestion.data to their displayable names 2246 * - `unformattableTokens` Array of strings which should not be highlighted 2247 * - `dataComponents` Array of 'bound's can be set as `bounds` option. Order is important. 2248 * 2249 * flags: 2250 * - `alwaysContinueSelecting` Forbids to hide dropdown after selecting 2251 * - `geoEnabled` Makes to detect client's location for passing it to all requests 2252 * - `enrichmentEnabled` Makes to send additional request when a suggestion is selected 2253 * 2254 * and methods: 2255 * - `isDataComplete` Checks if suggestion.data can be operated as full data of it's type 2256 * - `composeValue` returns string value based on suggestion.data 2257 * - `formatResult` returns html of a suggestion. Overrides default method 2258 * - `formatResultInn` returns html of suggestion.data.inn 2259 * - `isQueryRequestable` checks if query is appropriated for requesting server 2260 * - `formatSelected` returns string to be inserted in textbox 2261 */ 2262 2263var types = { 2264 NAME: NAME_TYPE, 2265 ADDRESS: ADDRESS_TYPE, 2266 FIAS: FIAS_TYPE, 2267 PARTY: PARTY_TYPE, 2268 EMAIL: EMAIL_TYPE, 2269 BANK: BANK_TYPE 2270}; 2271 2272types.get = function(type) { 2273 if (types.hasOwnProperty(type)) { 2274 return types[type]; 2275 } else { 2276 return new Outward(type); 2277 } 2278}; 2279 2280jqapi.extend(DEFAULT_OPTIONS, { 2281 suggest_local: true 2282}); 2283 2284var notificator = { 2285 chains: {}, 2286 2287 on: function(name, method) { 2288 this.get(name).push(method); 2289 return this; 2290 }, 2291 2292 get: function(name) { 2293 var chains = this.chains; 2294 return chains[name] || (chains[name] = []); 2295 } 2296}; 2297 2298var serviceMethods = { 2299 suggest: { 2300 defaultParams: { 2301 type: utils.getDefaultType(), 2302 dataType: "json", 2303 contentType: utils.getDefaultContentType() 2304 }, 2305 addTypeInUrl: true 2306 }, 2307 "iplocate/address": { 2308 defaultParams: { 2309 type: "GET", 2310 dataType: "json" 2311 }, 2312 addTypeInUrl: false 2313 }, 2314 status: { 2315 defaultParams: { 2316 type: "GET", 2317 dataType: "json" 2318 }, 2319 addTypeInUrl: true 2320 }, 2321 findById: { 2322 defaultParams: { 2323 type: utils.getDefaultType(), 2324 dataType: "json", 2325 contentType: utils.getDefaultContentType() 2326 }, 2327 addTypeInUrl: true 2328 } 2329}; 2330 2331var requestModes = { 2332 suggest: { 2333 method: "suggest", 2334 userSelect: true, 2335 updateValue: true, 2336 enrichmentEnabled: true 2337 }, 2338 findById: { 2339 method: "findById", 2340 userSelect: false, 2341 updateValue: false, 2342 enrichmentEnabled: false 2343 } 2344}; 2345 2346function Suggestions(el, options) { 2347 var that = this; 2348 2349 // Shared variables: 2350 that.element = el; 2351 that.el = $(el); 2352 that.suggestions = []; 2353 that.badQueries = []; 2354 that.selectedIndex = -1; 2355 that.currentValue = that.element.value; 2356 that.intervalId = 0; 2357 that.cachedResponse = {}; 2358 that.enrichmentCache = {}; 2359 that.currentRequest = null; 2360 that.inputPhase = $.Deferred(); 2361 that.fetchPhase = $.Deferred(); 2362 that.enrichPhase = $.Deferred(); 2363 that.onChangeTimeout = null; 2364 that.triggering = {}; 2365 that.$wrapper = null; 2366 that.options = $.extend({}, DEFAULT_OPTIONS, options); 2367 that.classes = CLASSES; 2368 that.disabled = false; 2369 that.selection = null; 2370 that.$viewport = $(window); 2371 that.$body = $(document.body); 2372 that.type = null; 2373 that.status = {}; 2374 2375 that.setupElement(); 2376 2377 that.initializer = $.Deferred(); 2378 2379 if (that.el.is(":visible")) { 2380 that.initializer.resolve(); 2381 } else { 2382 that.deferInitialization(); 2383 } 2384 2385 that.initializer.done($.proxy(that.initialize, that)); 2386} 2387 2388Suggestions.prototype = { 2389 // Creation and destruction 2390 2391 initialize: function() { 2392 var that = this; 2393 that.uniqueId = utils.uniqueId("i"); 2394 that.createWrapper(); 2395 that.notify("initialize"); 2396 that.bindWindowEvents(); 2397 that.setOptions(); 2398 that.inferIsMobile(); 2399 that.notify("ready"); 2400 }, 2401 2402 /** 2403 * Initialize when element is firstly interacted 2404 */ 2405 deferInitialization: function() { 2406 var that = this, 2407 events = "mouseover focus keydown", 2408 timer, 2409 callback = function() { 2410 that.initializer.resolve(); 2411 that.enable(); 2412 }; 2413 2414 that.initializer.always(function() { 2415 that.el.off(events, callback); 2416 clearInterval(timer); 2417 }); 2418 2419 that.disabled = true; 2420 that.el.on(events, callback); 2421 timer = setInterval(function() { 2422 if (that.el.is(":visible")) { 2423 callback(); 2424 } 2425 }, that.options.initializeInterval); 2426 }, 2427 2428 isInitialized: function() { 2429 return this.initializer.state() === "resolved"; 2430 }, 2431 2432 dispose: function() { 2433 var that = this; 2434 that.initializer.reject(); 2435 that.notify("dispose"); 2436 that.el.removeData(DATA_ATTR_KEY).removeClass("suggestions-input"); 2437 that.unbindWindowEvents(); 2438 that.removeWrapper(); 2439 that.el.trigger("suggestions-dispose"); 2440 }, 2441 2442 notify: function(chainName) { 2443 var that = this, 2444 args = utils.slice(arguments, 1); 2445 2446 return $.map(notificator.get(chainName), function(method) { 2447 return method.apply(that, args); 2448 }); 2449 }, 2450 2451 createWrapper: function() { 2452 var that = this; 2453 2454 that.$wrapper = $('<div class="suggestions-wrapper"/>'); 2455 that.el.after(that.$wrapper); 2456 2457 that.$wrapper.on( 2458 "mousedown" + EVENT_NS, 2459 $.proxy(that.onMousedown, that) 2460 ); 2461 }, 2462 2463 removeWrapper: function() { 2464 var that = this; 2465 2466 if (that.$wrapper) { 2467 that.$wrapper.remove(); 2468 } 2469 $(that.options.$helpers).off(EVENT_NS); 2470 }, 2471 2472 /** This whole handler is needed to prevent blur event on textbox 2473 * when suggestion is clicked (blur leads to suggestions hide, so we need to prevent it). 2474 * See https://github.com/jquery/jquery-ui/blob/master/ui/autocomplete.js for details 2475 */ 2476 onMousedown: function(e) { 2477 var that = this; 2478 2479 // prevent moving focus out of the text field 2480 e.preventDefault(); 2481 2482 // IE doesn't prevent moving focus even with e.preventDefault() 2483 // so we set a flag to know when we should ignore the blur event 2484 that.cancelBlur = true; 2485 utils.delay(function() { 2486 delete that.cancelBlur; 2487 }); 2488 2489 // clicking on the scrollbar causes focus to shift to the body 2490 // but we can't detect a mouseup or a click immediately afterward 2491 // so we have to track the next mousedown and close the menu if 2492 // the user clicks somewhere outside of the autocomplete 2493 if ($(e.target).closest(".ui-menu-item").length == 0) { 2494 utils.delay(function() { 2495 $(document).one("mousedown", function(e) { 2496 var $elements = that.el 2497 .add(that.$wrapper) 2498 .add(that.options.$helpers); 2499 2500 if (that.options.floating) { 2501 $elements = $elements.add(that.$container); 2502 } 2503 2504 $elements = $elements.filter(function() { 2505 return this === e.target || $.contains(this, e.target); 2506 }); 2507 2508 if (!$elements.length) { 2509 that.hide(); 2510 } 2511 }); 2512 }); 2513 } 2514 }, 2515 2516 bindWindowEvents: function() { 2517 var handler = $.proxy(this.inferIsMobile, this); 2518 this.$viewport.on("resize" + EVENT_NS + this.uniqueId, handler); 2519 }, 2520 2521 unbindWindowEvents: function() { 2522 this.$viewport.off("resize" + EVENT_NS + this.uniqueId); 2523 }, 2524 2525 scrollToTop: function() { 2526 var that = this, 2527 scrollTarget = that.options.scrollOnFocus; 2528 2529 if (scrollTarget === true) { 2530 scrollTarget = that.el; 2531 } 2532 if (scrollTarget instanceof $ && scrollTarget.length > 0) { 2533 $("body,html").animate( 2534 { 2535 scrollTop: scrollTarget.offset().top 2536 }, 2537 "fast" 2538 ); 2539 } 2540 }, 2541 2542 // Configuration methods 2543 2544 setOptions: function(suppliedOptions) { 2545 var that = this; 2546 2547 $.extend(that.options, suppliedOptions); 2548 2549 that["type"] = types.get(that.options["type"]); 2550 2551 // Check mandatory options 2552 $.each( 2553 { 2554 requestMode: requestModes 2555 }, 2556 function(option, available) { 2557 that[option] = available[that.options[option]]; 2558 if (!that[option]) { 2559 that.disable(); 2560 throw "`" + 2561 option + 2562 "` option is incorrect! Must be one of: " + 2563 $.map(available, function(value, name) { 2564 return '"' + name + '"'; 2565 }).join(", "); 2566 } 2567 } 2568 ); 2569 2570 $(that.options.$helpers) 2571 .off(EVENT_NS) 2572 .on("mousedown" + EVENT_NS, $.proxy(that.onMousedown, that)); 2573 2574 if (that.isInitialized()) { 2575 that.notify("setOptions"); 2576 } 2577 }, 2578 2579 // Common public methods 2580 2581 inferIsMobile: function() { 2582 this.isMobile = this.$viewport.width() <= this.options.mobileWidth; 2583 }, 2584 2585 clearCache: function() { 2586 this.cachedResponse = {}; 2587 this.enrichmentCache = {}; 2588 this.badQueries = []; 2589 }, 2590 2591 clear: function() { 2592 var that = this, 2593 currentSelection = that.selection; 2594 2595 if (that.isInitialized()) { 2596 that.clearCache(); 2597 that.currentValue = ""; 2598 that.selection = null; 2599 that.hide(); 2600 that.suggestions = []; 2601 that.el.val(""); 2602 that.el.trigger("suggestions-clear"); 2603 that.notify("clear"); 2604 that.trigger("InvalidateSelection", currentSelection); 2605 } 2606 }, 2607 2608 disable: function() { 2609 var that = this; 2610 2611 that.disabled = true; 2612 that.abortRequest(); 2613 if (that.visible) { 2614 that.hide(); 2615 } 2616 }, 2617 2618 enable: function() { 2619 this.disabled = false; 2620 }, 2621 2622 isUnavailable: function() { 2623 return this.disabled; 2624 }, 2625 2626 update: function() { 2627 var that = this, 2628 query = that.el.val(); 2629 2630 if (that.isInitialized()) { 2631 that.currentValue = query; 2632 if (that.isQueryRequestable(query)) { 2633 that.updateSuggestions(query); 2634 } else { 2635 that.hide(); 2636 } 2637 } 2638 }, 2639 2640 setSuggestion: function(suggestion) { 2641 var that = this, 2642 data, 2643 value; 2644 2645 if ($.isPlainObject(suggestion) && $.isPlainObject(suggestion.data)) { 2646 suggestion = $.extend(true, {}, suggestion); 2647 2648 if ( 2649 that.isUnavailable() && 2650 that.initializer && 2651 that.initializer.state() === "pending" 2652 ) { 2653 that.initializer.resolve(); 2654 that.enable(); 2655 } 2656 2657 if (that.bounds.own.length) { 2658 that.checkValueBounds(suggestion); 2659 data = that.copyDataComponents( 2660 suggestion.data, 2661 that.bounds.all 2662 ); 2663 if (suggestion.data.kladr_id) { 2664 data.kladr_id = that.getBoundedKladrId( 2665 suggestion.data.kladr_id, 2666 that.bounds.all 2667 ); 2668 } 2669 suggestion.data = data; 2670 } 2671 2672 that.selection = suggestion; 2673 2674 // `that.suggestions` required by `that.getSuggestionValue` and must be set before 2675 that.suggestions = [suggestion]; 2676 value = that.getSuggestionValue(suggestion) || ""; 2677 that.currentValue = value; 2678 that.el.val(value); 2679 that.abortRequest(); 2680 that.el.trigger("suggestions-set"); 2681 } 2682 }, 2683 2684 /** 2685 * Fetch full object for current INPUT's value 2686 * if no suitable object found, clean input element 2687 */ 2688 fixData: function() { 2689 var that = this, 2690 fullQuery = that.extendedCurrentValue(), 2691 currentValue = that.el.val(), 2692 resolver = $.Deferred(); 2693 2694 resolver 2695 .done(function(suggestion) { 2696 that.selectSuggestion(suggestion, 0, currentValue, { 2697 hasBeenEnriched: true 2698 }); 2699 that.el.trigger("suggestions-fixdata", suggestion); 2700 }) 2701 .fail(function() { 2702 that.selection = null; 2703 that.el.trigger("suggestions-fixdata"); 2704 }); 2705 2706 if (that.isQueryRequestable(fullQuery)) { 2707 that.currentValue = fullQuery; 2708 that.getSuggestions(fullQuery, { 2709 count: 1, 2710 from_bound: null, 2711 to_bound: null 2712 }) 2713 .done(function(suggestions) { 2714 // data fetched 2715 var suggestion = suggestions[0]; 2716 if (suggestion) { 2717 resolver.resolve(suggestion); 2718 } else { 2719 resolver.reject(); 2720 } 2721 }) 2722 .fail(function() { 2723 // no data fetched 2724 resolver.reject(); 2725 }); 2726 } else { 2727 resolver.reject(); 2728 } 2729 }, 2730 2731 // Querying related methods 2732 2733 /** 2734 * Looks up parent instances 2735 * @returns {String} current value prepended by parents' values 2736 */ 2737 extendedCurrentValue: function() { 2738 var that = this, 2739 parentInstance = that.getParentInstance(), 2740 parentValue = 2741 parentInstance && parentInstance.extendedCurrentValue(), 2742 currentValue = $.trim(that.el.val()); 2743 2744 return utils.compact([parentValue, currentValue]).join(" "); 2745 }, 2746 2747 getAjaxParams: function(method, custom) { 2748 var that = this, 2749 token = $.trim(that.options.token), 2750 partner = $.trim(that.options.partner), 2751 serviceUrl = that.options.serviceUrl, 2752 url = that.options.url, 2753 serviceMethod = serviceMethods[method], 2754 params = $.extend( 2755 { 2756 timeout: that.options.timeout 2757 }, 2758 serviceMethod.defaultParams 2759 ), 2760 headers = {}; 2761 2762 if (url) { 2763 serviceUrl = url; 2764 } else { 2765 if (!/\/$/.test(serviceUrl)) { 2766 serviceUrl += "/"; 2767 } 2768 serviceUrl += method; 2769 if (serviceMethod.addTypeInUrl) { 2770 serviceUrl += "/" + that.type.urlSuffix; 2771 } 2772 } 2773 2774 serviceUrl = utils.fixURLProtocol(serviceUrl); 2775 2776 if ($.support.cors) { 2777 // for XMLHttpRequest put token in header 2778 if (token) { 2779 headers["Authorization"] = "Token " + token; 2780 } 2781 if (partner) { 2782 headers["X-Partner"] = partner; 2783 } 2784 headers["X-Version"] = Suggestions.version; 2785 if (!params.headers) { 2786 params.headers = {}; 2787 } 2788 if (!params.xhrFields) { 2789 params.xhrFields = {}; 2790 } 2791 $.extend(params.headers, that.options.headers, headers); 2792 // server sets Access-Control-Allow-Origin: * 2793 // which requires no credentials 2794 params.xhrFields.withCredentials = false; 2795 } else { 2796 // for XDomainRequest put token into URL 2797 if (token) { 2798 headers["token"] = token; 2799 } 2800 if (partner) { 2801 headers["partner"] = partner; 2802 } 2803 headers["version"] = Suggestions.version; 2804 serviceUrl = utils.addUrlParams(serviceUrl, headers); 2805 } 2806 2807 params.url = serviceUrl; 2808 2809 return $.extend(params, custom); 2810 }, 2811 2812 isQueryRequestable: function(query) { 2813 var that = this, 2814 result; 2815 2816 result = query.length >= that.options.minChars; 2817 2818 if (result && that.type.isQueryRequestable) { 2819 result = that.type.isQueryRequestable.call(that, query); 2820 } 2821 2822 return result; 2823 }, 2824 2825 constructRequestParams: function(query, customParams) { 2826 var that = this, 2827 options = that.options, 2828 params = $.isFunction(options.params) 2829 ? options.params.call(that.element, query) 2830 : $.extend({}, options.params); 2831 2832 if (that.type.constructRequestParams) { 2833 $.extend(params, that.type.constructRequestParams.call(that)); 2834 } 2835 $.each(that.notify("requestParams"), function(i, hookParams) { 2836 $.extend(params, hookParams); 2837 }); 2838 params[options.paramName] = query; 2839 if ($.isNumeric(options.count) && options.count > 0) { 2840 params.count = options.count; 2841 } 2842 if (options.language) { 2843 params.language = options.language; 2844 } 2845 2846 return $.extend(params, customParams); 2847 }, 2848 2849 updateSuggestions: function(query) { 2850 var that = this; 2851 2852 that.fetchPhase = that 2853 .getSuggestions(query) 2854 .done(function(suggestions) { 2855 that.assignSuggestions(suggestions, query); 2856 }); 2857 }, 2858 2859 /** 2860 * Get suggestions from cache or from server 2861 * @param {String} query 2862 * @param {Object} customParams parameters specified here will be passed to request body 2863 * @param {Object} requestOptions 2864 * @param {Boolean} [requestOptions.noCallbacks] flag, request competence callbacks will not be invoked 2865 * @param {Boolean} [requestOptions.useEnrichmentCache] 2866 * @return {$.Deferred} waiter which is to be resolved with suggestions as argument 2867 */ 2868 getSuggestions: function(query, customParams, requestOptions) { 2869 var response, 2870 that = this, 2871 options = that.options, 2872 noCallbacks = requestOptions && requestOptions.noCallbacks, 2873 useEnrichmentCache = 2874 requestOptions && requestOptions.useEnrichmentCache, 2875 method = 2876 (requestOptions && requestOptions.method) || 2877 that.requestMode.method, 2878 params = that.constructRequestParams(query, customParams), 2879 cacheKey = $.param(params || {}), 2880 resolver = $.Deferred(); 2881 2882 response = that.cachedResponse[cacheKey]; 2883 if (response && $.isArray(response.suggestions)) { 2884 resolver.resolve(response.suggestions); 2885 } else { 2886 if (that.isBadQuery(query)) { 2887 resolver.reject(); 2888 } else { 2889 if ( 2890 !noCallbacks && 2891 options.onSearchStart.call(that.element, params) === false 2892 ) { 2893 resolver.reject(); 2894 } else { 2895 that.doGetSuggestions(params, method) 2896 .done(function(response) { 2897 // if response is correct and current value has not been changed 2898 if ( 2899 that.processResponse(response) && 2900 query == that.currentValue 2901 ) { 2902 // Cache results if cache is not disabled: 2903 if (!options.noCache) { 2904 if (useEnrichmentCache) { 2905 that.enrichmentCache[query] = 2906 response.suggestions[0]; 2907 } else { 2908 that.enrichResponse(response, query); 2909 that.cachedResponse[ 2910 cacheKey 2911 ] = response; 2912 if ( 2913 options.preventBadQueries && 2914 response.suggestions.length === 0 2915 ) { 2916 that.badQueries.push(query); 2917 } 2918 } 2919 } 2920 2921 resolver.resolve(response.suggestions); 2922 } else { 2923 resolver.reject(); 2924 } 2925 if (!noCallbacks) { 2926 options.onSearchComplete.call( 2927 that.element, 2928 query, 2929 response.suggestions 2930 ); 2931 } 2932 }) 2933 .fail(function(jqXHR, textStatus, errorThrown) { 2934 resolver.reject(); 2935 if (!noCallbacks && textStatus !== "abort") { 2936 options.onSearchError.call( 2937 that.element, 2938 query, 2939 jqXHR, 2940 textStatus, 2941 errorThrown 2942 ); 2943 } 2944 }); 2945 } 2946 } 2947 } 2948 return resolver; 2949 }, 2950 2951 /** 2952 * Sends an AJAX request to server suggest method. 2953 * @param {Object} params request params 2954 * @returns {$.Deferred} response promise 2955 */ 2956 doGetSuggestions: function(params, method) { 2957 var that = this, 2958 request = $.ajax( 2959 that.getAjaxParams(method, { data: utils.serialize(params) }) 2960 ); 2961 2962 that.abortRequest(); 2963 that.currentRequest = request; 2964 that.notify("request"); 2965 2966 request.always(function() { 2967 that.currentRequest = null; 2968 that.notify("request"); 2969 }); 2970 2971 return request; 2972 }, 2973 2974 isBadQuery: function(q) { 2975 if (!this.options.preventBadQueries) { 2976 return false; 2977 } 2978 2979 var result = false; 2980 $.each(this.badQueries, function(i, query) { 2981 return !(result = q.indexOf(query) === 0); 2982 }); 2983 return result; 2984 }, 2985 2986 abortRequest: function() { 2987 var that = this; 2988 2989 if (that.currentRequest) { 2990 that.currentRequest.abort(); 2991 } 2992 }, 2993 2994 /** 2995 * Checks response format and data 2996 * @return {Boolean} response contains acceptable data 2997 */ 2998 processResponse: function(response) { 2999 var that = this, 3000 suggestions; 3001 3002 if (!response || !$.isArray(response.suggestions)) { 3003 return false; 3004 } 3005 3006 that.verifySuggestionsFormat(response.suggestions); 3007 that.setUnrestrictedValues(response.suggestions); 3008 3009 if ($.isFunction(that.options.onSuggestionsFetch)) { 3010 suggestions = that.options.onSuggestionsFetch.call( 3011 that.element, 3012 response.suggestions 3013 ); 3014 if ($.isArray(suggestions)) { 3015 response.suggestions = suggestions; 3016 } 3017 } 3018 3019 return true; 3020 }, 3021 3022 verifySuggestionsFormat: function(suggestions) { 3023 if (typeof suggestions[0] === "string") { 3024 $.each(suggestions, function(i, value) { 3025 suggestions[i] = { value: value, data: null }; 3026 }); 3027 } 3028 }, 3029 3030 /** 3031 * Gets string to set as input value 3032 * 3033 * @param suggestion 3034 * @param {Object} [selectionOptions] 3035 * @param {boolean} selectionOptions.hasBeenEnriched 3036 * @param {boolean} selectionOptions.hasSameValues 3037 * @return {string} 3038 */ 3039 getSuggestionValue: function(suggestion, selectionOptions) { 3040 var that = this, 3041 formatSelected = 3042 that.options.formatSelected || that.type.formatSelected, 3043 hasSameValues = selectionOptions && selectionOptions.hasSameValues, 3044 hasBeenEnriched = 3045 selectionOptions && selectionOptions.hasBeenEnriched, 3046 formattedValue, 3047 typeFormattedValue = null; 3048 3049 if ($.isFunction(formatSelected)) { 3050 formattedValue = formatSelected.call(that, suggestion); 3051 } 3052 3053 if (typeof formattedValue !== "string") { 3054 formattedValue = suggestion.value; 3055 3056 if (that.type.getSuggestionValue) { 3057 typeFormattedValue = that.type.getSuggestionValue(that, { 3058 suggestion: suggestion, 3059 hasSameValues: hasSameValues, 3060 hasBeenEnriched: hasBeenEnriched 3061 }); 3062 3063 if (typeFormattedValue !== null) { 3064 formattedValue = typeFormattedValue; 3065 } 3066 } 3067 } 3068 3069 return formattedValue; 3070 }, 3071 3072 hasSameValues: function(suggestion) { 3073 var hasSame = false; 3074 3075 $.each(this.suggestions, function(i, anotherSuggestion) { 3076 if ( 3077 anotherSuggestion.value === suggestion.value && 3078 anotherSuggestion !== suggestion 3079 ) { 3080 hasSame = true; 3081 return false; 3082 } 3083 }); 3084 3085 return hasSame; 3086 }, 3087 3088 assignSuggestions: function(suggestions, query) { 3089 var that = this; 3090 that.suggestions = suggestions; 3091 that.notify("assignSuggestions", query); 3092 }, 3093 3094 shouldRestrictValues: function() { 3095 var that = this; 3096 // treat suggestions value as restricted only if there is one constraint 3097 // and restrict_value is true 3098 return ( 3099 that.options.restrict_value && 3100 that.constraints && 3101 Object.keys(that.constraints).length == 1 3102 ); 3103 }, 3104 3105 /** 3106 * Fills suggestion.unrestricted_value property 3107 */ 3108 setUnrestrictedValues: function(suggestions) { 3109 var that = this, 3110 shouldRestrict = that.shouldRestrictValues(), 3111 label = that.getFirstConstraintLabel(); 3112 3113 $.each(suggestions, function(i, suggestion) { 3114 if (!suggestion.unrestricted_value) { 3115 suggestion.unrestricted_value = shouldRestrict 3116 ? label + ", " + suggestion.value 3117 : suggestion.value; 3118 } 3119 }); 3120 }, 3121 3122 areSuggestionsSame: function(a, b) { 3123 return a && b && a.value === b.value && utils.areSame(a.data, b.data); 3124 }, 3125 3126 getNoSuggestionsHint: function() { 3127 var that = this; 3128 if (that.options.noSuggestionsHint === false) { 3129 return false; 3130 } 3131 return that.options.noSuggestionsHint || that.type.noSuggestionsHint; 3132 } 3133}; 3134 3135/** 3136 * Methods related to INPUT's behavior 3137 */ 3138 3139var methods = { 3140 setupElement: function() { 3141 // Remove autocomplete attribute to prevent native suggestions: 3142 this.el 3143 // if it stops working, see https://stackoverflow.com/q/15738259 3144 // chrome is constantly changing this logic 3145 .attr("autocomplete", "new-password") 3146 .attr("autocorrect", "off") 3147 .attr("autocapitalize", "off") 3148 .attr("spellcheck", "false") 3149 .addClass("suggestions-input") 3150 .css("box-sizing", "border-box"); 3151 }, 3152 3153 bindElementEvents: function() { 3154 var that = this; 3155 3156 that.el.on("keydown" + EVENT_NS, $.proxy(that.onElementKeyDown, that)); 3157 // IE is buggy, it doesn't trigger `input` on text deletion, so use following events 3158 that.el.on( 3159 [ 3160 "keyup" + EVENT_NS, 3161 "cut" + EVENT_NS, 3162 "paste" + EVENT_NS, 3163 "input" + EVENT_NS 3164 ].join(" "), 3165 $.proxy(that.onElementKeyUp, that) 3166 ); 3167 that.el.on("blur" + EVENT_NS, $.proxy(that.onElementBlur, that)); 3168 that.el.on("focus" + EVENT_NS, $.proxy(that.onElementFocus, that)); 3169 }, 3170 3171 unbindElementEvents: function() { 3172 this.el.off(EVENT_NS); 3173 }, 3174 3175 onElementBlur: function() { 3176 var that = this; 3177 3178 // suggestion was clicked, blur should be ignored 3179 // see container mousedown handler 3180 if (that.cancelBlur) { 3181 that.cancelBlur = false; 3182 return; 3183 } 3184 3185 if (that.options.triggerSelectOnBlur) { 3186 if (!that.isUnavailable()) { 3187 that.selectCurrentValue({ noSpace: true }).always(function() { 3188 // For NAMEs selecting keeps suggestions list visible, so hide it 3189 that.hide(); 3190 }); 3191 } 3192 } else { 3193 that.hide(); 3194 } 3195 3196 if (that.fetchPhase.abort) { 3197 that.fetchPhase.abort(); 3198 } 3199 }, 3200 3201 onElementFocus: function() { 3202 var that = this; 3203 3204 if (!that.cancelFocus) { 3205 // defer methods to allow browser update input's style before 3206 utils.delay($.proxy(that.completeOnFocus, that)); 3207 } 3208 that.cancelFocus = false; 3209 }, 3210 3211 onElementKeyDown: function(e) { 3212 var that = this; 3213 3214 if (that.isUnavailable()) { 3215 return; 3216 } 3217 3218 if (!that.visible) { 3219 switch (e.which) { 3220 // If suggestions are hidden and user presses arrow down, display suggestions 3221 case KEYS.DOWN: 3222 that.suggest(); 3223 break; 3224 // if no suggestions available and user pressed Enter 3225 case KEYS.ENTER: 3226 if (that.options.triggerSelectOnEnter) { 3227 that.triggerOnSelectNothing(); 3228 } 3229 break; 3230 } 3231 return; 3232 } 3233 3234 switch (e.which) { 3235 case KEYS.ESC: 3236 that.el.val(that.currentValue); 3237 that.hide(); 3238 that.abortRequest(); 3239 break; 3240 3241 case KEYS.TAB: 3242 if (that.options.tabDisabled === false) { 3243 return; 3244 } 3245 break; 3246 3247 case KEYS.ENTER: 3248 if (that.options.triggerSelectOnEnter) { 3249 that.selectCurrentValue(); 3250 } 3251 break; 3252 3253 case KEYS.SPACE: 3254 if (that.options.triggerSelectOnSpace && that.isCursorAtEnd()) { 3255 e.preventDefault(); 3256 that.selectCurrentValue({ 3257 continueSelecting: true, 3258 dontEnrich: true 3259 }).fail(function() { 3260 // If all data fetched but nothing selected 3261 that.currentValue += " "; 3262 that.el.val(that.currentValue); 3263 that.proceedChangedValue(); 3264 }); 3265 } 3266 return; 3267 case KEYS.UP: 3268 that.moveUp(); 3269 break; 3270 case KEYS.DOWN: 3271 that.moveDown(); 3272 break; 3273 default: 3274 return; 3275 } 3276 3277 // Cancel event if function did not return: 3278 e.stopImmediatePropagation(); 3279 e.preventDefault(); 3280 }, 3281 3282 onElementKeyUp: function(e) { 3283 var that = this; 3284 3285 if (that.isUnavailable()) { 3286 return; 3287 } 3288 3289 switch (e.which) { 3290 case KEYS.UP: 3291 case KEYS.DOWN: 3292 case KEYS.ENTER: 3293 return; 3294 } 3295 3296 // Cancel pending change 3297 clearTimeout(that.onChangeTimeout); 3298 that.inputPhase.reject(); 3299 3300 if (that.currentValue !== that.el.val()) { 3301 that.proceedChangedValue(); 3302 } 3303 }, 3304 3305 proceedChangedValue: function() { 3306 var that = this; 3307 3308 // Cancel fetching, because it became obsolete 3309 that.abortRequest(); 3310 3311 that.inputPhase = $.Deferred().done($.proxy(that.onValueChange, that)); 3312 3313 if (that.options.deferRequestBy > 0) { 3314 // Defer lookup in case when value changes very quickly: 3315 that.onChangeTimeout = utils.delay(function() { 3316 that.inputPhase.resolve(); 3317 }, that.options.deferRequestBy); 3318 } else { 3319 that.inputPhase.resolve(); 3320 } 3321 }, 3322 3323 onValueChange: function() { 3324 var that = this, 3325 currentSelection; 3326 3327 if (that.selection) { 3328 currentSelection = that.selection; 3329 that.selection = null; 3330 that.trigger("InvalidateSelection", currentSelection); 3331 } 3332 3333 that.selectedIndex = -1; 3334 3335 that.update(); 3336 that.notify("valueChange"); 3337 }, 3338 3339 completeOnFocus: function() { 3340 var that = this; 3341 3342 if (that.isUnavailable()) { 3343 return; 3344 } 3345 3346 if (that.isElementFocused()) { 3347 that.update(); 3348 if (that.isMobile) { 3349 that.setCursorAtEnd(); 3350 that.scrollToTop(); 3351 } 3352 } 3353 }, 3354 3355 isElementFocused: function() { 3356 return document.activeElement === this.element; 3357 }, 3358 3359 isElementDisabled: function() { 3360 return Boolean( 3361 this.element.getAttribute("disabled") || 3362 this.element.getAttribute("readonly") 3363 ); 3364 }, 3365 3366 isCursorAtEnd: function() { 3367 var that = this, 3368 valLength = that.el.val().length, 3369 selectionStart, 3370 range; 3371 3372 // `selectionStart` and `selectionEnd` are not supported by some input types 3373 try { 3374 selectionStart = that.element.selectionStart; 3375 if (typeof selectionStart === "number") { 3376 return selectionStart === valLength; 3377 } 3378 } catch (ex) {} 3379 3380 if (document.selection) { 3381 range = document.selection.createRange(); 3382 range.moveStart("character", -valLength); 3383 return valLength === range.text.length; 3384 } 3385 return true; 3386 }, 3387 3388 setCursorAtEnd: function() { 3389 var element = this.element; 3390 3391 // `selectionStart` and `selectionEnd` are not supported by some input types 3392 try { 3393 element.selectionEnd = element.selectionStart = 3394 element.value.length; 3395 element.scrollLeft = element.scrollWidth; 3396 } catch (ex) { 3397 element.value = element.value; 3398 } 3399 } 3400}; 3401 3402$.extend(Suggestions.prototype, methods); 3403 3404notificator 3405 .on("initialize", methods.bindElementEvents) 3406 .on("dispose", methods.unbindElementEvents); 3407 3408/** 3409 * Methods related to plugin's authorization on server 3410 */ 3411 3412// keys are "[type][token]" 3413var statusRequests = {}; 3414 3415function resetTokens() { 3416 utils.each(statusRequests, function(req) { 3417 req.abort(); 3418 }); 3419 statusRequests = {}; 3420} 3421 3422resetTokens(); 3423 3424var methods$1 = { 3425 checkStatus: function() { 3426 var that = this, 3427 token = (that.options.token && that.options.token.trim()) || "", 3428 requestKey = that.options.type + token, 3429 request = statusRequests[requestKey]; 3430 3431 if (!request) { 3432 request = statusRequests[requestKey] = jqapi.ajax( 3433 that.getAjaxParams("status") 3434 ); 3435 } 3436 3437 request 3438 .done(function(status, textStatus, request) { 3439 if (status.search) { 3440 var plan = request.getResponseHeader("X-Plan"); 3441 status.plan = plan; 3442 jqapi.extend(that.status, status); 3443 } else { 3444 triggerError("Service Unavailable"); 3445 } 3446 }) 3447 .fail(function() { 3448 triggerError(request.statusText); 3449 }); 3450 3451 function triggerError(errorThrown) { 3452 // If unauthorized 3453 if (utils.isFunction(that.options.onSearchError)) { 3454 that.options.onSearchError.call( 3455 that.element, 3456 null, 3457 request, 3458 "error", 3459 errorThrown 3460 ); 3461 } 3462 } 3463 } 3464}; 3465 3466Suggestions.resetTokens = resetTokens; 3467 3468jqapi.extend(Suggestions.prototype, methods$1); 3469 3470notificator.on("setOptions", methods$1.checkStatus); 3471 3472//export { methods, resetTokens }; 3473 3474var locationRequest; 3475var defaultGeoLocation = true; 3476 3477function resetLocation() { 3478 locationRequest = null; 3479 DEFAULT_OPTIONS.geoLocation = defaultGeoLocation; 3480} 3481 3482var methods$2 = { 3483 checkLocation: function() { 3484 var that = this, 3485 providedLocation = that.options.geoLocation; 3486 3487 if (!that.type.geoEnabled || !providedLocation) { 3488 return; 3489 } 3490 3491 that.geoLocation = $.Deferred(); 3492 if ($.isPlainObject(providedLocation) || $.isArray(providedLocation)) { 3493 that.geoLocation.resolve(providedLocation); 3494 } else { 3495 if (!locationRequest) { 3496 locationRequest = $.ajax( 3497 that.getAjaxParams("iplocate/address") 3498 ); 3499 } 3500 3501 locationRequest 3502 .done(function(resp) { 3503 var locationData = 3504 resp && resp.location && resp.location.data; 3505 if (locationData && locationData.kladr_id) { 3506 that.geoLocation.resolve({ 3507 kladr_id: locationData.kladr_id 3508 }); 3509 } else { 3510 that.geoLocation.reject(); 3511 } 3512 }) 3513 .fail(function() { 3514 that.geoLocation.reject(); 3515 }); 3516 } 3517 }, 3518 3519 /** 3520 * Public method to get `geoLocation` promise 3521 * @returns {$.Deferred} 3522 */ 3523 getGeoLocation: function() { 3524 return this.geoLocation; 3525 }, 3526 3527 constructParams: function() { 3528 var that = this, 3529 params = {}; 3530 3531 if ( 3532 that.geoLocation && 3533 $.isFunction(that.geoLocation.promise) && 3534 that.geoLocation.state() == "resolved" 3535 ) { 3536 that.geoLocation.done(function(locationData) { 3537 params["locations_boost"] = $.makeArray(locationData); 3538 }); 3539 } 3540 3541 return params; 3542 } 3543}; 3544 3545// Disable this feature when GET method used. See SUG-202 3546if (utils.getDefaultType() != "GET") { 3547 $.extend(DEFAULT_OPTIONS, { 3548 geoLocation: defaultGeoLocation 3549 }); 3550 3551 $.extend(Suggestions, { 3552 resetLocation: resetLocation 3553 }); 3554 3555 $.extend(Suggestions.prototype, { 3556 getGeoLocation: methods$2.getGeoLocation 3557 }); 3558 3559 notificator 3560 .on("setOptions", methods$2.checkLocation) 3561 .on("requestParams", methods$2.constructParams); 3562} 3563 3564var methods$3 = { 3565 enrichSuggestion: function(suggestion, selectionOptions) { 3566 var that = this, 3567 resolver = $.Deferred(); 3568 3569 if ( 3570 !that.options.enrichmentEnabled || 3571 !that.type.enrichmentEnabled || 3572 !that.requestMode.enrichmentEnabled || 3573 (selectionOptions && selectionOptions.dontEnrich) 3574 ) { 3575 return resolver.resolve(suggestion); 3576 } 3577 3578 // if current suggestion is already enriched, use it 3579 if (suggestion.data && suggestion.data.qc != null) { 3580 return resolver.resolve(suggestion); 3581 } 3582 3583 that.disableDropdown(); 3584 3585 var query = that.type.getEnrichmentQuery(suggestion); 3586 var customParams = that.type.enrichmentParams; 3587 var requestOptions = { 3588 noCallbacks: true, 3589 useEnrichmentCache: true, 3590 method: that.type.enrichmentMethod 3591 }; 3592 3593 // Set `currentValue` to make `processResponse` to consider enrichment response valid 3594 that.currentValue = query; 3595 3596 // prevent request abortion during onBlur 3597 that.enrichPhase = that 3598 .getSuggestions(query, customParams, requestOptions) 3599 .always(function() { 3600 that.enableDropdown(); 3601 }) 3602 .done(function(suggestions) { 3603 var enrichedSuggestion = suggestions && suggestions[0]; 3604 3605 resolver.resolve( 3606 enrichedSuggestion || suggestion, 3607 !!enrichedSuggestion 3608 ); 3609 }) 3610 .fail(function() { 3611 resolver.resolve(suggestion); 3612 }); 3613 3614 return resolver; 3615 }, 3616 3617 /** 3618 * Injects enriched suggestion into response 3619 * @param response 3620 * @param query 3621 */ 3622 enrichResponse: function(response, query) { 3623 var that = this, 3624 enrichedSuggestion = that.enrichmentCache[query]; 3625 3626 if (enrichedSuggestion) { 3627 $.each(response.suggestions, function(i, suggestion) { 3628 if (suggestion.value === query) { 3629 response.suggestions[i] = enrichedSuggestion; 3630 return false; 3631 } 3632 }); 3633 } 3634 } 3635}; 3636 3637$.extend(Suggestions.prototype, methods$3); 3638 3639/** 3640 * Methods related to suggestions dropdown list 3641 */ 3642 3643function highlightMatches(chunks) { 3644 return $.map(chunks, function(chunk) { 3645 var text = utils.escapeHtml(chunk.text); 3646 3647 if (text && chunk.matched) { 3648 text = "<strong>" + text + "</strong>"; 3649 } 3650 return text; 3651 }).join(""); 3652} 3653 3654function nowrapLinkedParts(formattedStr, nowrapClass) { 3655 var delimitedParts = formattedStr.split(", "); 3656 // string has no delimiters, should not wrap 3657 if (delimitedParts.length === 1) { 3658 return formattedStr; 3659 } 3660 // disable word-wrap inside delimited parts 3661 return $.map(delimitedParts, function(part) { 3662 return '<span class="' + nowrapClass + '">' + part + "</span>"; 3663 }).join(", "); 3664} 3665 3666function hasAnotherSuggestion(suggestions, suggestion) { 3667 var result = false; 3668 3669 $.each(suggestions, function(i, s) { 3670 result = s.value == suggestion.value && s != suggestion; 3671 if (result) { 3672 return false; 3673 } 3674 }); 3675 3676 return result; 3677} 3678 3679var optionsUsed = { 3680 width: "auto", 3681 floating: false 3682}; 3683 3684var methods$4 = { 3685 createContainer: function() { 3686 var that = this, 3687 suggestionSelector = "." + that.classes.suggestion, 3688 options = that.options, 3689 $container = $("<div/>") 3690 .addClass(options.containerClass) 3691 .css({ 3692 display: "none" 3693 }); 3694 3695 that.$container = $container; 3696 3697 $container.on( 3698 "click" + EVENT_NS, 3699 suggestionSelector, 3700 $.proxy(that.onSuggestionClick, that) 3701 ); 3702 }, 3703 3704 showContainer: function() { 3705 this.$container.appendTo( 3706 this.options.floating ? this.$body : this.$wrapper 3707 ); 3708 }, 3709 3710 getContainer: function() { 3711 return this.$container.get(0); 3712 }, 3713 3714 removeContainer: function() { 3715 var that = this; 3716 3717 if (that.options.floating) { 3718 that.$container.remove(); 3719 } 3720 }, 3721 3722 setContainerOptions: function() { 3723 var that = this, 3724 mousedownEvent = "mousedown" + EVENT_NS; 3725 3726 that.$container.off(mousedownEvent); 3727 if (that.options.floating) { 3728 that.$container.on(mousedownEvent, $.proxy(that.onMousedown, that)); 3729 } 3730 }, 3731 3732 /** 3733 * Listen for click event on suggestions list: 3734 */ 3735 onSuggestionClick: function(e) { 3736 var that = this, 3737 $el = $(e.target), 3738 index; 3739 3740 if (!that.dropdownDisabled) { 3741 that.cancelFocus = true; 3742 that.el.focus(); 3743 3744 while ($el.length && !(index = $el.attr("data-index"))) { 3745 $el = $el.closest("." + that.classes.suggestion); 3746 } 3747 3748 if (index && !isNaN(index)) { 3749 that.select(+index); 3750 } 3751 } 3752 }, 3753 3754 // Dropdown UI methods 3755 3756 getSuggestionsItems: function() { 3757 return this.$container.children("." + this.classes.suggestion); 3758 }, 3759 3760 toggleDropdownEnabling: function(enable) { 3761 this.dropdownDisabled = !enable; 3762 this.$container.attr("disabled", !enable); 3763 }, 3764 3765 disableDropdown: function() { 3766 this.toggleDropdownEnabling(false); 3767 }, 3768 3769 enableDropdown: function() { 3770 this.toggleDropdownEnabling(true); 3771 }, 3772 3773 /** 3774 * Shows if there are any suggestions besides currently selected 3775 * @returns {boolean} 3776 */ 3777 hasSuggestionsToChoose: function() { 3778 var that = this; 3779 3780 return ( 3781 that.suggestions.length > 1 || 3782 (that.suggestions.length === 1 && 3783 (!that.selection || 3784 $.trim(that.suggestions[0].value) !== 3785 $.trim(that.selection.value))) 3786 ); 3787 }, 3788 3789 suggest: function() { 3790 var that = this, 3791 options = that.options, 3792 formatResult, 3793 html = []; 3794 3795 if (!that.requestMode.userSelect) { 3796 return; 3797 } 3798 3799 // еÑли неÑего показÑваÑÑ, Ñо ÑообÑаем об ÑÑом 3800 if (!that.hasSuggestionsToChoose()) { 3801 if (that.suggestions.length) { 3802 that.hide(); 3803 return; 3804 } else { 3805 var noSuggestionsHint = that.getNoSuggestionsHint(); 3806 if (noSuggestionsHint) { 3807 html.push( 3808 '<div class="' + 3809 that.classes.hint + 3810 '">' + 3811 noSuggestionsHint + 3812 "</div>" 3813 ); 3814 } else { 3815 that.hide(); 3816 return; 3817 } 3818 } 3819 } else { 3820 // Build hint html 3821 if (options.hint && that.suggestions.length) { 3822 html.push( 3823 '<div class="' + 3824 that.classes.hint + 3825 '">' + 3826 options.hint + 3827 "</div>" 3828 ); 3829 } 3830 that.selectedIndex = -1; 3831 // Build suggestions inner HTML: 3832 that.suggestions.forEach(function(suggestion, i) { 3833 if (suggestion == that.selection) { 3834 that.selectedIndex = i; 3835 } 3836 that.buildSuggestionHtml(suggestion, i, html); 3837 }); 3838 } 3839 3840 html.push('<div class="' + CLASSES.promo + '"></div>'); 3841 html.push("</div>"); 3842 3843 that.$container.html(html.join("")); 3844 3845 // Select first value by default: 3846 if (options.autoSelectFirst && that.selectedIndex === -1) { 3847 that.selectedIndex = 0; 3848 } 3849 if (that.selectedIndex !== -1) { 3850 that.getSuggestionsItems() 3851 .eq(that.selectedIndex) 3852 .addClass(that.classes.selected); 3853 } 3854 3855 if ($.isFunction(options.beforeRender)) { 3856 options.beforeRender.call(that.element, that.$container); 3857 } 3858 3859 that.$container.show(); 3860 that.visible = true; 3861 }, 3862 3863 buildSuggestionHtml: function(suggestion, ordinal, html) { 3864 html.push( 3865 '<div class="' + 3866 this.classes.suggestion + 3867 '" data-index="' + 3868 ordinal + 3869 '">' 3870 ); 3871 3872 var formatResult = 3873 this.options.formatResult || 3874 this.type.formatResult || 3875 this.formatResult; 3876 html.push( 3877 formatResult.call( 3878 this, 3879 suggestion.value, 3880 this.currentValue, 3881 suggestion, 3882 { 3883 unformattableTokens: this.type.unformattableTokens 3884 } 3885 ) 3886 ); 3887 3888 var labels = this.makeSuggestionLabel(this.suggestions, suggestion); 3889 if (labels) { 3890 html.push( 3891 '<span class="' + 3892 this.classes.subtext_label + 3893 '">' + 3894 utils.escapeHtml(labels) + 3895 "</span>" 3896 ); 3897 } 3898 html.push("</div>"); 3899 }, 3900 3901 wrapFormattedValue: function(value, suggestion) { 3902 var that = this, 3903 status = utils.getDeepValue(suggestion.data, "state.status"); 3904 3905 return ( 3906 '<span class="' + 3907 that.classes.value + 3908 '"' + 3909 (status ? ' data-suggestion-status="' + status + '"' : "") + 3910 ">" + 3911 value + 3912 "</span>" 3913 ); 3914 }, 3915 3916 formatResult: function(value, currentValue, suggestion, options) { 3917 var that = this; 3918 3919 value = that.highlightMatches(value, currentValue, suggestion, options); 3920 3921 return that.wrapFormattedValue(value, suggestion); 3922 }, 3923 3924 /** 3925 * Makes HTML contents for suggestion item 3926 * @param {String} value string to be displayed as a value 3927 * @param {String} currentValue contents of the textbox 3928 * @param suggestion whole suggestion object with displaying value and other fields 3929 * @param {Object} [options] set of flags: 3930 * `unformattableTokens` - array of search tokens, that are not to be highlighted 3931 * `maxLength` - if set, `value` is limited by this length 3932 * @returns {String} HTML to be inserted in the list 3933 */ 3934 highlightMatches: function(value, currentValue, suggestion, options) { 3935 var that = this, 3936 chunks = [], 3937 unformattableTokens = options && options.unformattableTokens, 3938 maxLength = options && options.maxLength, 3939 tokens, 3940 tokenMatchers, 3941 preferredTokens, 3942 rWords = utils.reWordExtractor(), 3943 match, 3944 word, 3945 i, 3946 chunk, 3947 formattedStr; 3948 3949 if (!value) return ""; 3950 3951 tokens = text_util.tokenize(currentValue, unformattableTokens); 3952 3953 tokenMatchers = $.map(tokens, function(token) { 3954 return new RegExp( 3955 "^((.*)([" + 3956 WORD_PARTS_DELIMITERS + 3957 "]+))?" + 3958 "(" + 3959 utils.escapeRegExChars(token) + 3960 ")" + 3961 "([^" + 3962 WORD_PARTS_DELIMITERS + 3963 "]*[" + 3964 WORD_PARTS_DELIMITERS + 3965 "]*)", 3966 "i" 3967 ); 3968 }); 3969 3970 // parse string by words 3971 while ((match = rWords.exec(value)) && match[0]) { 3972 word = match[1]; 3973 chunks.push({ 3974 text: word, 3975 3976 // upper case means a word is a name and can be highlighted even if presents in unformattableTokens 3977 hasUpperCase: word.toLowerCase() !== word, 3978 formatted: utils.formatToken(word), 3979 matchable: true 3980 }); 3981 if (match[2]) { 3982 chunks.push({ 3983 text: match[2] 3984 }); 3985 } 3986 } 3987 3988 // use simple loop because length can change 3989 for (i = 0; i < chunks.length; i++) { 3990 chunk = chunks[i]; 3991 if ( 3992 chunk.matchable && 3993 !chunk.matched && 3994 ($.inArray(chunk.formatted, unformattableTokens) === -1 || 3995 chunk.hasUpperCase) 3996 ) { 3997 $.each(tokenMatchers, function(j, matcher) { 3998 var tokenMatch = matcher.exec(chunk.formatted), 3999 length, 4000 nextIndex = i + 1; 4001 4002 if (tokenMatch) { 4003 tokenMatch = { 4004 before: tokenMatch[1] || "", 4005 beforeText: tokenMatch[2] || "", 4006 beforeDelimiter: tokenMatch[3] || "", 4007 text: tokenMatch[4] || "", 4008 after: tokenMatch[5] || "" 4009 }; 4010 4011 if (tokenMatch.before) { 4012 // insert chunk before current 4013 chunks.splice( 4014 i, 4015 0, 4016 { 4017 text: chunk.text.substr( 4018 0, 4019 tokenMatch.beforeText.length 4020 ), 4021 formatted: tokenMatch.beforeText, 4022 matchable: true 4023 }, 4024 { 4025 text: tokenMatch.beforeDelimiter 4026 } 4027 ); 4028 nextIndex += 2; 4029 4030 length = tokenMatch.before.length; 4031 chunk.text = chunk.text.substr(length); 4032 chunk.formatted = chunk.formatted.substr(length); 4033 i--; 4034 } 4035 4036 length = 4037 tokenMatch.text.length + tokenMatch.after.length; 4038 if (chunk.formatted.length > length) { 4039 chunks.splice(nextIndex, 0, { 4040 text: chunk.text.substr(length), 4041 formatted: chunk.formatted.substr(length), 4042 matchable: true 4043 }); 4044 chunk.text = chunk.text.substr(0, length); 4045 chunk.formatted = chunk.formatted.substr(0, length); 4046 } 4047 4048 if (tokenMatch.after) { 4049 length = tokenMatch.text.length; 4050 chunks.splice(nextIndex, 0, { 4051 text: chunk.text.substr(length), 4052 formatted: chunk.formatted.substr(length) 4053 }); 4054 chunk.text = chunk.text.substr(0, length); 4055 chunk.formatted = chunk.formatted.substr(0, length); 4056 } 4057 chunk.matched = true; 4058 return false; 4059 } 4060 }); 4061 } 4062 } 4063 4064 if (maxLength) { 4065 for (i = 0; i < chunks.length && maxLength >= 0; i++) { 4066 chunk = chunks[i]; 4067 maxLength -= chunk.text.length; 4068 if (maxLength < 0) { 4069 chunk.text = 4070 chunk.text.substr(0, chunk.text.length + maxLength) + 4071 "..."; 4072 } 4073 } 4074 chunks.length = i; 4075 } 4076 4077 formattedStr = highlightMatches(chunks); 4078 return nowrapLinkedParts(formattedStr, that.classes.nowrap); 4079 }, 4080 4081 makeSuggestionLabel: function(suggestions, suggestion) { 4082 var that = this, 4083 fieldNames = that.type.fieldNames, 4084 nameData = {}, 4085 rWords = utils.reWordExtractor(), 4086 match, 4087 word, 4088 labels = []; 4089 4090 if ( 4091 fieldNames && 4092 hasAnotherSuggestion(suggestions, suggestion) && 4093 suggestion.data 4094 ) { 4095 $.each(fieldNames, function(field) { 4096 var value = suggestion.data[field]; 4097 if (value) { 4098 nameData[field] = utils.formatToken(value); 4099 } 4100 }); 4101 4102 if (!$.isEmptyObject(nameData)) { 4103 while ( 4104 (match = rWords.exec( 4105 utils.formatToken(suggestion.value) 4106 )) && 4107 (word = match[1]) 4108 ) { 4109 $.each(nameData, function(i, value) { 4110 if (value == word) { 4111 labels.push(fieldNames[i]); 4112 delete nameData[i]; 4113 return false; 4114 } 4115 }); 4116 } 4117 4118 if (labels.length) { 4119 return labels.join(", "); 4120 } 4121 } 4122 } 4123 }, 4124 4125 hide: function() { 4126 var that = this; 4127 that.visible = false; 4128 that.selectedIndex = -1; 4129 that.$container.hide().empty(); 4130 }, 4131 4132 activate: function(index) { 4133 var that = this, 4134 $activeItem, 4135 selected = that.classes.selected, 4136 $children; 4137 4138 if (!that.dropdownDisabled) { 4139 $children = that.getSuggestionsItems(); 4140 4141 $children.removeClass(selected); 4142 4143 that.selectedIndex = index; 4144 4145 if ( 4146 that.selectedIndex !== -1 && 4147 $children.length > that.selectedIndex 4148 ) { 4149 $activeItem = $children.eq(that.selectedIndex); 4150 $activeItem.addClass(selected); 4151 return $activeItem; 4152 } 4153 } 4154 4155 return null; 4156 }, 4157 4158 deactivate: function(restoreValue) { 4159 var that = this; 4160 4161 if (!that.dropdownDisabled) { 4162 that.selectedIndex = -1; 4163 that.getSuggestionsItems().removeClass(that.classes.selected); 4164 if (restoreValue) { 4165 that.el.val(that.currentValue); 4166 } 4167 } 4168 }, 4169 4170 moveUp: function() { 4171 var that = this; 4172 4173 if (that.dropdownDisabled) { 4174 return; 4175 } 4176 if (that.selectedIndex === -1) { 4177 if (that.suggestions.length) { 4178 that.adjustScroll(that.suggestions.length - 1); 4179 } 4180 return; 4181 } 4182 4183 if (that.selectedIndex === 0) { 4184 that.deactivate(true); 4185 return; 4186 } 4187 4188 that.adjustScroll(that.selectedIndex - 1); 4189 }, 4190 4191 moveDown: function() { 4192 var that = this; 4193 4194 if (that.dropdownDisabled) { 4195 return; 4196 } 4197 if (that.selectedIndex === that.suggestions.length - 1) { 4198 that.deactivate(true); 4199 return; 4200 } 4201 4202 that.adjustScroll(that.selectedIndex + 1); 4203 }, 4204 4205 adjustScroll: function(index) { 4206 var that = this, 4207 $activeItem = that.activate(index), 4208 itemTop, 4209 itemBottom, 4210 scrollTop = that.$container.scrollTop(), 4211 containerHeight; 4212 4213 if (!$activeItem || !$activeItem.length) { 4214 return; 4215 } 4216 4217 itemTop = $activeItem.position().top; 4218 if (itemTop < 0) { 4219 that.$container.scrollTop(scrollTop + itemTop); 4220 } else { 4221 itemBottom = itemTop + $activeItem.outerHeight(); 4222 containerHeight = that.$container.innerHeight(); 4223 if (itemBottom > containerHeight) { 4224 that.$container.scrollTop( 4225 scrollTop - containerHeight + itemBottom 4226 ); 4227 } 4228 } 4229 4230 that.el.val(that.suggestions[index].value); 4231 } 4232}; 4233 4234$.extend(DEFAULT_OPTIONS, optionsUsed); 4235 4236$.extend(Suggestions.prototype, methods$4); 4237 4238notificator 4239 .on("initialize", methods$4.createContainer) 4240 .on("dispose", methods$4.removeContainer) 4241 .on("setOptions", methods$4.setContainerOptions) 4242 .on("ready", methods$4.showContainer) 4243 .on("assignSuggestions", methods$4.suggest); 4244 4245/** 4246 * Methods related to CONSTRAINTS component 4247 */ 4248var optionsUsed$1 = { 4249 constraints: null, 4250 restrict_value: false 4251}; 4252 4253var fiasParamNames = [ 4254 "country_iso_code", 4255 "region_iso_code", 4256 "region_fias_id", 4257 "area_fias_id", 4258 "city_fias_id", 4259 "city_district_fias_id", 4260 "settlement_fias_id", 4261 "planning_structure_fias_id", 4262 "street_fias_id" 4263]; 4264 4265/** 4266 * Compares two suggestion objects 4267 * @param suggestion 4268 * @param instance other Suggestions instance 4269 */ 4270function belongsToArea(suggestion, instance) { 4271 var parentSuggestion = instance.selection, 4272 result = parentSuggestion && parentSuggestion.data && instance.bounds; 4273 4274 if (result) { 4275 collection_util.each(instance.bounds.all, function(bound, i) { 4276 return (result = 4277 parentSuggestion.data[bound] === suggestion.data[bound]); 4278 }); 4279 } 4280 return result; 4281} 4282 4283/** 4284 * ÐозвÑаÑÐ°ÐµÑ ÐÐÐÐÐ -код, обÑезаннÑй до поÑледнего непÑÑÑого ÑÑÐ¾Ð²Ð½Ñ 4285 * 50 000 040 000 00 â 50 000 040 4286 * @param kladr_id 4287 * @returns {string} 4288 */ 4289function getSignificantKladrId(kladr_id) { 4290 var significantKladrId = kladr_id.replace(/^(\d{2})(\d*?)(0+)$/g, "$1$2"); 4291 var length = significantKladrId.length; 4292 var significantLength = -1; 4293 if (length <= 2) { 4294 significantLength = 2; 4295 } else if (length > 2 && length <= 5) { 4296 significantLength = 5; 4297 } else if (length > 5 && length <= 8) { 4298 significantLength = 8; 4299 } else if (length > 8 && length <= 11) { 4300 significantLength = 11; 4301 } else if (length > 11 && length <= 15) { 4302 significantLength = 15; 4303 } else if (length > 15) { 4304 significantLength = 19; 4305 } 4306 return text_util.padEnd(significantKladrId, significantLength, "0"); 4307} 4308 4309/** 4310 * @param {Object} data fields 4311 * @param {Suggestions} instance 4312 * @constructor 4313 */ 4314var ConstraintLocation = function(data, instance) { 4315 var that = this, 4316 fieldNames, 4317 fiasFieldNames, 4318 fiasFields = {}; 4319 4320 that.instance = instance; 4321 that.fields = {}; 4322 that.specificity = -1; 4323 4324 if (lang_util.isPlainObject(data) && instance.type.dataComponents) { 4325 collection_util.each(instance.type.dataComponents, function( 4326 component, 4327 i 4328 ) { 4329 var fieldName = component.id; 4330 4331 if (component.forLocations && data[fieldName]) { 4332 that.fields[fieldName] = data[fieldName]; 4333 that.specificity = i; 4334 } 4335 }); 4336 } 4337 4338 fieldNames = Object.keys(that.fields); 4339 fiasFieldNames = collection_util.intersect(fieldNames, fiasParamNames); 4340 if (fiasFieldNames.length) { 4341 collection_util.each(fiasFieldNames, function(fieldName, index) { 4342 fiasFields[fieldName] = that.fields[fieldName]; 4343 }); 4344 that.fields = fiasFields; 4345 that.specificity = that.getFiasSpecificity(fiasFieldNames); 4346 } else if (that.fields.kladr_id) { 4347 that.fields = { kladr_id: that.fields.kladr_id }; 4348 that.significantKladr = getSignificantKladrId(that.fields.kladr_id); 4349 that.specificity = that.getKladrSpecificity(that.significantKladr); 4350 } 4351}; 4352 4353jqapi.extend(ConstraintLocation.prototype, { 4354 getLabel: function() { 4355 return this.instance.type.composeValue(this.fields, { 4356 saveCityDistrict: true 4357 }); 4358 }, 4359 4360 getFields: function() { 4361 return this.fields; 4362 }, 4363 4364 isValid: function() { 4365 return !lang_util.isEmptyObject(this.fields); 4366 }, 4367 4368 /** 4369 * ÐозвÑаÑÐ°ÐµÑ specificity Ð´Ð»Ñ ÐÐÐÐÐ 4370 * ÐпиÑание ниже, в getFiasSpecificity 4371 * @param kladr_id 4372 * @returns {number} 4373 */ 4374 getKladrSpecificity: function(kladr_id) { 4375 var specificity = -1; 4376 var kladrLength = kladr_id.length; 4377 4378 collection_util.each(this.instance.type.dataComponents, function( 4379 component, 4380 i 4381 ) { 4382 if ( 4383 component.kladrFormat && 4384 kladrLength === component.kladrFormat.digits 4385 ) { 4386 specificity = i; 4387 } 4388 }); 4389 4390 return specificity; 4391 }, 4392 4393 /** 4394 * ÐозвÑаÑÐ°ÐµÑ Ð¾ÑобÑÑ Ð²ÐµÐ»Ð¸ÑÐ¸Ð½Ñ specificity Ð´Ð»Ñ Ð¤ÐÐС 4395 * Specificity ÑÑо Ð¸Ð½Ð´ÐµÐºÑ Ð´Ð»Ñ Ð¼Ð°ÑÑива this.instance.type.dataComponents 4396 * до коÑоÑого (вклÑÑиÑелÑно) обÑежеÑÑÑ ÑÑÐ¾Ñ Ð¼Ð°ÑÑив пÑи ÑоÑмиÑовании ÑÑÑоки адÑеÑа. 4397 * ÐÑÐ¾Ñ Ð¿Ð°ÑамеÑÑ Ð½Ñжен Ð´Ð»Ñ ÑлÑÑаев, когда в наÑÑÑÐ¾Ð¹ÐºÐ°Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° restrict_value = true 4398 * ÐапÑимеÑ, ÑÑÑановлено огÑаниÑение (locations) по region_fias_id (ÐÑаÑнодаÑÑкий кÑай) 4399 * РвÑпадаÑке нажимаем на "г. СоÑи" 4400 * ÐÑли restrict_value оÑклÑÑен, Ñо вÑведеÑÑÑ Ð·Ð½Ð°Ñение "ÐÑаÑнодаÑÑкий кÑай, г СоÑи" 4401 * ÐÑли вклÑÑен, Ñо пÑоÑÑо "г СоÑи" 4402 * 4403 * @param fiasFieldNames 4404 * @returns {number} 4405 */ 4406 getFiasSpecificity: function(fiasFieldNames) { 4407 var specificity = -1; 4408 4409 collection_util.each(this.instance.type.dataComponents, function( 4410 component, 4411 i 4412 ) { 4413 if ( 4414 component.fiasType && 4415 fiasFieldNames.indexOf(component.fiasType) > -1 && 4416 specificity < i 4417 ) { 4418 specificity = i; 4419 } 4420 }); 4421 4422 return specificity; 4423 }, 4424 4425 containsData: function(data) { 4426 var result = true; 4427 4428 if (this.fields.kladr_id) { 4429 return ( 4430 !!data.kladr_id && 4431 data.kladr_id.indexOf(this.significantKladr) === 0 4432 ); 4433 } else { 4434 collection_util.each(this.fields, function(value, fieldName) { 4435 return (result = 4436 !!data[fieldName] && 4437 data[fieldName].toLowerCase() === value.toLowerCase()); 4438 }); 4439 4440 return result; 4441 } 4442 } 4443}); 4444 4445Suggestions.ConstraintLocation = ConstraintLocation; 4446 4447/** 4448 * @param {Object} data 4449 * @param {Object|Array} data.locations 4450 * @param {string} [data.label] 4451 * @param {boolean} [data.deletable] 4452 * @param {Suggestions} [instance] 4453 * @constructor 4454 */ 4455var Constraint = function(data, instance) { 4456 this.id = generateId("c"); 4457 this.deletable = !!data.deletable; 4458 this.instance = instance; 4459 4460 var locationsArray = collection_util.makeArray( 4461 data && (data.locations || data.restrictions) 4462 ); 4463 this.locations = locationsArray.map(function(data) { 4464 return new ConstraintLocation(data, instance); 4465 }); 4466 4467 this.locations = this.locations.filter(function(location) { 4468 return location.isValid(); 4469 }); 4470 4471 this.label = data.label; 4472 if (this.label == null && instance.type.composeValue) { 4473 this.label = this.locations 4474 .map(function(location) { 4475 return location.getLabel(); 4476 }) 4477 .join(", "); 4478 } 4479}; 4480 4481jqapi.extend(Constraint.prototype, { 4482 isValid: function() { 4483 return this.locations.length > 0; 4484 }, 4485 getFields: function() { 4486 return this.locations.map(function(location) { 4487 return location.getFields(); 4488 }); 4489 } 4490}); 4491 4492var methods$5 = { 4493 createConstraints: function() { 4494 var that = this; 4495 that.constraints = {}; 4496 }, 4497 setupConstraints: function() { 4498 var that = this, 4499 constraints = that.options.constraints, 4500 $parent; 4501 4502 if (!constraints) { 4503 that.unbindFromParent(); 4504 return; 4505 } 4506 4507 if ( 4508 jqapi.isJqObject(constraints) || 4509 typeof constraints === "string" || 4510 typeof constraints.nodeType === "number" 4511 ) { 4512 $parent = jqapi.select(constraints); 4513 if (!$parent.is(that.constraints)) { 4514 that.unbindFromParent(); 4515 if (!$parent.is(that.el)) { 4516 that.constraints = $parent; 4517 that.bindToParent(); 4518 } 4519 } 4520 } else { 4521 collection_util.each(that.constraints, function(_, id) { 4522 that.removeConstraint(id); 4523 }); 4524 collection_util.each( 4525 collection_util.makeArray(constraints), 4526 function(constraint, i) { 4527 that.addConstraint(constraint); 4528 } 4529 ); 4530 } 4531 }, 4532 4533 filteredLocation: function(data) { 4534 var locationComponents = [], 4535 location = {}; 4536 4537 collection_util.each(this.type.dataComponents, function() { 4538 if (this.forLocations) locationComponents.push(this.id); 4539 }); 4540 4541 if (lang_util.isPlainObject(data)) { 4542 // Copy to location only allowed fields 4543 collection_util.each(data, function(value, key) { 4544 if (value && locationComponents.indexOf(key) >= 0) { 4545 location[key] = value; 4546 } 4547 }); 4548 } 4549 4550 if (!lang_util.isEmptyObject(location)) { 4551 return location.kladr_id 4552 ? { kladr_id: location.kladr_id } 4553 : location; 4554 } 4555 }, 4556 4557 addConstraint: function(constraint) { 4558 var that = this; 4559 4560 constraint = new Constraint(constraint, that); 4561 4562 if (constraint.isValid()) { 4563 that.constraints[constraint.id] = constraint; 4564 } 4565 }, 4566 4567 removeConstraint: function(id) { 4568 var that = this; 4569 delete that.constraints[id]; 4570 }, 4571 4572 constructConstraintsParams: function() { 4573 var that = this, 4574 locations = [], 4575 constraints = that.constraints, 4576 parentInstance, 4577 parentData, 4578 params = {}; 4579 4580 while ( 4581 jqapi.isJqObject(constraints) && 4582 (parentInstance = constraints.suggestions()) && 4583 !(parentData = object_util.getDeepValue( 4584 parentInstance, 4585 "selection.data" 4586 )) 4587 ) { 4588 constraints = parentInstance.constraints; 4589 } 4590 4591 if (jqapi.isJqObject(constraints)) { 4592 parentData = new ConstraintLocation( 4593 parentData, 4594 parentInstance 4595 ).getFields(); 4596 4597 if (parentData) { 4598 // if send city_fias_id for city request 4599 // then no cities will responded 4600 if (that.bounds.own.indexOf("city") > -1) { 4601 delete parentData.city_fias_id; 4602 } 4603 params.locations = [parentData]; 4604 params.restrict_value = true; 4605 } 4606 } else { 4607 if (constraints) { 4608 collection_util.each(constraints, function(constraint, id) { 4609 locations = locations.concat(constraint.getFields()); 4610 }); 4611 4612 if (locations.length) { 4613 params.locations = locations; 4614 params.restrict_value = that.options.restrict_value; 4615 } 4616 } 4617 } 4618 4619 return params; 4620 }, 4621 4622 /** 4623 * Returns label of the first constraint (if any), empty string otherwise 4624 * @returns {String} 4625 */ 4626 getFirstConstraintLabel: function() { 4627 var that = this, 4628 constraints_id = 4629 lang_util.isPlainObject(that.constraints) && 4630 Object.keys(that.constraints)[0]; 4631 4632 return constraints_id ? that.constraints[constraints_id].label : ""; 4633 }, 4634 4635 bindToParent: function() { 4636 var that = this; 4637 4638 that.constraints 4639 .on( 4640 [ 4641 "suggestions-select." + that.uniqueId, 4642 "suggestions-invalidateselection." + that.uniqueId, 4643 "suggestions-clear." + that.uniqueId 4644 ].join(" "), 4645 jqapi.proxy(that.onParentSelectionChanged, that) 4646 ) 4647 .on( 4648 "suggestions-dispose." + that.uniqueId, 4649 jqapi.proxy(that.onParentDispose, that) 4650 ); 4651 }, 4652 4653 unbindFromParent: function() { 4654 var that = this, 4655 $parent = that.constraints; 4656 4657 if (jqapi.isJqObject($parent)) { 4658 $parent.off("." + that.uniqueId); 4659 } 4660 }, 4661 4662 onParentSelectionChanged: function(e, suggestion, valueChanged) { 4663 // Don't clear if parent has been just enriched 4664 if (e.type !== "suggestions-select" || valueChanged) { 4665 this.clear(); 4666 } 4667 }, 4668 4669 onParentDispose: function(e) { 4670 this.unbindFromParent(); 4671 }, 4672 4673 getParentInstance: function() { 4674 return ( 4675 jqapi.isJqObject(this.constraints) && this.constraints.suggestions() 4676 ); 4677 }, 4678 4679 shareWithParent: function(suggestion) { 4680 // that is the parent control's instance 4681 var that = this.getParentInstance(); 4682 4683 if ( 4684 !that || 4685 that.type !== this.type || 4686 belongsToArea(suggestion, that) 4687 ) { 4688 return; 4689 } 4690 4691 that.shareWithParent(suggestion); 4692 that.setSuggestion(suggestion); 4693 }, 4694 4695 /** 4696 * Pick only fields that absent in restriction 4697 */ 4698 getUnrestrictedData: function(data) { 4699 var that = this, 4700 restrictedKeys = [], 4701 unrestrictedData = {}, 4702 maxSpecificity = -1; 4703 4704 // Find most specific location that could restrict current data 4705 collection_util.each(that.constraints, function(constraint, id) { 4706 collection_util.each(constraint.locations, function(location, i) { 4707 if ( 4708 location.containsData(data) && 4709 location.specificity > maxSpecificity 4710 ) { 4711 maxSpecificity = location.specificity; 4712 } 4713 }); 4714 }); 4715 4716 if (maxSpecificity >= 0) { 4717 // ÐÐ»Ñ Ð³Ð¾Ñодов-Ñегионов нÑжно Ñакже оÑÑеÑÑ Ð¸ гоÑод 4718 if ( 4719 data.region_kladr_id && 4720 data.region_kladr_id === data.city_kladr_id 4721 ) { 4722 restrictedKeys.push.apply( 4723 restrictedKeys, 4724 that.type.dataComponentsById["city"].fields 4725 ); 4726 } 4727 4728 // Collect all fieldnames from all restricted components 4729 collection_util.each( 4730 that.type.dataComponents.slice(0, maxSpecificity + 1), 4731 function(component, i) { 4732 restrictedKeys.push.apply(restrictedKeys, component.fields); 4733 } 4734 ); 4735 4736 // Copy skipping restricted fields 4737 collection_util.each(data, function(value, key) { 4738 if (restrictedKeys.indexOf(key) === -1) { 4739 unrestrictedData[key] = value; 4740 } 4741 }); 4742 } else { 4743 unrestrictedData = data; 4744 } 4745 4746 return unrestrictedData; 4747 } 4748}; 4749 4750jqapi.extend(DEFAULT_OPTIONS, optionsUsed$1); 4751 4752jqapi.extend(Suggestions.prototype, methods$5); 4753 4754// Disable this feature when GET method used. See SUG-202 4755if (ajax.getDefaultType() != "GET") { 4756 notificator 4757 .on("initialize", methods$5.createConstraints) 4758 .on("setOptions", methods$5.setupConstraints) 4759 .on("requestParams", methods$5.constructConstraintsParams) 4760 .on("dispose", methods$5.unbindFromParent); 4761} 4762 4763/** 4764 * Methods for selecting a suggestion 4765 */ 4766 4767var methods$6 = { 4768 proceedQuery: function(query) { 4769 var that = this; 4770 4771 if (query.length >= that.options.minChars) { 4772 that.updateSuggestions(query); 4773 } else { 4774 that.hide(); 4775 } 4776 }, 4777 4778 /** 4779 * Selects current or first matched suggestion, but firstly waits for data ready 4780 * @param selectionOptions 4781 * @returns {$.Deferred} promise, resolved with index of selected suggestion or rejected if nothing matched 4782 */ 4783 selectCurrentValue: function(selectionOptions) { 4784 var that = this, 4785 result = jqapi.Deferred(); 4786 4787 // force onValueChange to be executed if it has been deferred 4788 that.inputPhase.resolve(); 4789 4790 that.fetchPhase 4791 .done(function() { 4792 var index; 4793 4794 // When suggestion has already been selected and not modified 4795 if (that.selection && !that.visible) { 4796 result.reject(); 4797 } else { 4798 index = that.findSuggestionIndex(); 4799 4800 that.select(index, selectionOptions); 4801 4802 if (index === -1) { 4803 result.reject(); 4804 } else { 4805 result.resolve(index); 4806 } 4807 } 4808 }) 4809 .fail(function() { 4810 result.reject(); 4811 }); 4812 4813 return result; 4814 }, 4815 4816 /** 4817 * Selects first when user interaction is not supposed 4818 */ 4819 selectFoundSuggestion: function() { 4820 var that = this; 4821 4822 if (!that.requestMode.userSelect) { 4823 that.select(0); 4824 } 4825 }, 4826 4827 /** 4828 * Selects current or first matched suggestion 4829 * @returns {number} index of found suggestion 4830 */ 4831 findSuggestionIndex: function() { 4832 var that = this, 4833 index = that.selectedIndex, 4834 value; 4835 4836 if (index === -1) { 4837 // matchers always operate with trimmed strings 4838 value = that.el.val().trim(); 4839 if (value) { 4840 that.type.matchers.some(function(matcher) { 4841 index = matcher(value, that.suggestions); 4842 return index !== -1; 4843 }); 4844 } 4845 } 4846 4847 return index; 4848 }, 4849 4850 /** 4851 * Selects a suggestion at specified index 4852 * @param index index of suggestion to select. Can be -1 4853 * @param {Object} selectionOptions 4854 * @param {boolean} [selectionOptions.continueSelecting] prevents hiding after selection 4855 * @param {boolean} [selectionOptions.noSpace] prevents adding space at the end of current value 4856 */ 4857 select: function(index, selectionOptions) { 4858 var that = this, 4859 suggestion = that.suggestions[index], 4860 continueSelecting = 4861 selectionOptions && selectionOptions.continueSelecting, 4862 currentValue = that.currentValue, 4863 hasSameValues; 4864 4865 // Prevent recursive execution 4866 if (that.triggering["Select"]) return; 4867 4868 // if no suggestion to select 4869 if (!suggestion) { 4870 if (!continueSelecting && !that.selection) { 4871 that.triggerOnSelectNothing(); 4872 } 4873 that.onSelectComplete(continueSelecting); 4874 return; 4875 } 4876 4877 hasSameValues = that.hasSameValues(suggestion); 4878 4879 that.enrichSuggestion(suggestion, selectionOptions).done(function( 4880 enrichedSuggestion, 4881 hasBeenEnriched 4882 ) { 4883 var newSelectionOptions = jqapi.extend( 4884 { 4885 hasBeenEnriched: hasBeenEnriched, 4886 hasSameValues: hasSameValues 4887 }, 4888 selectionOptions 4889 ); 4890 that.selectSuggestion( 4891 enrichedSuggestion, 4892 index, 4893 currentValue, 4894 newSelectionOptions 4895 ); 4896 }); 4897 }, 4898 4899 /** 4900 * Formats and selects final (enriched) suggestion 4901 * @param suggestion 4902 * @param index 4903 * @param lastValue 4904 * @param {Object} selectionOptions 4905 * @param {boolean} [selectionOptions.continueSelecting] prevents hiding after selection 4906 * @param {boolean} [selectionOptions.noSpace] prevents adding space at the end of current value 4907 * @param {boolean} selectionOptions.hasBeenEnriched 4908 * @param {boolean} selectionOptions.hasSameValues 4909 */ 4910 selectSuggestion: function(suggestion, index, lastValue, selectionOptions) { 4911 var that = this, 4912 continueSelecting = selectionOptions.continueSelecting, 4913 assumeDataComplete = 4914 !that.type.isDataComplete || 4915 that.type.isDataComplete.call(that, suggestion), 4916 currentSelection = that.selection; 4917 4918 // Prevent recursive execution 4919 if (that.triggering["Select"]) return; 4920 4921 if (that.type.alwaysContinueSelecting) { 4922 continueSelecting = true; 4923 } 4924 4925 if (assumeDataComplete) { 4926 continueSelecting = false; 4927 } 4928 4929 // `suggestions` cat be empty, e.g. during `fixData` 4930 if (selectionOptions.hasBeenEnriched && that.suggestions[index]) { 4931 that.suggestions[index].data = suggestion.data; 4932 } 4933 4934 if (that.requestMode.updateValue) { 4935 that.checkValueBounds(suggestion); 4936 that.currentValue = that.getSuggestionValue( 4937 suggestion, 4938 selectionOptions 4939 ); 4940 4941 if ( 4942 that.currentValue && 4943 !selectionOptions.noSpace && 4944 !assumeDataComplete 4945 ) { 4946 that.currentValue += " "; 4947 } 4948 that.el.val(that.currentValue); 4949 } 4950 4951 if (that.currentValue) { 4952 that.selection = suggestion; 4953 if (!that.areSuggestionsSame(suggestion, currentSelection)) { 4954 that.trigger( 4955 "Select", 4956 suggestion, 4957 that.currentValue != lastValue 4958 ); 4959 } 4960 if (that.requestMode.userSelect) { 4961 that.onSelectComplete(continueSelecting); 4962 } 4963 } else { 4964 that.selection = null; 4965 that.triggerOnSelectNothing(); 4966 } 4967 4968 that.shareWithParent(suggestion); 4969 }, 4970 4971 onSelectComplete: function(continueSelecting) { 4972 var that = this; 4973 4974 if (continueSelecting) { 4975 that.selectedIndex = -1; 4976 that.updateSuggestions(that.currentValue); 4977 } else { 4978 that.hide(); 4979 } 4980 }, 4981 4982 triggerOnSelectNothing: function() { 4983 var that = this; 4984 4985 if (!that.triggering["SelectNothing"]) { 4986 that.trigger("SelectNothing", that.currentValue); 4987 } 4988 }, 4989 4990 trigger: function(event) { 4991 var that = this, 4992 args = utils.slice(arguments, 1), 4993 callback = that.options["on" + event]; 4994 4995 that.triggering[event] = true; 4996 if (utils.isFunction(callback)) { 4997 callback.apply(that.element, args); 4998 } 4999 that.el.trigger.call( 5000 that.el, 5001 "suggestions-" + event.toLowerCase(), 5002 args 5003 ); 5004 that.triggering[event] = false; 5005 } 5006}; 5007 5008jqapi.extend(Suggestions.prototype, methods$6); 5009 5010notificator.on("assignSuggestions", methods$6.selectFoundSuggestion); 5011 5012/** 5013 * features for connected instances 5014 */ 5015 5016var optionsUsed$2 = { 5017 bounds: null, 5018}; 5019 5020var methods$7 = { 5021 setupBounds: function() { 5022 this.bounds = { 5023 from: null, 5024 to: null, 5025 }; 5026 }, 5027 5028 setBoundsOptions: function() { 5029 var that = this, 5030 boundsAvailable = [], 5031 newBounds = $.trim(that.options.bounds).split("-"), 5032 boundFrom = newBounds[0], 5033 boundTo = newBounds[newBounds.length - 1], 5034 boundsOwn = [], 5035 boundIsOwn, 5036 boundsAll = []; 5037 5038 if (that.type.dataComponents) { 5039 $.each(that.type.dataComponents, function() { 5040 if (this.forBounds) { 5041 boundsAvailable.push(this.id); 5042 } 5043 }); 5044 } 5045 5046 if (boundsAvailable.indexOf(boundFrom) === -1) { 5047 boundFrom = null; 5048 } 5049 5050 if (boundsAvailable.indexOf(boundTo) === -1) { 5051 boundTo = null; 5052 } 5053 5054 if (boundFrom || boundTo) { 5055 boundIsOwn = !boundFrom; 5056 $.each(boundsAvailable, function(i, bound) { 5057 if (bound == boundFrom) { 5058 boundIsOwn = true; 5059 } 5060 boundsAll.push(bound); 5061 if (boundIsOwn) { 5062 boundsOwn.push(bound); 5063 } 5064 if (bound == boundTo) { 5065 return false; 5066 } 5067 }); 5068 } 5069 5070 that.bounds.from = boundFrom; 5071 that.bounds.to = boundTo; 5072 that.bounds.all = boundsAll; 5073 that.bounds.own = boundsOwn; 5074 }, 5075 5076 constructBoundsParams: function() { 5077 var that = this, 5078 params = {}; 5079 5080 if (that.bounds.from) { 5081 params["from_bound"] = { value: that.bounds.from }; 5082 } 5083 if (that.bounds.to) { 5084 params["to_bound"] = { value: that.bounds.to }; 5085 } 5086 5087 return params; 5088 }, 5089 5090 /** 5091 * ÐодÑÑÑÐ°Ð¸Ð²Ð°ÐµÑ suggestion.value под that.bounds.own 5092 * ÐиÑего не возвÑаÑаеÑ, менÑÐµÑ Ð² Ñамом suggestion 5093 * @param suggestion 5094 */ 5095 checkValueBounds: function(suggestion) { 5096 var that = this, 5097 valueData; 5098 5099 // If any bounds set up 5100 if (that.bounds.own.length && that.type.composeValue) { 5101 // делаем ÐºÐ¾Ð¿Ð¸Ñ 5102 var bounds = that.bounds.own.slice(0); 5103 // еÑли ÑÐ¾Ð»Ñ ÑекÑÑего инÑÑанÑа плагина показÑваÑÑ ÑолÑко Ñайон гоÑода 5104 // Ñо Ð´Ð»Ñ ÐºÐ¾ÑÑекÑного ÑоÑмиÑÐ¾Ð²Ð½Ð¸Ñ Ð½Ñжен city_district_fias_id 5105 if (bounds.length === 1 && bounds[0] === "city_district") { 5106 bounds.push("city_district_fias_id"); 5107 } 5108 valueData = that.copyDataComponents(suggestion.data, bounds); 5109 suggestion.value = that.type.composeValue(valueData); 5110 } 5111 }, 5112 5113 copyDataComponents: function(data, components) { 5114 var result = {}, 5115 dataComponentsById = this.type.dataComponentsById; 5116 5117 if (dataComponentsById) { 5118 $.each(components, function(i, component) { 5119 $.each(dataComponentsById[component].fields, function( 5120 i, 5121 field 5122 ) { 5123 if (data[field] != null) { 5124 result[field] = data[field]; 5125 } 5126 }); 5127 }); 5128 } 5129 5130 return result; 5131 }, 5132 5133 getBoundedKladrId: function(kladr_id, boundsRange) { 5134 var boundTo = boundsRange[boundsRange.length - 1], 5135 kladrFormat; 5136 5137 $.each(this.type.dataComponents, function(i, component) { 5138 if (component.id === boundTo) { 5139 kladrFormat = component.kladrFormat; 5140 return false; 5141 } 5142 }); 5143 5144 return ( 5145 kladr_id.substr(0, kladrFormat.digits) + 5146 new Array((kladrFormat.zeros || 0) + 1).join("0") 5147 ); 5148 }, 5149}; 5150 5151$.extend(DEFAULT_OPTIONS, optionsUsed$2); 5152 5153$.extend(Suggestions.prototype, methods$7); 5154 5155notificator 5156 .on("initialize", methods$7.setupBounds) 5157 .on("setOptions", methods$7.setBoundsOptions) 5158 .on("requestParams", methods$7.constructBoundsParams); 5159 5160/** 5161 * УÑилиÑÑ Ð´Ð»Ñ ÑабоÑÑ Ñ DOM. 5162 */ 5163var dom = { 5164 /** 5165 * ÐÑбÑаÑÑ Ð¿ÐµÑвÑй ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ñ ÑказаннÑм клаÑÑом. 5166 */ 5167 selectByClass: function(classname, parent) { 5168 var selector = "." + classname; 5169 if (parent) { 5170 return parent.querySelector(selector); 5171 } else { 5172 return document.querySelector(selector); 5173 } 5174 }, 5175 5176 /** 5177 * ÐобавиÑÑ ÑлеменÑÑ ÐºÐ»Ð°ÑÑ. 5178 */ 5179 addClass: function(element, className) { 5180 var list = element.className.split(" "); 5181 if (list.indexOf(className) === -1) { 5182 list.push(className); 5183 } 5184 element.className = list.join(" "); 5185 }, 5186 5187 /** 5188 * ÐобавиÑÑ ÑлеменÑÑ ÑÑилÑ. 5189 */ 5190 setStyle: function(element, name, value) { 5191 element.style[name] = value; 5192 }, 5193 5194 /** 5195 * ÐодпиÑаÑÑÑÑ Ð½Ð° ÑобÑÑие на ÑлеменÑе. 5196 * @param {Element} element - ÑÐ»ÐµÐ¼ÐµÐ½Ñ 5197 * @param {string} eventName - название ÑобÑÑÐ¸Ñ 5198 * @param {string} namespace - пÑоÑÑÑанÑÑво имÑн ÑобÑÑÐ¸Ñ 5199 * @param {Function} callback - ÑÑнкÑиÑ-обÑабоÑÑик ÑобÑÑÐ¸Ñ 5200 */ 5201 listenTo: function(element, eventName, namespace, callback) { 5202 element.addEventListener(eventName, callback, false); 5203 if (namespace) { 5204 if (!eventsByNamespace[namespace]) { 5205 eventsByNamespace[namespace] = []; 5206 } 5207 eventsByNamespace[namespace].push({ 5208 eventName: eventName, 5209 element: element, 5210 callback: callback 5211 }); 5212 } 5213 }, 5214 5215 /** 5216 * ÐÑпиÑаÑÑÑÑ Ð¾Ñ Ð²ÑÐµÑ ÑобÑÑий Ñ ÑказаннÑм пÑоÑÑÑанÑÑвом имÑн. 5217 */ 5218 stopListeningNamespace: function(namespace) { 5219 var events = eventsByNamespace[namespace]; 5220 if (events) { 5221 events.forEach(function(event) { 5222 event.element.removeEventListener( 5223 event.eventName, 5224 event.callback, 5225 false 5226 ); 5227 }); 5228 } 5229 } 5230}; 5231 5232/** 5233 * ÐÑомо-ÑÑÑлка в ÑпиÑке подÑказок. 5234 */ 5235var FREE_PLAN = "FRE
5235
vendor: 4,686 bytes, lines 5235-5314
5235E"; 5236var LINK = 5237 "https://dadata.ru/suggestions/?utm_source=dadata&utm_medium=module&utm_campaign=suggestions-jquery"; 5238var PREFIX = ""; 5239var SUFFIX = ""; 5240var IMAGE = 5241 '<svg version="1.1" viewBox="0 0 128 38" xmlns="http://www.w3.org/2000/svg"><path d="m128 19v16.077c0 1.614-1.302 2.923-2.909 2.923h-122.18c-1.607 0-2.909-1.309-2.909-2.923v-32.154c-0-1.614 1.302-2.923 2.909-2.923h122.18c1.607 0 2.909 1.309 2.909 2.923z" fill="#ef4741"/><path d="m59.52 7.912h-8.902v22.098h9.92c3.724 0 9.872-0.341 9.872-6.703v-8.682c-0.01-6.372-7.166-6.713-10.89-6.713zm5.595 14.81c0 3.186-2.308 3.508-4.936 3.508h-4.276v-14.538h3.287c2.628 0 5.954 0.322 5.954 3.508zm-46.545-14.81h-8.834v22.098h9.871c3.724 0 9.872-0.341 9.872-6.703v-8.682c0-6.372-7.137-6.713-10.88-6.713zm5.595 14.81c0 3.186-2.308 3.508-4.936 3.508h-4.247v-14.538h3.258c2.628 0 5.954 0.322 5.954 3.508zm71.757-13.953h-4.945v16.301c-0.018 0.785 0.113 1.565 0.388 2.3 0.203 0.569 0.535 1.082 0.97 1.5 0.446 0.385 0.962 0.677 1.522 0.858 0.58 0.205 1.182 0.343 1.794 0.409 0.575 0.052 1.248 0.081 2.017 0.088 0.917-1e-3 1.834-0.057 2.744-0.166v-2.796h-1.765c-0.393 0.055-0.795 0.032-1.18-0.071-0.385-0.102-0.745-0.28-1.06-0.524-0.413-0.691-0.59-1.498-0.504-2.299v-8.068h4.509v-3.06h-4.509zm20.364 5.535c-1.176-0.741-3.278-1.108-6.303-1.101h-5.741v0.243l0.708 2.826h5.033c0.837-0.051 1.672 0.117 2.424 0.487 0.259 0.248 0.458 0.553 0.579 0.891 0.121 0.339 0.162 0.701 0.119 1.058v1.12h-5.527c-1.939 0-3.271 0.38-3.995 1.14-0.725 0.76-1.099 2.127-1.125 4.102 0 2.154 0.359 3.06 1.086 3.742 0.728 0.682 2.134 1.188 4.344 1.188h6.847c1.706 0 3.345-0.808 3.345-2.747v-8.584c0-2.176-0.589-3.635-1.765-4.375zm-3.19 12.959h-3.249c-0.735 0.081-1.478-0.036-2.152-0.342-0.285-0.227-0.427-0.876-0.427-1.948s0.136-1.741 0.407-2.007c0.625-0.331 1.336-0.46 2.037-0.371h3.384zm-26.667-12.959c-1.176-0.741-3.277-1.108-6.303-1.101h-5.741v0.243l0.708 2.826h5.033c0.836-0.051 1.672 0.117 2.424 0.487 0.259 0.248 0.457 0.553 0.578 0.891 0.121 0.339 0.162 0.701 0.12 1.058v1.12h-5.556c-1.939 0-3.271 0.38-3.995 1.14s-1.086 2.127-1.086 4.102c0 2.154 0.359 3.06 1.086 3.742s2.133 1.188 4.344 1.188h6.846c1.717 0 3.346-0.808 3.346-2.747v-8.584c-7e-3 -2.176-0.595-3.635-1.765-4.375zm-3.181 12.959h-3.248c-0.735 0.081-1.478-0.037-2.153-0.342-0.284-0.227-0.426-0.876-0.426-1.948s0.135-1.741 0.407-2.007c0.624-0.331 1.336-0.46 2.036-0.371h3.384zm-37.74-12.959c-1.176-0.741-3.278-1.108-6.303-1.101h-5.712v0.243l0.708 2.826h5.033c0.837-0.051 1.672 0.117 2.424 0.487 0.259 0.248 0.457 0.553 0.578 0.891 0.121 0.339 0.162 0.701 0.12 1.058v1.12h-5.556c-1.939 0-3.271 0.38-3.995 1.14s-1.099 2.127-1.125 4.102c0 2.154 0.359 3.06 1.086 3.742s2.133 1.188 4.344 1.188h6.846c1.717 0 3.346-0.808 3.346-2.747v-8.584c0-2.176-0.589-3.635-1.765-4.375zm-3.181 12.959h-3.219c-0.735 0.081-1.478-0.037-2.153-0.342-0.284-0.227-0.427-0.876-0.427-1.948s0.136-1.741 0.408-2.007c0.624-0.331 1.336-0.46 2.036-0.371h3.384z" fill="#fff"/></svg>'; 5242 5243function Promo(plugin) { 5244 this.plan = plugin.status.plan; 5245 var container = plugin.getContainer(); 5246 this.element = dom.selectByClass(CLASSES.promo, container); 5247} 5248 5249Promo.prototype.show = function() { 5250 if (this.plan !== FREE_PLAN) { 5251 return; 5252 } 5253 if (!this.element) { 5254 return; 5255 } 5256 this.setStyles(); 5257 this.setHtml(); 5258}; 5259 5260Promo.prototype.setStyles = function() { 5261 this.element.style.display = "block"; 5262}; 5263 5264Promo.prototype.setHtml = function() { 5265 this.element.innerHTML = 5266 '<a target="_blank" tabindex="-1" href="' + 5267 LINK + 5268 '">' + 5269 PREFIX + 5270 IMAGE + 5271 SUFFIX + 5272 "</a>"; 5273}; 5274 5275function show() { 5276 new Promo(this).show(); 5277} 5278 5279notificator.on("assignSuggestions", show); 5280 5281Suggestions.defaultOptions = DEFAULT_OPTIONS; 5282 5283Suggestions.version = "22.6.0"; 5284 5285$.Suggestions = Suggestions; 5286 5287// Create chainable jQuery plugin: 5288$.fn.suggestions = function(options, args) { 5289 // If function invoked without argument return 5290 // instance of the first matched element: 5291 if (arguments.length === 0) { 5292 return this.first().data(DATA_ATTR_KEY); 5293 } 5294 5295 return this.each(function() { 5296 var inputElement = $(this), 5297 instance = inputElement.data(DATA_ATTR_KEY); 5298 5299 if (typeof options === "string") { 5300 if (instance && typeof instance[options] === "function") { 5301 instance[options](args); 5302 } 5303 } else { 5304 // If instance already exists, destroy it: 5305 if (instance && instance.dispose) { 5306 instance.dispose(); 5307 } 5308 instance = new Suggestions(this, options); 5309 inputElement.data(DATA_ATTR_KEY, instance); 5310 } 5311 }); 5312}; 5313 5314})));
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.