<!DOCTYPE html>
<html>
<head>
<title>Angular 2 Autocomplete - Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSS file -->
<link rel="stylesheet" type="text/css" href="style.css">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" >
<!-- IE polyfills, keep the order please -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
<!-- Agular 2 -->
<script src="https://code.angularjs.org/2.0.0-beta.7/angular2-polyfills.js"></script>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.7/Rx.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.7/angular2.dev.js"></script>
<!-- Config Agular 2 and Typescript -->
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'app': {defaultExtension: 'ts'}}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
</head>
<!-- Run the application -->
<body>
<h1>Angular 2 Autocomplete</h1>
<my-app class="container" style="display: block">Loading Sample...</my-app>
<div style="padding-top:50px">
<a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Tutorial">
<img src="http://www.angulartypescript.com/wp-content/uploads/2016/03/learn-more-angular-2.png" alt="Smiley face" height="200" width="500">
</a>
<ul class="nav nav-pills nav-stacked" >
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Home"> Angular 2 Tutorial </a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-introduction/">Angular 2 Introduction</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-architecture/">Angular 2 Architecture</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-annotations/">Angular 2 Annotations</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-getting-started/">Angular 2 Setup</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-hello-world/">Angular 2 Hello World</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-components/">Angular 2 Components</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-template-syntax/">Angular 2 Template Syntax</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-data-binding/">Angular 2 Data Binding</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-forms/">Angular 2 Forms</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-formbuilder-example/">Angular 2 Formbuilder</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-router-example/">Angular 2 Router</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-http-example-typescript/">Angular 2 HTTP</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-services/">Angular 2 Service</a></li>
</ul>
</div>
</body>
</html>
<!--
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/
-->
/* Styles go here */
/**
* Created by Tareq Boulakjar. from angulartypescript.com
*/
import {bootstrap} from 'angular2/platform/browser';
import {Angular2Autocomplete} from './autocomplete-example';
bootstrap(Angular2Autocomplete);
/*
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/
*/
import { latinMap } from './latin-map';
export class AutocompleteUtils {
static latinMap:any = latinMap;
public static latinize(str:string) {
return str.replace(/[^A-Za-z0-9\[\] ]/g, function (a) {
return AutocompleteUtils.latinMap[a] || a;
});
}
public static escapeRegexp(queryToEscape:string) {
// Regex: capture the whole query string and replace it with the string that will be used to match
// the results, for example if the capture is 'a' the result will be \a
return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
}
public static tokenize(str:string, wordRegexDelimiters:string = ' ', phraseRegexDelimiters:string = ''):Array<string> {
let regexStr:string = '(?:[' + phraseRegexDelimiters + '])([^' + phraseRegexDelimiters + ']+)(?:[' + phraseRegexDelimiters + '])|([^' + wordRegexDelimiters + ']+)';
let preTokenized:Array<string> = str.split(new RegExp(regexStr, 'g'));
let result:Array<string> = [];
let preTokenizedLength:number = preTokenized.length;
let token:string;
let replacePhraseDelimiters = new RegExp('[' + phraseRegexDelimiters + ']+', 'g');
for (let i = 0; i < preTokenizedLength; i += 1) {
token = preTokenized[i];
if (token && token.length && token !== wordRegexDelimiters) {
result.push(token.replace(replacePhraseDelimiters, ''));
}
}
return result;
}
}
/**
* bootstrap
*/
import {
Injectable,
ElementRef
} from 'angular2/core';
import {IAttribute} from './IAttribute';
export class PositionService {
private get window():any {
return window;
}
private get document():any {
return window.document;
}
private getStyle(nativeEl:any, cssProp:string):any {
// IE
if (nativeEl.currentStyle) {
return nativeEl.currentStyle[cssProp];
}
if (this.window.getComputedStyle) {
return this.window.getComputedStyle(nativeEl)[cssProp];
}
// finally try and get inline style
return nativeEl.style[cssProp];
}
/**
* Checks if a given element is statically positioned
* @param nativeEl - raw DOM element
*/
private isStaticPositioned(nativeEl:any):any {
return (this.getStyle(nativeEl, 'position') || 'static' ) === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param nativeEl
*/
private parentOffsetEl(nativeEl:any) {
let offsetParent = nativeEl.offsetParent || this.document;
while (offsetParent && offsetParent !== this.document &&
this.isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || this.document;
};
/**
* Provides read-only equivalent of jQuery's position function:
* http://api.jquery.com/position/
*/
public position(nativeEl:any):{width: number, height: number, top: number, left: number} {
let elBCR = this.offset(nativeEl);
let offsetParentBCR = {top: 0, left: 0};
let offsetParentEl = this.parentOffsetEl(nativeEl);
if (offsetParentEl !== this.document) {
offsetParentBCR = this.offset(offsetParentEl);
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
let boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
}
/**
* Provides read-only equivalent of jQuery's offset function:
* http://api.jquery.com/offset/
*/
public offset(nativeEl:any):{width: number, height: number, top: number, left: number} {
let boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: boundingClientRect.top + (this.window.pageYOffset || this.document.documentElement.scrollTop),
left: boundingClientRect.left + (this.window.pageXOffset || this.document.documentElement.scrollLeft)
};
}
/**
* Provides coordinates for the targetEl in relation to hostEl
*/
public positionElements(hostEl:any, targetEl:any, positionStr:any, appendToBody:any):{top: number, left: number} {
let positionStrParts = positionStr.split('-');
let pos0 = positionStrParts[0];
let pos1 = positionStrParts[1] || 'center';
let hostElPos = appendToBody ?
this.offset(hostEl) :
this.position(hostEl);
let targetElWidth = targetEl.offsetWidth;
let targetElHeight = targetEl.offsetHeight;
let shiftWidth:IAttribute = {
center: function () {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function () {
return hostElPos.left;
},
right: function () {
return hostElPos.left + hostElPos.width;
}
};
let shiftHeight:IAttribute = {
center: function ():number {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function ():number {
return hostElPos.top;
},
bottom: function ():number {
return hostElPos.top + hostElPos.height;
}
};
let targetElPos:{top: number, left: number};
switch (pos0) {
case 'right':
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case 'left':
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case 'bottom':
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
}
export const positionService = new PositionService();
export class AutocompleteOptions {
public placement:string;
public animation:boolean;
constructor(options:AutocompleteOptions) {
Object.assign(this, options);
}
}
export const latinMap = {
'Á': 'A',
'Ă': 'A',
'Ắ': 'A',
'Ặ': 'A',
'Ằ': 'A',
'Ẳ': 'A',
'Ẵ': 'A',
'Ǎ': 'A',
'Â': 'A',
'Ấ': 'A',
'Ậ': 'A',
'Ầ': 'A',
'Ẩ': 'A',
'Ẫ': 'A',
'Ä': 'A',
'Ǟ': 'A',
'Ȧ': 'A',
'Ǡ': 'A',
'Ạ': 'A',
'Ȁ': 'A',
'À': 'A',
'Ả': 'A',
'Ȃ': 'A',
'Ā': 'A',
'Ą': 'A',
'Å': 'A',
'Ǻ': 'A',
'Ḁ': 'A',
'Ⱥ': 'A',
'Ã': 'A',
'Ꜳ': 'AA',
'Æ': 'AE',
'Ǽ': 'AE',
'Ǣ': 'AE',
'Ꜵ': 'AO',
'Ꜷ': 'AU',
'Ꜹ': 'AV',
'Ꜻ': 'AV',
'Ꜽ': 'AY',
'Ḃ': 'B',
'Ḅ': 'B',
'Ɓ': 'B',
'Ḇ': 'B',
'Ƀ': 'B',
'Ƃ': 'B',
'Ć': 'C',
'Č': 'C',
'Ç': 'C',
'Ḉ': 'C',
'Ĉ': 'C',
'Ċ': 'C',
'Ƈ': 'C',
'Ȼ': 'C',
'Ď': 'D',
'Ḑ': 'D',
'Ḓ': 'D',
'Ḋ': 'D',
'Ḍ': 'D',
'Ɗ': 'D',
'Ḏ': 'D',
'Dz': 'D',
'Dž': 'D',
'Đ': 'D',
'Ƌ': 'D',
'DZ': 'DZ',
'DŽ': 'DZ',
'É': 'E',
'Ĕ': 'E',
'Ě': 'E',
'Ȩ': 'E',
'Ḝ': 'E',
'Ê': 'E',
'Ế': 'E',
'Ệ': 'E',
'Ề': 'E',
'Ể': 'E',
'Ễ': 'E',
'Ḙ': 'E',
'Ë': 'E',
'Ė': 'E',
'Ẹ': 'E',
'Ȅ': 'E',
'È': 'E',
'Ẻ': 'E',
'Ȇ': 'E',
'Ē': 'E',
'Ḗ': 'E',
'Ḕ': 'E',
'Ę': 'E',
'Ɇ': 'E',
'Ẽ': 'E',
'Ḛ': 'E',
'Ꝫ': 'ET',
'Ḟ': 'F',
'Ƒ': 'F',
'Ǵ': 'G',
'Ğ': 'G',
'Ǧ': 'G',
'Ģ': 'G',
'Ĝ': 'G',
'Ġ': 'G',
'Ɠ': 'G',
'Ḡ': 'G',
'Ǥ': 'G',
'Ḫ': 'H',
'Ȟ': 'H',
'Ḩ': 'H',
'Ĥ': 'H',
'Ⱨ': 'H',
'Ḧ': 'H',
'Ḣ': 'H',
'Ḥ': 'H',
'Ħ': 'H',
'Í': 'I',
'Ĭ': 'I',
'Ǐ': 'I',
'Î': 'I',
'Ï': 'I',
'Ḯ': 'I',
'İ': 'I',
'Ị': 'I',
'Ȉ': 'I',
'Ì': 'I',
'Ỉ': 'I',
'Ȋ': 'I',
'Ī': 'I',
'Į': 'I',
'Ɨ': 'I',
'Ĩ': 'I',
'Ḭ': 'I',
'Ꝺ': 'D',
'Ꝼ': 'F',
'Ᵹ': 'G',
'Ꞃ': 'R',
'Ꞅ': 'S',
'Ꞇ': 'T',
'Ꝭ': 'IS',
'Ĵ': 'J',
'Ɉ': 'J',
'Ḱ': 'K',
'Ǩ': 'K',
'Ķ': 'K',
'Ⱪ': 'K',
'Ꝃ': 'K',
'Ḳ': 'K',
'Ƙ': 'K',
'Ḵ': 'K',
'Ꝁ': 'K',
'Ꝅ': 'K',
'Ĺ': 'L',
'Ƚ': 'L',
'Ľ': 'L',
'Ļ': 'L',
'Ḽ': 'L',
'Ḷ': 'L',
'Ḹ': 'L',
'Ⱡ': 'L',
'Ꝉ': 'L',
'Ḻ': 'L',
'Ŀ': 'L',
'Ɫ': 'L',
'Lj': 'L',
'Ł': 'L',
'LJ': 'LJ',
'Ḿ': 'M',
'Ṁ': 'M',
'Ṃ': 'M',
'Ɱ': 'M',
'Ń': 'N',
'Ň': 'N',
'Ņ': 'N',
'Ṋ': 'N',
'Ṅ': 'N',
'Ṇ': 'N',
'Ǹ': 'N',
'Ɲ': 'N',
'Ṉ': 'N',
'Ƞ': 'N',
'Nj': 'N',
'Ñ': 'N',
'NJ': 'NJ',
'Ó': 'O',
'Ŏ': 'O',
'Ǒ': 'O',
'Ô': 'O',
'Ố': 'O',
'Ộ': 'O',
'Ồ': 'O',
'Ổ': 'O',
'Ỗ': 'O',
'Ö': 'O',
'Ȫ': 'O',
'Ȯ': 'O',
'Ȱ': 'O',
'Ọ': 'O',
'Ő': 'O',
'Ȍ': 'O',
'Ò': 'O',
'Ỏ': 'O',
'Ơ': 'O',
'Ớ': 'O',
'Ợ': 'O',
'Ờ': 'O',
'Ở': 'O',
'Ỡ': 'O',
'Ȏ': 'O',
'Ꝋ': 'O',
'Ꝍ': 'O',
'Ō': 'O',
'Ṓ': 'O',
'Ṑ': 'O',
'Ɵ': 'O',
'Ǫ': 'O',
'Ǭ': 'O',
'Ø': 'O',
'Ǿ': 'O',
'Õ': 'O',
'Ṍ': 'O',
'Ṏ': 'O',
'Ȭ': 'O',
'Ƣ': 'OI',
'Ꝏ': 'OO',
'Ɛ': 'E',
'Ɔ': 'O',
'Ȣ': 'OU',
'Ṕ': 'P',
'Ṗ': 'P',
'Ꝓ': 'P',
'Ƥ': 'P',
'Ꝕ': 'P',
'Ᵽ': 'P',
'Ꝑ': 'P',
'Ꝙ': 'Q',
'Ꝗ': 'Q',
'Ŕ': 'R',
'Ř': 'R',
'Ŗ': 'R',
'Ṙ': 'R',
'Ṛ': 'R',
'Ṝ': 'R',
'Ȑ': 'R',
'Ȓ': 'R',
'Ṟ': 'R',
'Ɍ': 'R',
'Ɽ': 'R',
'Ꜿ': 'C',
'Ǝ': 'E',
'Ś': 'S',
'Ṥ': 'S',
'Š': 'S',
'Ṧ': 'S',
'Ş': 'S',
'Ŝ': 'S',
'Ș': 'S',
'Ṡ': 'S',
'Ṣ': 'S',
'Ṩ': 'S',
'Ť': 'T',
'Ţ': 'T',
'Ṱ': 'T',
'Ț': 'T',
'Ⱦ': 'T',
'Ṫ': 'T',
'Ṭ': 'T',
'Ƭ': 'T',
'Ṯ': 'T',
'Ʈ': 'T',
'Ŧ': 'T',
'Ɐ': 'A',
'Ꞁ': 'L',
'Ɯ': 'M',
'Ʌ': 'V',
'Ꜩ': 'TZ',
'Ú': 'U',
'Ŭ': 'U',
'Ǔ': 'U',
'Û': 'U',
'Ṷ': 'U',
'Ü': 'U',
'Ǘ': 'U',
'Ǚ': 'U',
'Ǜ': 'U',
'Ǖ': 'U',
'Ṳ': 'U',
'Ụ': 'U',
'Ű': 'U',
'Ȕ': 'U',
'Ù': 'U',
'Ủ': 'U',
'Ư': 'U',
'Ứ': 'U',
'Ự': 'U',
'Ừ': 'U',
'Ử': 'U',
'Ữ': 'U',
'Ȗ': 'U',
'Ū': 'U',
'Ṻ': 'U',
'Ų': 'U',
'Ů': 'U',
'Ũ': 'U',
'Ṹ': 'U',
'Ṵ': 'U',
'Ꝟ': 'V',
'Ṿ': 'V',
'Ʋ': 'V',
'Ṽ': 'V',
'Ꝡ': 'VY',
'Ẃ': 'W',
'Ŵ': 'W',
'Ẅ': 'W',
'Ẇ': 'W',
'Ẉ': 'W',
'Ẁ': 'W',
'Ⱳ': 'W',
'Ẍ': 'X',
'Ẋ': 'X',
'Ý': 'Y',
'Ŷ': 'Y',
'Ÿ': 'Y',
'Ẏ': 'Y',
'Ỵ': 'Y',
'Ỳ': 'Y',
'Ƴ': 'Y',
'Ỷ': 'Y',
'Ỿ': 'Y',
'Ȳ': 'Y',
'Ɏ': 'Y',
'Ỹ': 'Y',
'Ź': 'Z',
'Ž': 'Z',
'Ẑ': 'Z',
'Ⱬ': 'Z',
'Ż': 'Z',
'Ẓ': 'Z',
'Ȥ': 'Z',
'Ẕ': 'Z',
'Ƶ': 'Z',
'IJ': 'IJ',
'Œ': 'OE',
'ᴀ': 'A',
'ᴁ': 'AE',
'ʙ': 'B',
'ᴃ': 'B',
'ᴄ': 'C',
'ᴅ': 'D',
'ᴇ': 'E',
'ꜰ': 'F',
'ɢ': 'G',
'ʛ': 'G',
'ʜ': 'H',
'ɪ': 'I',
'ʁ': 'R',
'ᴊ': 'J',
'ᴋ': 'K',
'ʟ': 'L',
'ᴌ': 'L',
'ᴍ': 'M',
'ɴ': 'N',
'ᴏ': 'O',
'ɶ': 'OE',
'ᴐ': 'O',
'ᴕ': 'OU',
'ᴘ': 'P',
'ʀ': 'R',
'ᴎ': 'N',
'ᴙ': 'R',
'ꜱ': 'S',
'ᴛ': 'T',
'ⱻ': 'E',
'ᴚ': 'R',
'ᴜ': 'U',
'ᴠ': 'V',
'ᴡ': 'W',
'ʏ': 'Y',
'ᴢ': 'Z',
'á': 'a',
'ă': 'a',
'ắ': 'a',
'ặ': 'a',
'ằ': 'a',
'ẳ': 'a',
'ẵ': 'a',
'ǎ': 'a',
'â': 'a',
'ấ': 'a',
'ậ': 'a',
'ầ': 'a',
'ẩ': 'a',
'ẫ': 'a',
'ä': 'a',
'ǟ': 'a',
'ȧ': 'a',
'ǡ': 'a',
'ạ': 'a',
'ȁ': 'a',
'à': 'a',
'ả': 'a',
'ȃ': 'a',
'ā': 'a',
'ą': 'a',
'ᶏ': 'a',
'ẚ': 'a',
'å': 'a',
'ǻ': 'a',
'ḁ': 'a',
'ⱥ': 'a',
'ã': 'a',
'ꜳ': 'aa',
'æ': 'ae',
'ǽ': 'ae',
'ǣ': 'ae',
'ꜵ': 'ao',
'ꜷ': 'au',
'ꜹ': 'av',
'ꜻ': 'av',
'ꜽ': 'ay',
'ḃ': 'b',
'ḅ': 'b',
'ɓ': 'b',
'ḇ': 'b',
'ᵬ': 'b',
'ᶀ': 'b',
'ƀ': 'b',
'ƃ': 'b',
'ɵ': 'o',
'ć': 'c',
'č': 'c',
'ç': 'c',
'ḉ': 'c',
'ĉ': 'c',
'ɕ': 'c',
'ċ': 'c',
'ƈ': 'c',
'ȼ': 'c',
'ď': 'd',
'ḑ': 'd',
'ḓ': 'd',
'ȡ': 'd',
'ḋ': 'd',
'ḍ': 'd',
'ɗ': 'd',
'ᶑ': 'd',
'ḏ': 'd',
'ᵭ': 'd',
'ᶁ': 'd',
'đ': 'd',
'ɖ': 'd',
'ƌ': 'd',
'ı': 'i',
'ȷ': 'j',
'ɟ': 'j',
'ʄ': 'j',
'dz': 'dz',
'dž': 'dz',
'é': 'e',
'ĕ': 'e',
'ě': 'e',
'ȩ': 'e',
'ḝ': 'e',
'ê': 'e',
'ế': 'e',
'ệ': 'e',
'ề': 'e',
'ể': 'e',
'ễ': 'e',
'ḙ': 'e',
'ë': 'e',
'ė': 'e',
'ẹ': 'e',
'ȅ': 'e',
'è': 'e',
'ẻ': 'e',
'ȇ': 'e',
'ē': 'e',
'ḗ': 'e',
'ḕ': 'e',
'ⱸ': 'e',
'ę': 'e',
'ᶒ': 'e',
'ɇ': 'e',
'ẽ': 'e',
'ḛ': 'e',
'ꝫ': 'et',
'ḟ': 'f',
'ƒ': 'f',
'ᵮ': 'f',
'ᶂ': 'f',
'ǵ': 'g',
'ğ': 'g',
'ǧ': 'g',
'ģ': 'g',
'ĝ': 'g',
'ġ': 'g',
'ɠ': 'g',
'ḡ': 'g',
'ᶃ': 'g',
'ǥ': 'g',
'ḫ': 'h',
'ȟ': 'h',
'ḩ': 'h',
'ĥ': 'h',
'ⱨ': 'h',
'ḧ': 'h',
'ḣ': 'h',
'ḥ': 'h',
'ɦ': 'h',
'ẖ': 'h',
'ħ': 'h',
'ƕ': 'hv',
'í': 'i',
'ĭ': 'i',
'ǐ': 'i',
'î': 'i',
'ï': 'i',
'ḯ': 'i',
'ị': 'i',
'ȉ': 'i',
'ì': 'i',
'ỉ': 'i',
'ȋ': 'i',
'ī': 'i',
'į': 'i',
'ᶖ': 'i',
'ɨ': 'i',
'ĩ': 'i',
'ḭ': 'i',
'ꝺ': 'd',
'ꝼ': 'f',
'ᵹ': 'g',
'ꞃ': 'r',
'ꞅ': 's',
'ꞇ': 't',
'ꝭ': 'is',
'ǰ': 'j',
'ĵ': 'j',
'ʝ': 'j',
'ɉ': 'j',
'ḱ': 'k',
'ǩ': 'k',
'ķ': 'k',
'ⱪ': 'k',
'ꝃ': 'k',
'ḳ': 'k',
'ƙ': 'k',
'ḵ': 'k',
'ᶄ': 'k',
'ꝁ': 'k',
'ꝅ': 'k',
'ĺ': 'l',
'ƚ': 'l',
'ɬ': 'l',
'ľ': 'l',
'ļ': 'l',
'ḽ': 'l',
'ȴ': 'l',
'ḷ': 'l',
'ḹ': 'l',
'ⱡ': 'l',
'ꝉ': 'l',
'ḻ': 'l',
'ŀ': 'l',
'ɫ': 'l',
'ᶅ': 'l',
'ɭ': 'l',
'ł': 'l',
'lj': 'lj',
'ſ': 's',
'ẜ': 's',
'ẛ': 's',
'ẝ': 's',
'ḿ': 'm',
'ṁ': 'm',
'ṃ': 'm',
'ɱ': 'm',
'ᵯ': 'm',
'ᶆ': 'm',
'ń': 'n',
'ň': 'n',
'ņ': 'n',
'ṋ': 'n',
'ȵ': 'n',
'ṅ': 'n',
'ṇ': 'n',
'ǹ': 'n',
'ɲ': 'n',
'ṉ': 'n',
'ƞ': 'n',
'ᵰ': 'n',
'ᶇ': 'n',
'ɳ': 'n',
'ñ': 'n',
'nj': 'nj',
'ó': 'o',
'ŏ': 'o',
'ǒ': 'o',
'ô': 'o',
'ố': 'o',
'ộ': 'o',
'ồ': 'o',
'ổ': 'o',
'ỗ': 'o',
'ö': 'o',
'ȫ': 'o',
'ȯ': 'o',
'ȱ': 'o',
'ọ': 'o',
'ő': 'o',
'ȍ': 'o',
'ò': 'o',
'ỏ': 'o',
'ơ': 'o',
'ớ': 'o',
'ợ': 'o',
'ờ': 'o',
'ở': 'o',
'ỡ': 'o',
'ȏ': 'o',
'ꝋ': 'o',
'ꝍ': 'o',
'ⱺ': 'o',
'ō': 'o',
'ṓ': 'o',
'ṑ': 'o',
'ǫ': 'o',
'ǭ': 'o',
'ø': 'o',
'ǿ': 'o',
'õ': 'o',
'ṍ': 'o',
'ṏ': 'o',
'ȭ': 'o',
'ƣ': 'oi',
'ꝏ': 'oo',
'ɛ': 'e',
'ᶓ': 'e',
'ɔ': 'o',
'ᶗ': 'o',
'ȣ': 'ou',
'ṕ': 'p',
'ṗ': 'p',
'ꝓ': 'p',
'ƥ': 'p',
'ᵱ': 'p',
'ᶈ': 'p',
'ꝕ': 'p',
'ᵽ': 'p',
'ꝑ': 'p',
'ꝙ': 'q',
'ʠ': 'q',
'ɋ': 'q',
'ꝗ': 'q',
'ŕ': 'r',
'ř': 'r',
'ŗ': 'r',
'ṙ': 'r',
'ṛ': 'r',
'ṝ': 'r',
'ȑ': 'r',
'ɾ': 'r',
'ᵳ': 'r',
'ȓ': 'r',
'ṟ': 'r',
'ɼ': 'r',
'ᵲ': 'r',
'ᶉ': 'r',
'ɍ': 'r',
'ɽ': 'r',
'ↄ': 'c',
'ꜿ': 'c',
'ɘ': 'e',
'ɿ': 'r',
'ś': 's',
'ṥ': 's',
'š': 's',
'ṧ': 's',
'ş': 's',
'ŝ': 's',
'ș': 's',
'ṡ': 's',
'ṣ': 's',
'ṩ': 's',
'ʂ': 's',
'ᵴ': 's',
'ᶊ': 's',
'ȿ': 's',
'ɡ': 'g',
'ᴑ': 'o',
'ᴓ': 'o',
'ᴝ': 'u',
'ť': 't',
'ţ': 't',
'ṱ': 't',
'ț': 't',
'ȶ': 't',
'ẗ': 't',
'ⱦ': 't',
'ṫ': 't',
'ṭ': 't',
'ƭ': 't',
'ṯ': 't',
'ᵵ': 't',
'ƫ': 't',
'ʈ': 't',
'ŧ': 't',
'ᵺ': 'th',
'ɐ': 'a',
'ᴂ': 'ae',
'ǝ': 'e',
'ᵷ': 'g',
'ɥ': 'h',
'ʮ': 'h',
'ʯ': 'h',
'ᴉ': 'i',
'ʞ': 'k',
'ꞁ': 'l',
'ɯ': 'm',
'ɰ': 'm',
'ᴔ': 'oe',
'ɹ': 'r',
'ɻ': 'r',
'ɺ': 'r',
'ⱹ': 'r',
'ʇ': 't',
'ʌ': 'v',
'ʍ': 'w',
'ʎ': 'y',
'ꜩ': 'tz',
'ú': 'u',
'ŭ': 'u',
'ǔ': 'u',
'û': 'u',
'ṷ': 'u',
'ü': 'u',
'ǘ': 'u',
'ǚ': 'u',
'ǜ': 'u',
'ǖ': 'u',
'ṳ': 'u',
'ụ': 'u',
'ű': 'u',
'ȕ': 'u',
'ù': 'u',
'ủ': 'u',
'ư': 'u',
'ứ': 'u',
'ự': 'u',
'ừ': 'u',
'ử': 'u',
'ữ': 'u',
'ȗ': 'u',
'ū': 'u',
'ṻ': 'u',
'ų': 'u',
'ᶙ': 'u',
'ů': 'u',
'ũ': 'u',
'ṹ': 'u',
'ṵ': 'u',
'ᵫ': 'ue',
'ꝸ': 'um',
'ⱴ': 'v',
'ꝟ': 'v',
'ṿ': 'v',
'ʋ': 'v',
'ᶌ': 'v',
'ⱱ': 'v',
'ṽ': 'v',
'ꝡ': 'vy',
'ẃ': 'w',
'ŵ': 'w',
'ẅ': 'w',
'ẇ': 'w',
'ẉ': 'w',
'ẁ': 'w',
'ⱳ': 'w',
'ẘ': 'w',
'ẍ': 'x',
'ẋ': 'x',
'ᶍ': 'x',
'ý': 'y',
'ŷ': 'y',
'ÿ': 'y',
'ẏ': 'y',
'ỵ': 'y',
'ỳ': 'y',
'ƴ': 'y',
'ỷ': 'y',
'ỿ': 'y',
'ȳ': 'y',
'ẙ': 'y',
'ɏ': 'y',
'ỹ': 'y',
'ź': 'z',
'ž': 'z',
'ẑ': 'z',
'ʑ': 'z',
'ⱬ': 'z',
'ż': 'z',
'ẓ': 'z',
'ȥ': 'z',
'ẕ': 'z',
'ᵶ': 'z',
'ᶎ': 'z',
'ʐ': 'z',
'ƶ': 'z',
'ɀ': 'z',
'ff': 'ff',
'ffi': 'ffi',
'ffl': 'ffl',
'fi': 'fi',
'fl': 'fl',
'ij': 'ij',
'œ': 'oe',
'st': 'st',
'ₐ': 'a',
'ₑ': 'e',
'ᵢ': 'i',
'ⱼ': 'j',
'ₒ': 'o',
'ᵣ': 'r',
'ᵤ': 'u',
'ᵥ': 'v',
'ₓ': 'x'
};
/**
* bootstrap
*/
import {Directive, TemplateRef, ViewContainerRef, Inject} from 'angular2/core';
export interface IAttribute {
[key: string]: any;
}
@Directive({
selector: '[ngTransclude]',
properties: ['ngTransclude']
})
export class NgTransclude {
private _ngTransclude: TemplateRef;
private set ngTransclude(templateRef:TemplateRef) {
this._ngTransclude = templateRef;
if (templateRef) {
this.viewRef.createEmbeddedView(templateRef);
}
}
private get ngTransclude() {
return this._ngTransclude;
}
constructor(@Inject(ViewContainerRef) public viewRef:ViewContainerRef) {
}
}
/**
* Created by Tareq Boulakjar. from angulartypescript.com
*/
import {Component, ElementRef, ViewEncapsulation} from 'angular2/core';
import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {AutocompleteContainer} from './autocomplete-container';
import {Autocomplete} from './autocomplete.component';
export const AUTOCOMPLETE_DIRECTIVES = [Autocomplete, AutocompleteContainer];
/*Angular 2 Autocomplete Example*/
@Component({
selector: 'my-app',
template:`
<div class='container-fluid'>
<h3>Angular 2 Autocomplete Example</h3>
<h4>The Selected Car: {{selectedCar}}</h4>
<input [(ngModel)]="selectedCar"
[autocomplete]="carsExample2"
(autocompleteOnSelect)="autocompleteOnSelect($event)"
[autocompleteOptionField]="'name'"
class="form-control">
<h3>Asynchronous results</h3>
<h4>Model: {{asyncSelectedCar}}</h4>
<input [(ngModel)]="asyncSelectedCar"
[autocomplete]="getAsyncData(getCurrentContext())"
(autocompleteLoading)="changeAutocompleteLoading($event)"
(autocompleteNoResults)="changeAutocompleteNoResults($event)"
(autocompleteOnSelect)="autocompleteOnSelect($event)"
[autocompleteOptionsLimit]="7"
placeholder="Locations loaded with timeout"
class="form-control">
<div [hidden]="autocompleteLoading!==true">
<i class="glyphicon glyphicon-refresh ng-hide" style=""></i>
</div>
<div [hidden]="autocompleteNoResults!==true" class="" style="">
<i class="glyphicon glyphicon-remove"></i> Empty Query !
</div>
</div>
`,
directives: [AUTOCOMPLETE_DIRECTIVES, CORE_DIRECTIVES, FORM_DIRECTIVES],
})
export class Angular2Autocomplete {
private selectedCar:string = '';
private asyncSelectedCar:string = '';
private autocompleteLoading:boolean = false;
private autocompleteNoResults:boolean = false;
private carsExample1:Array<string> = ['BMW', 'Audi','Mercedes','Porsche','Volkswagen','Opel','Maserati','Volkswagen','BMW Serie 1','BMW Serie 2'];
private carsExample2:Array<any> = [
{id: 1, name: 'BMW'},
{id: 2, name: 'Audi'},
{id: 3, name: 'Mercedes'},
{id: 4, name: 'Porsche'},
{id: 5, name: 'Volkswagen'},
{id: 6, name: 'Opel'},
{id: 7, name: 'Maserati'},
{id: 8, name: 'Volkswagen'},
{id: 9, name: 'BMW Serie 1'},
{id: 10, name: 'BMW Serie 2'},
];
private getCurrentContext() {
return this;
}
private _cachedResult:any;
private _previousContext:any;
private getAsyncData(context:any):Function {
if (this._previousContext === context) {
return this._cachedResult;
}
this._previousContext = context;
let f:Function = function ():Promise<string[]> {
let p:Promise<string[]> = new Promise((resolve:Function) => {
setTimeout(() => {
let query = new RegExp(context.asyncSelectedCar, 'ig');
return resolve(context.carsExample1.filter((state:any) => {
return query.test(state);
}));
}, 500);
});
return p;
};
this._cachedResult = f;
return this._cachedResult;
}
private changeAutocompleteLoading(e:boolean) {
this.autocompleteLoading = e;
}
private changeAutocompleteNoResults(e:boolean) {
this.autocompleteNoResults = e;
}
private autocompleteOnSelect(e:any) {
console.log(`Selected value: ${e.item}`);
}
}
/**
* bootstrap
*/
import {Component, ElementRef, ViewEncapsulation} from 'angular2/core';
import {CORE_DIRECTIVES} from 'angular2/common';
import {Autocomplete} from './autocomplete.component';
import {AutocompleteOptions} from './options.class';
import {positionService} from './position';
@Component({
selector: 'autocomplete-container',
directives: [CORE_DIRECTIVES],
template: `
<ul class="dropdown-menu"
[ngStyle]="{top: top, left: left, display: display}"
style="display: block">
<li *ngFor="#match of matches"
[class.active]="isActive(match)"
(mouseenter)="selectActive(match)">
<a href="#" (click)="selectMatch(match, $event)" tabindex="-1" [innerHtml]="hightlight(match, query)"></a>
</li>
</ul>
`,
encapsulation: ViewEncapsulation.None
})
export class AutocompleteContainer {
public parent:Autocomplete;
public query:any;
private _matches:Array<any> = [];
private _field:string;
private _active:any;
private top:string;
private left:string;
private display:string;
private placement:string;
constructor(public element:ElementRef, options:AutocompleteOptions) {
Object.assign(this, options);
}
public get matches():Array<string> {
return this._matches;
}
public set matches(value:Array<string>) {
this._matches = value;
if (this._matches.length > 0) {
this._active = this._matches[0];
}
}
public set field(value:string) {
this._field = value;
}
public position(hostEl:ElementRef) {
this.display = 'block';
this.top = '0px';
this.left = '0px';
let p = positionService
.positionElements(hostEl.nativeElement,
this.element.nativeElement.children[0],
this.placement, false);
this.top = p.top + 'px';
this.left = p.left + 'px';
}
public selectActiveMatch() {
this.selectMatch(this._active);
}
public prevActiveMatch() {
let index = this.matches.indexOf(this._active);
this._active = this.matches[index - 1 < 0 ? this.matches.length - 1 : index - 1];
}
public nextActiveMatch() {
let index = this.matches.indexOf(this._active);
this._active = this.matches[index + 1 > this.matches.length - 1 ? 0 : index + 1];
}
private selectActive(value:any) {
this._active = value;
}
private isActive(value:any):boolean {
return this._active === value;
}
private selectMatch(value:any, e:Event = null) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.parent.changeModel(value);
this.parent.autocompleteOnSelect.emit({
item: value
});
return false;
}
private hightlight(item:any, query:string) {
let itemStr:string = (typeof item === 'object' && this._field ? item[this._field] : item).toString();
//let itemStrHelper:string = (this.parent.autocompleteLatinize ? AutocompleteUtils.latinize(itemStr) : itemStr).toLowerCase();
let itemStrHelper:string =itemStr.toLowerCase();
let startIdx:number;
let tokenLen:number;
if (typeof query === 'object') {
let queryLen:number = query.length;
for (let i = 0; i < queryLen; i += 1) {
startIdx = itemStrHelper.indexOf(query[i]);
tokenLen = query[i].length;
if (startIdx >= 0 && tokenLen > 0) {
itemStr = itemStr.substring(0, startIdx) + '<strong>' + itemStr.substring(startIdx, startIdx + tokenLen) + '</strong>' + itemStr.substring(startIdx + tokenLen);
itemStrHelper = itemStrHelper.substring(0, startIdx) + ' ' + ' '.repeat(tokenLen) + ' ' + itemStrHelper.substring(startIdx + tokenLen);
}
}
} else if (query) {
startIdx = itemStrHelper.indexOf(query);
tokenLen = query.length;
if (startIdx >= 0 && tokenLen > 0) {
itemStr = itemStr.substring(0, startIdx) + '<strong>' + itemStr.substring(startIdx, startIdx + tokenLen) + '</strong>' + itemStr.substring(startIdx + tokenLen);
}
}
return itemStr;
}
}
/**
* bootstrap
*/
import {Component, ElementRef, ViewEncapsulation} from 'angular2/core';
import {CORE_DIRECTIVES} from 'angular2/common';
import {
Directive, Input, Output, HostListener,
EventEmitter, OnInit,
ElementRef, Renderer,
DynamicComponentLoader, ComponentRef, Provider, Injector
} from 'angular2/core';
import {NgModel}from 'angular2/common';
function setProperty(renderer:Renderer, elementRef:ElementRef, propName:string, propValue:any) {
renderer.setElementProperty(elementRef.nativeElement, propName, propValue);
}
import {positionService} from './position';
import {AutocompleteUtils} from './sanitize';
import {AutocompleteContainer} from './autocomplete-container';
import {AutocompleteOptions} from './options.class';
@Directive({
selector: 'autocomplete[ngModel], [ngModel][autocomplete]'
})
export class Autocomplete implements OnInit {
@Output() public autocompleteLoading:EventEmitter<boolean> = new EventEmitter();
@Output() public autocompleteNoResults:EventEmitter<boolean> = new EventEmitter();
@Output() public autocompleteOnSelect:EventEmitter<{item: any}> = new EventEmitter();
@Input() public autocomplete:any;
@Input() public autocompleteMinLength:number;
@Input() public autocompleteWaitMs:number;
@Input() public autocompleteOptionsLimit:number;
@Input() public autocompleteOptionField:string;
@Input() public autocompleteAsync:boolean = null;
@Input() public autocompleteLatinize:boolean = true;
@Input() public autocompleteSingleWords:boolean = true;
@Input() public autocompleteWordDelimiters:string = ' ';
@Input() public autocompletePhraseDelimiters:string = '\'"';
public container:AutocompleteContainer;
private debouncer:Function;
private _matches:Array<any> = [];
private placement:string = 'bottom-left';
private popup:Promise<ComponentRef>;
constructor(private cd:NgModel,
private element:ElementRef,
private renderer:Renderer,
private loader:DynamicComponentLoader) {
}
public get matches() {
return this._matches;
}
private debounce(func:Function, wait:number):Function {
let timeout:any;
let args:Array<any>;
let timestamp:number;
let waitOriginal:number = wait;
return function () {
// save details of latest call
args = [].slice.call(arguments, 0);
timestamp = Date.now();
// this trick is about implementing of 'autocompleteWaitMs'
// in this case we have adaptive 'wait' parameter
// we should use standard 'wait'('waitOriginal') in case of
// popup is opened, otherwise - 'autocompleteWaitMs' parameter
wait = this.container ? waitOriginal : this.autocompleteWaitMs;
// this is where the magic happens
let later = function () {
// how long ago was the last call
let last = Date.now() - timestamp;
// if the latest call was less that the wait period ago
// then we reset the timeout to wait for the difference
if (last < wait) {
timeout = setTimeout(later, wait - last);
// or if not we can null out the timer and run the latest
} else {
timeout = null;
func.apply(this, args);
}
};
// we only need to set the timer now if one isn't already running
if (!timeout) {
timeout = setTimeout(later, wait);
}
};
}
private processMatches() {
this._matches = [];
if (this.cd.model.toString().length >= this.autocompleteMinLength) {
// If singleWords, break model here to not be doing extra work on each iteration
let normalizedQuery = (this.autocompleteLatinize ? AutocompleteUtils.latinize(this.cd.model) : this.cd.model).toString().toLowerCase();
normalizedQuery = this.autocompleteSingleWords ? AutocompleteUtils.tokenize(normalizedQuery, this.autocompleteWordDelimiters, this.autocompletePhraseDelimiters) : normalizedQuery;
for (let i = 0; i < this.autocomplete.length; i++) {
let match:string;
if (typeof this.autocomplete[i] === 'object' &&
this.autocomplete[i][this.autocompleteOptionField]) {
match = this.autocompleteLatinize ? AutocompleteUtils.latinize(this.autocomplete[i][this.autocompleteOptionField].toString()) : this.autocomplete[i][this.autocompleteOptionField].toString();
}
if (typeof this.autocomplete[i] === 'string') {
match = this.autocompleteLatinize ? AutocompleteUtils.latinize(this.autocomplete[i].toString()) : this.autocomplete[i].toString();
}
if (!match) {
console.log('Invalid match type', typeof this.autocomplete[i], this.autocompleteOptionField);
continue;
}
if (this.testMatch(match.toLowerCase(), normalizedQuery)) {
this._matches.push(this.autocomplete[i]);
if (this._matches.length > this.autocompleteOptionsLimit - 1) {
break;
}
}
}
}
}
private testMatch(match:string, test:any) {
let spaceLength:number;
if (typeof test === 'object') {
spaceLength = test.length;
for (let i = 0; i < spaceLength; i += 1) {
if (test[i].length > 0 && match.indexOf(test[i]) < 0) {
return false;
}
}
return true;
} else {
return match.indexOf(test) >= 0;
}
}
private finalizeAsyncCall() {
this.autocompleteLoading.emit(false);
this.autocompleteNoResults.emit(this.cd.model.toString().length >=
this.autocompleteMinLength && this.matches.length <= 0);
if (this.cd.model.toString().length <= 0 || this._matches.length <= 0) {
this.hide();
return;
}
if (this.container && this._matches.length > 0) {
// This improves the speedas it won't have to be done for each list item
let normalizedQuery = (this.autocompleteLatinize ? AutocompleteUtils.latinize(this.cd.model) : this.cd.model).toString().toLowerCase();
this.container.query = this.autocompleteSingleWords ? AutocompleteUtils.tokenize(normalizedQuery, this.autocompleteWordDelimiters, this.autocompletePhraseDelimiters) : normalizedQuery;
this.container.matches = this._matches;
}
if (!this.container && this._matches.length > 0) {
this.show(this._matches);
}
}
ngOnInit() {
this.autocompleteOptionsLimit = this.autocompleteOptionsLimit || 20;
this.autocompleteMinLength = this.autocompleteMinLength || 1;
this.autocompleteWaitMs = this.autocompleteWaitMs || 0;
// async should be false in case of array
if (this.autocompleteAsync === null && typeof this.autocomplete !== 'function') {
this.autocompleteAsync = false;
}
// async should be true for any case of function
if (typeof this.autocomplete === 'function') {
this.autocompleteAsync = true;
}
if (this.autocompleteAsync === true) {
this.debouncer = this.debounce(() => {
if (typeof this.autocomplete === 'function') {
this.autocomplete().then((matches:any[]) => {
this._matches = [];
if (this.cd.model.toString().length >= this.autocompleteMinLength) {
for (let i = 0; i < matches.length; i++) {
this._matches.push(matches[i]);
if (this._matches.length > this.autocompleteOptionsLimit - 1) {
break;
}
}
}
this.finalizeAsyncCall();
});
}
// source is array
if (typeof this.autocomplete === 'object' && this.autocomplete.length) {
this.processMatches();
this.finalizeAsyncCall();
}
}, 100);
}
}
@HostListener('keyup', ['$event'])
onChange(e:KeyboardEvent) {
if (this.container) {
// esc
if (e.keyCode === 27) {
this.hide();
return;
}
// up
if (e.keyCode === 38) {
this.container.prevActiveMatch();
return;
}
// down
if (e.keyCode === 40) {
this.container.nextActiveMatch();
return;
}
// enter
if (e.keyCode === 13) {
this.container.selectActiveMatch();
return;
}
}
this.autocompleteLoading.emit(true);
if (this.autocompleteAsync === true) {
this.debouncer();
}
if (this.autocompleteAsync === false) {
this.processMatches();
this.finalizeAsyncCall();
}
}
public changeModel(value:any) {
let valueStr:string = ((typeof value === 'object' && this.autocompleteOptionField) ? value[this.autocompleteOptionField] : value).toString();
this.cd.viewToModelUpdate(valueStr);
setProperty(this.renderer, this.element, 'value', valueStr);
this.hide();
}
show(matches:Array<any>) {
let options = new AutocompleteOptions({
placement: this.placement,
animation: false
});
let binding = Injector.resolve([
new Provider(AutocompleteOptions, {useValue: options})
]);
this.popup = this.loader
.loadNextToLocation(AutocompleteContainer, this.element, binding)
.then((componentRef:ComponentRef) => {
componentRef.instance.position(this.element);
this.container = componentRef.instance;
this.container.parent = this;
// This improves the speedas it won't have to be done for each list item
let normalizedQuery = (this.autocompleteLatinize ? AutocompleteUtils.latinize(this.cd.model) : this.cd.model).toString().toLowerCase();
this.container.query = this.autocompleteSingleWords ? AutocompleteUtils.tokenize(normalizedQuery, this.autocompleteWordDelimiters, this.autocompletePhraseDelimiters) : normalizedQuery;
this.container.matches = matches;
this.container.field = this.autocompleteOptionField;
this.element.nativeElement.focus();
return componentRef;
});
}
hide() {
if (this.container) {
this.popup.then((componentRef:ComponentRef) => {
componentRef.dispose();
this.container = null;
return componentRef;
});
}
}
}