Showing preview only (513K chars total). Download the full file or copy to clipboard to get everything.
Repository: twitter/typeahead.js
Branch: master
Commit: 588440f66559
Files: 66
Total size: 490.7 KB
Directory structure:
gitextract_0m8m_xl4/
├── .gitignore
├── .jshintrc
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── composer.json
├── dist/
│ ├── bloodhound.js
│ ├── typeahead.bundle.js
│ └── typeahead.jquery.js
├── doc/
│ ├── bloodhound.md
│ ├── jquery_typeahead.md
│ └── migration/
│ └── 0.10.0.md
├── karma.conf.js
├── package.json
├── src/
│ ├── bloodhound/
│ │ ├── bloodhound.js
│ │ ├── lru_cache.js
│ │ ├── options_parser.js
│ │ ├── persistent_storage.js
│ │ ├── prefetch.js
│ │ ├── remote.js
│ │ ├── search_index.js
│ │ ├── tokenizers.js
│ │ ├── transport.js
│ │ └── version.js
│ ├── common/
│ │ └── utils.js
│ └── typeahead/
│ ├── dataset.js
│ ├── default_menu.js
│ ├── event_bus.js
│ ├── event_emitter.js
│ ├── highlight.js
│ ├── input.js
│ ├── menu.js
│ ├── plugin.js
│ ├── typeahead.js
│ └── www.js
├── test/
│ ├── bloodhound/
│ │ ├── bloodhound_spec.js
│ │ ├── lru_cache_spec.js
│ │ ├── options_parser_spec.js
│ │ ├── persistent_storage_spec.js
│ │ ├── prefetch_spec.js
│ │ ├── remote_spec.js
│ │ ├── search_index_spec.js
│ │ ├── tokenizers_spec.js
│ │ └── transport_spec.js
│ ├── ci
│ ├── fixtures/
│ │ ├── ajax_responses.js
│ │ ├── data.js
│ │ └── html.js
│ ├── helpers/
│ │ └── typeahead_mocks.js
│ ├── integration/
│ │ ├── test.html
│ │ └── test.js
│ ├── playground.html
│ └── typeahead/
│ ├── dataset_spec.js
│ ├── default_results_spec.js
│ ├── event_bus_spec.js
│ ├── event_emitter_spec.js
│ ├── highlight_spec.js
│ ├── input_spec.js
│ ├── plugin_spec.js
│ ├── results_spec.js
│ └── typeahead_spec.js
└── typeahead.js.jquery.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.swp
.DS_Store
.grunt
_SpecRunner.html
test/coverage
dist_temp
node_modules
npm-debug.log
bower_components
*.iml
.idea
================================================
FILE: .jshintrc
================================================
{
"curly": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"trailing": true,
"boss": true,
"eqnull": true,
"expr": true,
"validthis": true,
"browser": true,
"jquery": true
}
================================================
FILE: .travis.yml
================================================
language: node_js
env:
matrix:
- TEST_SUITE=unit
- TEST_SUITE=integration BROWSER='firefox'
- TEST_SUITE=integration BROWSER='firefox:3.5'
- TEST_SUITE=integration BROWSER='firefox:3.6'
- TEST_SUITE=integration BROWSER='safari:5'
- TEST_SUITE=integration BROWSER='safari:6'
- TEST_SUITE=integration BROWSER='safari:7'
- TEST_SUITE=integration BROWSER='internet explorer:8'
- TEST_SUITE=integration BROWSER='internet explorer:9'
- TEST_SUITE=integration BROWSER='internet explorer:10'
- TEST_SUITE=integration BROWSER='internet explorer:11'
- TEST_SUITE=integration BROWSER='chrome'
global:
- secure: VY4J2ERfrMEin++f4+UDDtTMWLuE3jaYAVchRxfO2c6PQUYgR+SW4SMekz855U/BuptMtiVMR2UUoNGMgOSKIFkIXpPfHhx47G5a541v0WNjXfQ2qzivXAWaXNK3l3C58z4dKxgPWsFY9JtMVCddJd2vQieAILto8D8G09p7bpo=
- secure: kehbNCoYUG2gLnhmCH/oKhlJG6LoxgcOPMCtY7KOI4ropG8qlypb+O2b/19+BWeO3aIuMB0JajNh3p2NL0UKgLmUK7EYBA9fQz+vesFReRk0V/KqMTSxHJuseM4aLOWA2Wr9US843VGltfODVvDN5sNrfY7RcoRx2cTK/k1CXa8=
node_js:
- 0.11.13
before_script:
- npm install -g grunt-cli@0.1.13
- npm install -g node-static@0.7.3
- npm install -g bower@1.3.8
- bower install
- grunt build
script: test/ci
addons:
sauce_connect: true
================================================
FILE: CHANGELOG.md
================================================
Changelog
=========
For transparency and insight into our release cycle, releases will be numbered
with the follow format:
`<major>.<minor>.<patch>`
And constructed with the following guidelines:
* Breaking backwards compatibility bumps the major
* New additions without breaking backwards compatibility bumps the minor
* Bug fixes and misc changes bump the patch
For more information on semantic versioning, please visit http://semver.org/.
---
### 0.11.1 April 26, 2015
* Add prepare option to prefetch. [#1181]
* Handle QuotaExceededError. [#1110]
* Escape HTML entities from suggestion display value when rendering with default
template. [#964]
* List jquery as a dependency in package.json. [#1143]
### 0.11.0 April 25, 2015
An overhaul of typeahead.js – consider this a release candidate for v1. There
are bunch of API changes with this release so don't expect backwards
compatibility with previous versions. There are also many new undocumented
features that have been introduced. Documentation for those features will be
added before v1 ships.
Beware that since this release is pretty much a rewrite, there are bound to be
some bugs. To be safe, you should consider this release beta software and
throughly test your integration of it before using it in production
environments. This caveat only applies to this release as subsequent releases
will address any issues that come up.
### 0.10.5 August 7, 2014
* Increase supported version range for jQuery dependency. [#917]
### 0.10.4 July 13, 2014
**Hotfix**
* Fix regression that breaks Bloodhound instances when more than 1 instance is
relying on remote data. [#899]
### 0.10.3 July 10, 2014
**Bug fixes**
* `Bloodhound#clearPrefetchCache` now works with cache keys that contain regex
characters. [#771]
* Prevent outdated network requests from being sent. [#809]
* Add support to object tokenizers for multiple property tokenization. [#811]
* Fix broken `jQuery#typeahead('val')` method. [#815]
* Remove `disabled` attribute from the hint input control. [#839]
* Add `tt-highlight` class to highlighted text. [#833]
* Handle non-string types that are passed to `jQuery#typeahead('val', val)`. [#881]
### 0.10.2 March 10, 2014
* Prevent flickering of dropdown menu when requesting remote suggestions. [#718]
* Reduce hint flickering. [#754]
* Added `Bloodhound#{clear, clearPrefetchCache, clearRemoteCache}` and made it
possible to reinitialize Bloodhound instances. [#703]
* Invoke `local` function during initialization. [#687]
* In addition to HTML strings, templates can now return DOM nodes. [#742]
* Prevent `jQuery#typeahead('val', val)` from opening dropdown menus of
non-active typeaheads. [#646]
* Fix bug in IE that resulted in dropdown menus with overflow being closed
when clicking on the scrollbar. [#705]
* Only show dropdown menu if `minLength` is satisfied. [#710]
### 0.10.1 February 9, 2014
**Hotfix**
* Fixed bug that prevented some ajax configs from being respected. [#630]
* Event delegation on suggestion clicks is no longer broken. [#118]
* Ensure dataset names are valid class name suffixes. [#610]
* Added support for `displayKey` to be a function. [#633]
* `jQuery#typeahead('val')` now mirrors `jQuery#val()`. [#659]
* Datasets can now be passed to jQuery plugin as an array. [#664]
* Added a `noConflict` method to the jQuery plugin. [#612]
* Bloodhound's `local` property can now be a function. [#485]
### 0.10.0 February 2, 2014
**Introducting Bloodhound**
This release was almost a complete rewrite of typeahead.js and will hopefully
lay the foundation for the 1.0.0 release. It's impossible to enumerate all of
the issues that were fixed. If you want to get an idea of what issues 0.10.0
resolved, take a look at the closed issues in the [0.10.0 milestone].
The most important change in 0.10.0 is that typeahead.js was broken up into 2
individual components: Bloodhound and jQuery#typeahead. Bloodhound is an
feature-rich suggestion engine. jQuery#typeahead is a jQuery plugin that turns
input controls into typeaheads.
It's impossible to write a typeahead library that supports every use-case out
of the box – that was the main motivation behind decomposing typeahead.js.
Previously, some prospective typeahead.js users were unable to use the library
because either the suggestion engine or the typeahead UI did not meet their
requirements. In those cases, they were either forced to fork typeahead.js and
make the necessary modifications or they had to give up on using typeahead.js
entirely. Now they have the option of swapping out the component that doesn't
work for them with a custom implementation.
### 0.9.3 June 24, 2013
* Ensure cursor visibility in menus with overflow. [#209]
* Fixed bug that led to the menu staying open when it should have been closed. [#260]
* Private browsing in Safari no longer breaks prefetch. [#270]
* Pressing tab while a suggestion is highlighted now results in a selection. [#266]
* Dataset name is now passed as an argument for typeahead:selected event. [#207]
### 0.9.2 April 14, 2013
* Prefetch usage no longer breaks when cookies are disabled. [#190]
* Precompiled templates are now wrapped in the appropriate DOM element. [#172]
### 0.9.1 April 1, 2013
* Multiple requests no longer get sent for a query when datasets share a remote source. [#152]
* Datasets now support precompiled templates. [#137]
* Cached remote suggestions now get rendered immediately. [#156]
* Added typeahead:autocompleted event. [#132]
* Added a plugin method for programmatically setting the query. Experimental. [#159]
* Added minLength option for datasets. Experimental. [#131]
* Prefetch objects now support thumbprint option. Experimental. [#157]
### 0.9.0 March 24, 2013
**Custom events, no more typeahead.css, and an improved API**
* Implemented the triggering of custom events. [#106]
* Got rid of typeahead.css and now apply styling through JavaScript. [#15]
* Made the API more flexible and addressed a handful of remote issues by rewriting the transport component. [#25]
* Added support for dataset headers and footers. [#81]
* No longer cache unnamed datasets. [#116]
* Made the key name of the value property configurable. [#115]
* Input values set before initialization of typeaheads are now respected. [#109]
* Fixed an input value/hint casing bug. [#108]
### 0.8.2 March 04, 2013
* Fixed bug causing error to be thrown when initializing a typeahead on multiple elements. [#51]
* Tokens with falsy values are now filtered out – was causing wonky behavior. [#75]
* No longer making remote requests for blank queries. [#74]
* Datums with regex characters in their value no longer cause errors. [#77]
* Now compatible with the Closure Compiler. [#48]
* Reference to jQuery is now obtained through window.jQuery, not window.$. [#47]
* Added a plugin method for destroying typeaheads. Won't be documented until v0.9 and might change before then. [#59]
### 0.8.1 February 25, 2013
* Fixed bug preventing local and prefetch from being used together. [#39]
* No longer prevent default browser behavior when up or down arrow is pressed with a modifier. [#6]
* Hint is hidden when user entered query is wider than the input. [#26]
* Data stored in localStorage now expires properly. [#34]
* Normalized search tokens and fixed query tokenization. [#38]
* Remote suggestions now are appended, not prepended to suggestions list. [#40]
* Fixed some typos through the codebase. [#3]
### 0.8.0 February 19, 2013
**Initial public release**
* Prefetch and search data locally insanely fast.
* Search hard-coded, prefetched, and/or remote data.
* Hinting.
* RTL/IME/international support.
* Search multiple datasets.
* Share datasets (and caching) between multiple inputs.
* And much, much more...
[0.10.0 milestone]: https://github.com/twitter/typeahead.js/issues?milestone=8&page=1&state=closed
[#1181]: https://github.com/twitter/typeahead.js/pull/1181
[#1143]: https://github.com/twitter/typeahead.js/pull/1143
[#1110]: https://github.com/twitter/typeahead.js/pull/1110
[#964]: https://github.com/twitter/typeahead.js/pull/964
[#917]: https://github.com/twitter/typeahead.js/pull/917
[#899]: https://github.com/twitter/typeahead.js/pull/899
[#881]: https://github.com/twitter/typeahead.js/pull/881
[#839]: https://github.com/twitter/typeahead.js/pull/839
[#833]: https://github.com/twitter/typeahead.js/pull/833
[#815]: https://github.com/twitter/typeahead.js/pull/815
[#811]: https://github.com/twitter/typeahead.js/pull/811
[#809]: https://github.com/twitter/typeahead.js/pull/809
[#771]: https://github.com/twitter/typeahead.js/pull/771
[#754]: https://github.com/twitter/typeahead.js/pull/754
[#742]: https://github.com/twitter/typeahead.js/pull/742
[#718]: https://github.com/twitter/typeahead.js/pull/718
[#710]: https://github.com/twitter/typeahead.js/pull/710
[#705]: https://github.com/twitter/typeahead.js/pull/705
[#703]: https://github.com/twitter/typeahead.js/pull/703
[#687]: https://github.com/twitter/typeahead.js/pull/687
[#664]: https://github.com/twitter/typeahead.js/pull/664
[#659]: https://github.com/twitter/typeahead.js/pull/659
[#646]: https://github.com/twitter/typeahead.js/pull/646
[#633]: https://github.com/twitter/typeahead.js/pull/633
[#630]: https://github.com/twitter/typeahead.js/pull/630
[#612]: https://github.com/twitter/typeahead.js/pull/612
[#610]: https://github.com/twitter/typeahead.js/pull/610
[#485]: https://github.com/twitter/typeahead.js/pull/485
[#270]: https://github.com/twitter/typeahead.js/pull/270
[#266]: https://github.com/twitter/typeahead.js/pull/266
[#260]: https://github.com/twitter/typeahead.js/pull/260
[#209]: https://github.com/twitter/typeahead.js/pull/209
[#207]: https://github.com/twitter/typeahead.js/pull/207
[#190]: https://github.com/twitter/typeahead.js/pull/190
[#172]: https://github.com/twitter/typeahead.js/pull/172
[#159]: https://github.com/twitter/typeahead.js/pull/159
[#157]: https://github.com/twitter/typeahead.js/pull/157
[#156]: https://github.com/twitter/typeahead.js/pull/156
[#152]: https://github.com/twitter/typeahead.js/pull/152
[#137]: https://github.com/twitter/typeahead.js/pull/137
[#132]: https://github.com/twitter/typeahead.js/pull/132
[#131]: https://github.com/twitter/typeahead.js/pull/131
[#118]: https://github.com/twitter/typeahead.js/pull/118
[#116]: https://github.com/twitter/typeahead.js/pull/116
[#115]: https://github.com/twitter/typeahead.js/pull/115
[#109]: https://github.com/twitter/typeahead.js/pull/109
[#108]: https://github.com/twitter/typeahead.js/pull/108
[#106]: https://github.com/twitter/typeahead.js/pull/106
[#81]: https://github.com/twitter/typeahead.js/pull/81
[#77]: https://github.com/twitter/typeahead.js/pull/77
[#75]: https://github.com/twitter/typeahead.js/pull/75
[#74]: https://github.com/twitter/typeahead.js/pull/74
[#59]: https://github.com/twitter/typeahead.js/pull/59
[#51]: https://github.com/twitter/typeahead.js/pull/51
[#48]: https://github.com/twitter/typeahead.js/pull/48
[#47]: https://github.com/twitter/typeahead.js/pull/47
[#40]: https://github.com/twitter/typeahead.js/pull/40
[#39]: https://github.com/twitter/typeahead.js/pull/39
[#38]: https://github.com/twitter/typeahead.js/pull/38
[#34]: https://github.com/twitter/typeahead.js/pull/34
[#26]: https://github.com/twitter/typeahead.js/pull/26
[#25]: https://github.com/twitter/typeahead.js/pull/25
[#15]: https://github.com/twitter/typeahead.js/pull/15
[#6]: https://github.com/twitter/typeahead.js/pull/6
[#3]: https://github.com/twitter/typeahead.js/pull/3
================================================
FILE: CONTRIBUTING.md
================================================
Contributing to typeahead.js
============================
*These contributing guidelines were proudly stolen from the
[Flight](https://github.com/flightjs/flight) project*
Looking to contribute something to typeahead.js? Here's how you can help.
Bugs Reports
------------
A bug is a _demonstrable problem_ that is caused by the code in the
repository. Good bug reports are extremely helpful – thank you!
Guidelines for bug reports:
1. **Use the GitHub issue search** — check if the issue has already been
reported.
2. **Check if the issue has been fixed** — try to reproduce it using the
latest `master` or integration branch in the repository.
3. **Isolate the problem** — ideally create a reduced test
case and a live example.
4. Please try to be as detailed as possible in your report. Include specific
information about the environment – operating system and version, browser
and version, version of typeahead.js – and steps required to reproduce the
issue.
Feature Requests & Contribution Enquiries
-----------------------------------------
Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case for the inclusion of your feature. Please provide as much detail and
context as possible.
Contribution enquiries should take place before any significant pull request,
otherwise you risk spending a lot of time working on something that we might
have good reasons for rejecting.
Pull Requests
-------------
Good pull requests – patches, improvements, new features – are a fantastic
help. They should remain focused in scope and avoid containing unrelated
commits.
Make sure to adhere to the coding conventions used throughout the codebase
(indentation, accurate comments, etc.) and any other requirements (such as test
coverage).
Please follow this process; it's the best way to get your work included in the
project:
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
and configure the remotes:
```bash
# Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/typeahead.js
# Navigate to the newly cloned directory
cd <repo-name>
# Assign the original repo to a remote called "upstream"
git remote add upstream git://github.com/twitter/typeahead.js
```
2. If you cloned a while ago, get the latest changes from upstream:
```bash
git checkout master
git pull upstream master
```
3. Install the dependencies (you must have Node.js and [Bower](http://bower.io)
installed), and create a new topic branch (off the main project development
branch) to contain your feature, change, or fix:
```bash
npm install
bower install
git checkout -b <topic-branch-name>
```
4. Make sure to update, or add to the tests when appropriate. Patches and
features will not be accepted without tests. Run `npm test` to check that
all tests pass after you've made changes.
5. Commit your changes in logical chunks. Provide clear and explanatory commit
messages. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up
your commits before making them public.
6. Locally merge (or rebase) the upstream development branch into your topic branch:
```bash
git pull [--rebase] upstream master
```
7. Push your topic branch up to your fork:
```bash
git push origin <topic-branch-name>
```
8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
with a clear title and description.
9. If you are asked to amend your changes before they can be merged in, please
use `git commit --amend` (or rebasing for multi-commit Pull Requests) and
force push to your remote feature branch. You may also be asked to squash
commits.
License
-------
By contributing your code,
You agree to license your contribution under the terms of the MIT License
https://github.com/twitter/typeahead.js/blob/master/LICENSE
================================================
FILE: Gruntfile.js
================================================
var semver = require('semver'),
f = require('util').format,
files = {
common: [
'src/common/utils.js'
],
bloodhound: [
'src/bloodhound/version.js',
'src/bloodhound/tokenizers.js',
'src/bloodhound/lru_cache.js',
'src/bloodhound/persistent_storage.js',
'src/bloodhound/transport.js',
'src/bloodhound/search_index.js',
'src/bloodhound/prefetch.js',
'src/bloodhound/remote.js',
'src/bloodhound/options_parser.js',
'src/bloodhound/bloodhound.js'
],
typeahead: [
'src/typeahead/www.js',
'src/typeahead/event_bus.js',
'src/typeahead/event_emitter.js',
'src/typeahead/highlight.js',
'src/typeahead/input.js',
'src/typeahead/dataset.js',
'src/typeahead/menu.js',
'src/typeahead/default_menu.js',
'src/typeahead/typeahead.js',
'src/typeahead/plugin.js'
]
};
module.exports = function(grunt) {
grunt.initConfig({
version: grunt.file.readJSON('package.json').version,
tempDir: 'dist_temp',
buildDir: 'dist',
banner: [
'/*!',
' * typeahead.js <%= version %>',
' * https://github.com/twitter/typeahead.js',
' * Copyright 2013-<%= grunt.template.today("yyyy") %> Twitter, Inc. and other contributors; Licensed MIT',
' */\n\n'
].join('\n'),
uglify: {
options: {
banner: '<%= banner %>'
},
concatBloodhound: {
options: {
mangle: false,
beautify: true,
compress: false,
banner: ''
},
src: files.common.concat(files.bloodhound),
dest: '<%= tempDir %>/bloodhound.js'
},
concatTypeahead: {
options: {
mangle: false,
beautify: true,
compress: false,
banner: ''
},
src: files.common.concat(files.typeahead),
dest: '<%= tempDir %>/typeahead.jquery.js'
},
bloodhound: {
options: {
mangle: false,
beautify: true,
compress: false
},
src: '<%= tempDir %>/bloodhound.js',
dest: '<%= buildDir %>/bloodhound.js'
},
bloodhoundMin: {
options: {
mangle: true,
compress: {}
},
src: '<%= tempDir %>/bloodhound.js',
dest: '<%= buildDir %>/bloodhound.min.js'
},
typeahead: {
options: {
mangle: false,
beautify: true,
compress: false
},
src: '<%= tempDir %>/typeahead.jquery.js',
dest: '<%= buildDir %>/typeahead.jquery.js'
},
typeaheadMin: {
options: {
mangle: true,
compress: {}
},
src: '<%= tempDir %>/typeahead.jquery.js',
dest: '<%= buildDir %>/typeahead.jquery.min.js'
},
bundle: {
options: {
mangle: false,
beautify: true,
compress: false
},
src: [
'<%= tempDir %>/bloodhound.js',
'<%= tempDir %>/typeahead.jquery.js'
],
dest: '<%= buildDir %>/typeahead.bundle.js'
},
bundleMin: {
options: {
mangle: true,
compress: {}
},
src: [
'<%= tempDir %>/bloodhound.js',
'<%= tempDir %>/typeahead.jquery.js'
],
dest: '<%= buildDir %>/typeahead.bundle.min.js'
}
},
umd: {
bloodhound: {
src: '<%= tempDir %>/bloodhound.js',
objectToExport: 'Bloodhound',
amdModuleId: 'bloodhound',
deps: {
default: ['$'],
amd: ['jquery'],
cjs: ['jquery'],
global: ['jQuery']
}
},
typeahead: {
src: '<%= tempDir %>/typeahead.jquery.js',
amdModuleId: 'typeahead.js',
deps: {
default: ['$'],
amd: ['jquery'],
cjs: ['jquery'],
global: ['jQuery']
}
}
},
sed: {
version: {
pattern: '%VERSION%',
replacement: '<%= version %>',
recursive: true,
path: '<%= buildDir %>'
}
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
src: 'src/**/*.js',
test: ['test/**/*_spec.js', 'test/integration/test.js'],
gruntfile: ['Gruntfile.js']
},
watch: {
js: {
files: 'src/**/*',
tasks: 'build'
}
},
exec: {
npm_publish: 'npm publish',
git_is_clean: 'test -z "$(git status --porcelain)"',
git_on_master: 'test $(git symbolic-ref --short -q HEAD) = master',
git_add: 'git add .',
git_push: 'git push && git push --tags',
git_commit: {
cmd: function(m) { return f('git commit -m "%s"', m); }
},
git_tag: {
cmd: function(v) { return f('git tag v%s -am "%s"', v, v); }
},
publish_assets: [
'cp -r <%= buildDir %> typeahead.js',
'zip -r typeahead.js/typeahead.js.zip typeahead.js',
'git checkout gh-pages',
'rm -rf releases/latest',
'cp -r typeahead.js releases/<%= version %>',
'cp -r typeahead.js releases/latest',
'git add releases/<%= version %> releases/latest',
'sed -E -i "" \'s/v[0-9]+\\.[0-9]+\\.[0-9]+/v<%= version %>/\' index.html',
'git add index.html',
'git commit -m "Add assets for <%= version %>."',
'git push',
'git checkout -',
'rm -rf typeahead.js'
].join(' && ')
},
clean: {
dist: 'dist'
},
connect: {
server: {
options: { port: 8888, keepalive: true }
}
},
concurrent: {
options: { logConcurrentOutput: true },
dev: ['server', 'watch']
},
step: {
options: {
option: false
}
}
});
grunt.registerTask('release', '#shipit', function(version) {
var curVersion = grunt.config.get('version');
version = semver.inc(curVersion, version) || version;
if (!semver.valid(version) || semver.lte(version, curVersion)) {
grunt.fatal('hey dummy, that version is no good!');
}
grunt.config.set('version', version);
grunt.task.run([
'exec:git_on_master',
'exec:git_is_clean',
f('step:Update to version %s?', version),
f('manifests:%s', version),
'build',
'exec:git_add',
f('exec:git_commit:%s', version),
f('exec:git_tag:%s', version),
'step:Push changes?',
'exec:git_push',
'step:Publish to npm?',
'exec:npm_publish',
'step:Publish assets?',
'exec:publish_assets'
]);
});
grunt.registerTask('manifests', 'Update manifests.', function(version) {
var _ = grunt.util._,
pkg = grunt.file.readJSON('package.json'),
bower = grunt.file.readJSON('bower.json'),
jqueryPlugin = grunt.file.readJSON('typeahead.js.jquery.json');
bower = JSON.stringify(_.extend(bower, {
name: pkg.name,
version: version
}), null, 2);
jqueryPlugin = JSON.stringify(_.extend(jqueryPlugin, {
name: pkg.name,
title: pkg.name,
version: version,
author: pkg.author,
description: pkg.description,
keywords: pkg.keywords,
homepage: pkg.homepage,
bugs: pkg.bugs,
maintainers: pkg.contributors
}), null, 2);
pkg = JSON.stringify(_.extend(pkg, {
version: version
}), null, 2);
grunt.file.write('package.json', pkg);
grunt.file.write('bower.json', bower);
grunt.file.write('typeahead.js.jquery.json', jqueryPlugin);
});
// aliases
// -------
grunt.registerTask('default', 'build');
grunt.registerTask('server', 'connect:server');
grunt.registerTask('lint', 'jshint');
grunt.registerTask('dev', ['build', 'concurrent:dev']);
grunt.registerTask('build', [
'uglify:concatBloodhound',
'uglify:concatTypeahead',
'umd:bloodhound',
'umd:typeahead',
'uglify:bloodhound',
'uglify:bloodhoundMin',
'uglify:typeahead',
'uglify:typeaheadMin',
'uglify:bundle',
'uglify:bundleMin',
'sed:version'
]);
// load tasks
// ----------
grunt.loadNpmTasks('grunt-umd');
grunt.loadNpmTasks('grunt-sed');
grunt.loadNpmTasks('grunt-exec');
grunt.loadNpmTasks('grunt-step');
grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-connect');
};
================================================
FILE: LICENSE
================================================
Copyright (c) 2013-2014 Twitter, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
[](http://travis-ci.org/twitter/typeahead.js)
[](http://gruntjs.com/)
[typeahead.js][gh-page]
=======================
Inspired by [twitter.com]'s autocomplete search functionality, typeahead.js is
a flexible JavaScript library that provides a strong foundation for building
robust typeaheads.
The typeahead.js library consists of 2 components: the suggestion engine,
[Bloodhound], and the UI view, [Typeahead].
The suggestion engine is responsible for computing suggestions for a given
query. The UI view is responsible for rendering suggestions and handling DOM
interactions. Both components can be used separately, but when used together,
they can provide a rich typeahead experience.
<!-- section links -->
[gh-page]: http://twitter.github.io/typeahead.js/
[twitter.com]: https://twitter.com
[Bloodhound]: https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md
[Typeahead]: https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md
Getting Started
---------------
How you acquire typeahead.js is up to you.
Preferred method:
* Install with [Bower]: `$ bower install typeahead.js`
Other methods:
* [Download zipball of latest release][zipball].
* Download the latest dist files individually:
* *[bloodhound.js]* (standalone suggestion engine)
* *[typeahead.jquery.js]* (standalone UI view)
* *[typeahead.bundle.js]* (*bloodhound.js* + *typeahead.jquery.js*)
* *[typeahead.bundle.min.js]*
**Note:** both *bloodhound.js* and *typeahead.jquery.js* have a dependency on
[jQuery] 1.9+.
<!-- section links -->
[Bower]: http://bower.io/
[zipball]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.js.zip
[bloodhound.js]: http://twitter.github.com/typeahead.js/releases/latest/bloodhound.js
[typeahead.jquery.js]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.jquery.js
[typeahead.bundle.js]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.bundle.js
[typeahead.bundle.min.js]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.bundle.min.js
[jQuery]: http://jquery.com/
Documentation
-------------
* [Typeahead Docs]
* [Bloodhound Docs]
[Typeahead Docs]: https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md
[Bloodhound Docs]: https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md
Examples
--------
For some working examples of typeahead.js, visit the [examples page].
<!-- section links -->
[examples page]: http://twitter.github.io/typeahead.js/examples
Browser Support
---------------
* Chrome
* Firefox 3.5+
* Safari 4+
* Internet Explorer 8+
* Opera 11+
**NOTE:** typeahead.js is not tested on mobile browsers.
Customer Support
----------------
For general questions about typeahead.js, tweet at [@typeahead].
For technical questions, you should post a question on [Stack Overflow] and tag
it with [typeahead.js][so tag].
<!-- section links -->
[Stack Overflow]: http://stackoverflow.com/
[@typeahead]: https://twitter.com/typeahead
[so tag]: http://stackoverflow.com/questions/tagged/typeahead.js
Issues
------
Discovered a bug? Please create an issue here on GitHub!
https://github.com/twitter/typeahead.js/issues
Versioning
----------
For transparency and insight into our release cycle, releases will be numbered
with the following format:
`<major>.<minor>.<patch>`
And constructed with the following guidelines:
* Breaking backwards compatibility bumps the major
* New additions without breaking backwards compatibility bumps the minor
* Bug fixes and misc changes bump the patch
For more information on semantic versioning, please visit http://semver.org/.
Testing
-------
Tests are written using [Jasmine] and ran with [Karma]. To run
the test suite with PhantomJS, run `$ npm test`.
<!-- section links -->
[Jasmine]: http://jasmine.github.io/
[Karma]: http://karma-runner.github.io/
Developers
----------
If you plan on contributing to typeahead.js, be sure to read the
[contributing guidelines]. A good starting place for new contributors are issues
labeled with [entry-level]. Entry-level issues tend to require minor changes
and provide developers a chance to get more familiar with typeahead.js before
taking on more challenging work.
In order to build and test typeahead.js, you'll need to install its dev
dependencies (`$ npm install`) and have [grunt-cli]
installed (`$ npm install -g grunt-cli`). Below is an overview of the available
Grunt tasks that'll be useful in development.
* `grunt build` – Builds *typeahead.js* from source.
* `grunt lint` – Runs source and test files through JSHint.
* `grunt watch` – Rebuilds *typeahead.js* whenever a source file is modified.
* `grunt server` – Serves files from the root of typeahead.js on localhost:8888.
Useful for using *test/playground.html* for debugging/testing.
* `grunt dev` – Runs `grunt watch` and `grunt server` in parallel.
<!-- section links -->
[contributing guidelines]: https://github.com/twitter/typeahead.js/blob/master/CONTRIBUTING.md
[entry-level]: https://github.com/twitter/typeahead.js/issues?&labels=entry-level&state=open
[grunt-cli]: https://github.com/gruntjs/grunt-cli
Maintainers
-----------
* **Jake Harding**
* [@JakeHarding](https://twitter.com/JakeHarding)
* [GitHub](https://github.com/jharding)
* **You?**
Authors
-------
* **Jake Harding**
* [@JakeHarding](https://twitter.com/JakeHarding)
* [GitHub](https://github.com/jharding)
* **Veljko Skarich**
* [@vskarich](https://twitter.com/vskarich)
* [GitHub](https://github.com/vskarich)
* **Tim Trueman**
* [@timtrueman](https://twitter.com/timtrueman)
* [GitHub](https://github.com/timtrueman)
License
-------
Copyright 2013 Twitter, Inc.
Licensed under the MIT License
================================================
FILE: bower.json
================================================
{
"name": "typeahead.js",
"version": "0.11.1",
"main": "dist/typeahead.bundle.js",
"dependencies": {
"jquery": ">=1.7"
},
"devDependencies": {
"jquery": "~1.7",
"jasmine-ajax": "~1.3.1",
"jasmine-jquery": "~1.5.2"
}
}
================================================
FILE: composer.json
================================================
{
"name": "twitter/typeahead.js",
"description": "fast and fully-featured autocomplete library",
"keywords": ["typeahead", "autocomplete"],
"homepage": "http://twitter.github.com/typeahead.js",
"authors": [
{
"name": "Twitter Inc.",
"homepage": "https://twitter.com/twitteross"
}
],
"support": {
"issues": "https://github.com/twitter/typeahead.js/issues"
},
"author": "Twitter Inc.",
"license": "MIT"
}
================================================
FILE: dist/bloodhound.js
================================================
/*!
* typeahead.js 0.11.1
* https://github.com/twitter/typeahead.js
* Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT
*/
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define("bloodhound", [ "jquery" ], function(a0) {
return root["Bloodhound"] = factory(a0);
});
} else if (typeof exports === "object") {
module.exports = factory(require("jquery"));
} else {
root["Bloodhound"] = factory(jQuery);
}
})(this, function($) {
var _ = function() {
"use strict";
return {
isMsie: function() {
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
},
isBlankString: function(str) {
return !str || /^\s*$/.test(str);
},
escapeRegExChars: function(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
isString: function(obj) {
return typeof obj === "string";
},
isNumber: function(obj) {
return typeof obj === "number";
},
isArray: $.isArray,
isFunction: $.isFunction,
isObject: $.isPlainObject,
isUndefined: function(obj) {
return typeof obj === "undefined";
},
isElement: function(obj) {
return !!(obj && obj.nodeType === 1);
},
isJQuery: function(obj) {
return obj instanceof $;
},
toStr: function toStr(s) {
return _.isUndefined(s) || s === null ? "" : s + "";
},
bind: $.proxy,
each: function(collection, cb) {
$.each(collection, reverseArgs);
function reverseArgs(index, value) {
return cb(value, index);
}
},
map: $.map,
filter: $.grep,
every: function(obj, test) {
var result = true;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (!(result = test.call(null, val, key, obj))) {
return false;
}
});
return !!result;
},
some: function(obj, test) {
var result = false;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (result = test.call(null, val, key, obj)) {
return false;
}
});
return !!result;
},
mixin: $.extend,
identity: function(x) {
return x;
},
clone: function(obj) {
return $.extend(true, {}, obj);
},
getIdGenerator: function() {
var counter = 0;
return function() {
return counter++;
};
},
templatify: function templatify(obj) {
return $.isFunction(obj) ? obj : template;
function template() {
return String(obj);
}
},
defer: function(fn) {
setTimeout(fn, 0);
},
debounce: function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments, later, callNow;
later = function() {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
}
};
callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
}
return result;
};
},
throttle: function(func, wait) {
var context, args, timeout, result, previous, later;
previous = 0;
later = function() {
previous = new Date();
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date(), remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
},
stringify: function(val) {
return _.isString(val) ? val : JSON.stringify(val);
},
noop: function() {}
};
}();
var VERSION = "0.11.1";
var tokenizers = function() {
"use strict";
return {
nonword: nonword,
whitespace: whitespace,
obj: {
nonword: getObjTokenizer(nonword),
whitespace: getObjTokenizer(whitespace)
}
};
function whitespace(str) {
str = _.toStr(str);
return str ? str.split(/\s+/) : [];
}
function nonword(str) {
str = _.toStr(str);
return str ? str.split(/\W+/) : [];
}
function getObjTokenizer(tokenizer) {
return function setKey(keys) {
keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);
return function tokenize(o) {
var tokens = [];
_.each(keys, function(k) {
tokens = tokens.concat(tokenizer(_.toStr(o[k])));
});
return tokens;
};
};
}
}();
var LruCache = function() {
"use strict";
function LruCache(maxSize) {
this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
this.reset();
if (this.maxSize <= 0) {
this.set = this.get = $.noop;
}
}
_.mixin(LruCache.prototype, {
set: function set(key, val) {
var tailItem = this.list.tail, node;
if (this.size >= this.maxSize) {
this.list.remove(tailItem);
delete this.hash[tailItem.key];
this.size--;
}
if (node = this.hash[key]) {
node.val = val;
this.list.moveToFront(node);
} else {
node = new Node(key, val);
this.list.add(node);
this.hash[key] = node;
this.size++;
}
},
get: function get(key) {
var node = this.hash[key];
if (node) {
this.list.moveToFront(node);
return node.val;
}
},
reset: function reset() {
this.size = 0;
this.hash = {};
this.list = new List();
}
});
function List() {
this.head = this.tail = null;
}
_.mixin(List.prototype, {
add: function add(node) {
if (this.head) {
node.next = this.head;
this.head.prev = node;
}
this.head = node;
this.tail = this.tail || node;
},
remove: function remove(node) {
node.prev ? node.prev.next = node.next : this.head = node.next;
node.next ? node.next.prev = node.prev : this.tail = node.prev;
},
moveToFront: function(node) {
this.remove(node);
this.add(node);
}
});
function Node(key, val) {
this.key = key;
this.val = val;
this.prev = this.next = null;
}
return LruCache;
}();
var PersistentStorage = function() {
"use strict";
var LOCAL_STORAGE;
try {
LOCAL_STORAGE = window.localStorage;
LOCAL_STORAGE.setItem("~~~", "!");
LOCAL_STORAGE.removeItem("~~~");
} catch (err) {
LOCAL_STORAGE = null;
}
function PersistentStorage(namespace, override) {
this.prefix = [ "__", namespace, "__" ].join("");
this.ttlKey = "__ttl__";
this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
this.ls = override || LOCAL_STORAGE;
!this.ls && this._noop();
}
_.mixin(PersistentStorage.prototype, {
_prefix: function(key) {
return this.prefix + key;
},
_ttlKey: function(key) {
return this._prefix(key) + this.ttlKey;
},
_noop: function() {
this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
},
_safeSet: function(key, val) {
try {
this.ls.setItem(key, val);
} catch (err) {
if (err.name === "QuotaExceededError") {
this.clear();
this._noop();
}
}
},
get: function(key) {
if (this.isExpired(key)) {
this.remove(key);
}
return decode(this.ls.getItem(this._prefix(key)));
},
set: function(key, val, ttl) {
if (_.isNumber(ttl)) {
this._safeSet(this._ttlKey(key), encode(now() + ttl));
} else {
this.ls.removeItem(this._ttlKey(key));
}
return this._safeSet(this._prefix(key), encode(val));
},
remove: function(key) {
this.ls.removeItem(this._ttlKey(key));
this.ls.removeItem(this._prefix(key));
return this;
},
clear: function() {
var i, keys = gatherMatchingKeys(this.keyMatcher);
for (i = keys.length; i--; ) {
this.remove(keys[i]);
}
return this;
},
isExpired: function(key) {
var ttl = decode(this.ls.getItem(this._ttlKey(key)));
return _.isNumber(ttl) && now() > ttl ? true : false;
}
});
return PersistentStorage;
function now() {
return new Date().getTime();
}
function encode(val) {
return JSON.stringify(_.isUndefined(val) ? null : val);
}
function decode(val) {
return $.parseJSON(val);
}
function gatherMatchingKeys(keyMatcher) {
var i, key, keys = [], len = LOCAL_STORAGE.length;
for (i = 0; i < len; i++) {
if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
keys.push(key.replace(keyMatcher, ""));
}
}
return keys;
}
}();
var Transport = function() {
"use strict";
var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
function Transport(o) {
o = o || {};
this.cancelled = false;
this.lastReq = null;
this._send = o.transport;
this._get = o.limiter ? o.limiter(this._get) : this._get;
this._cache = o.cache === false ? new LruCache(0) : sharedCache;
}
Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
maxPendingRequests = num;
};
Transport.resetCache = function resetCache() {
sharedCache.reset();
};
_.mixin(Transport.prototype, {
_fingerprint: function fingerprint(o) {
o = o || {};
return o.url + o.type + $.param(o.data || {});
},
_get: function(o, cb) {
var that = this, fingerprint, jqXhr;
fingerprint = this._fingerprint(o);
if (this.cancelled || fingerprint !== this.lastReq) {
return;
}
if (jqXhr = pendingRequests[fingerprint]) {
jqXhr.done(done).fail(fail);
} else if (pendingRequestsCount < maxPendingRequests) {
pendingRequestsCount++;
pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
} else {
this.onDeckRequestArgs = [].slice.call(arguments, 0);
}
function done(resp) {
cb(null, resp);
that._cache.set(fingerprint, resp);
}
function fail() {
cb(true);
}
function always() {
pendingRequestsCount--;
delete pendingRequests[fingerprint];
if (that.onDeckRequestArgs) {
that._get.apply(that, that.onDeckRequestArgs);
that.onDeckRequestArgs = null;
}
}
},
get: function(o, cb) {
var resp, fingerprint;
cb = cb || $.noop;
o = _.isString(o) ? {
url: o
} : o || {};
fingerprint = this._fingerprint(o);
this.cancelled = false;
this.lastReq = fingerprint;
if (resp = this._cache.get(fingerprint)) {
cb(null, resp);
} else {
this._get(o, cb);
}
},
cancel: function() {
this.cancelled = true;
}
});
return Transport;
}();
var SearchIndex = window.SearchIndex = function() {
"use strict";
var CHILDREN = "c", IDS = "i";
function SearchIndex(o) {
o = o || {};
if (!o.datumTokenizer || !o.queryTokenizer) {
$.error("datumTokenizer and queryTokenizer are both required");
}
this.identify = o.identify || _.stringify;
this.datumTokenizer = o.datumTokenizer;
this.queryTokenizer = o.queryTokenizer;
this.reset();
}
_.mixin(SearchIndex.prototype, {
bootstrap: function bootstrap(o) {
this.datums = o.datums;
this.trie = o.trie;
},
add: function(data) {
var that = this;
data = _.isArray(data) ? data : [ data ];
_.each(data, function(datum) {
var id, tokens;
that.datums[id = that.identify(datum)] = datum;
tokens = normalizeTokens(that.datumTokenizer(datum));
_.each(tokens, function(token) {
var node, chars, ch;
node = that.trie;
chars = token.split("");
while (ch = chars.shift()) {
node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
node[IDS].push(id);
}
});
});
},
get: function get(ids) {
var that = this;
return _.map(ids, function(id) {
return that.datums[id];
});
},
search: function search(query) {
var that = this, tokens, matches;
tokens = normalizeTokens(this.queryTokenizer(query));
_.each(tokens, function(token) {
var node, chars, ch, ids;
if (matches && matches.length === 0) {
return false;
}
node = that.trie;
chars = token.split("");
while (node && (ch = chars.shift())) {
node = node[CHILDREN][ch];
}
if (node && chars.length === 0) {
ids = node[IDS].slice(0);
matches = matches ? getIntersection(matches, ids) : ids;
} else {
matches = [];
return false;
}
});
return matches ? _.map(unique(matches), function(id) {
return that.datums[id];
}) : [];
},
all: function all() {
var values = [];
for (var key in this.datums) {
values.push(this.datums[key]);
}
return values;
},
reset: function reset() {
this.datums = {};
this.trie = newNode();
},
serialize: function serialize() {
return {
datums: this.datums,
trie: this.trie
};
}
});
return SearchIndex;
function normalizeTokens(tokens) {
tokens = _.filter(tokens, function(token) {
return !!token;
});
tokens = _.map(tokens, function(token) {
return token.toLowerCase();
});
return tokens;
}
function newNode() {
var node = {};
node[IDS] = [];
node[CHILDREN] = {};
return node;
}
function unique(array) {
var seen = {}, uniques = [];
for (var i = 0, len = array.length; i < len; i++) {
if (!seen[array[i]]) {
seen[array[i]] = true;
uniques.push(array[i]);
}
}
return uniques;
}
function getIntersection(arrayA, arrayB) {
var ai = 0, bi = 0, intersection = [];
arrayA = arrayA.sort();
arrayB = arrayB.sort();
var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
while (ai < lenArrayA && bi < lenArrayB) {
if (arrayA[ai] < arrayB[bi]) {
ai++;
} else if (arrayA[ai] > arrayB[bi]) {
bi++;
} else {
intersection.push(arrayA[ai]);
ai++;
bi++;
}
}
return intersection;
}
}();
var Prefetch = function() {
"use strict";
var keys;
keys = {
data: "data",
protocol: "protocol",
thumbprint: "thumbprint"
};
function Prefetch(o) {
this.url = o.url;
this.ttl = o.ttl;
this.cache = o.cache;
this.prepare = o.prepare;
this.transform = o.transform;
this.transport = o.transport;
this.thumbprint = o.thumbprint;
this.storage = new PersistentStorage(o.cacheKey);
}
_.mixin(Prefetch.prototype, {
_settings: function settings() {
return {
url: this.url,
type: "GET",
dataType: "json"
};
},
store: function store(data) {
if (!this.cache) {
return;
}
this.storage.set(keys.data, data, this.ttl);
this.storage.set(keys.protocol, location.protocol, this.ttl);
this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
},
fromCache: function fromCache() {
var stored = {}, isExpired;
if (!this.cache) {
return null;
}
stored.data = this.storage.get(keys.data);
stored.protocol = this.storage.get(keys.protocol);
stored.thumbprint = this.storage.get(keys.thumbprint);
isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
return stored.data && !isExpired ? stored.data : null;
},
fromNetwork: function(cb) {
var that = this, settings;
if (!cb) {
return;
}
settings = this.prepare(this._settings());
this.transport(settings).fail(onError).done(onResponse);
function onError() {
cb(true);
}
function onResponse(resp) {
cb(null, that.transform(resp));
}
},
clear: function clear() {
this.storage.clear();
return this;
}
});
return Prefetch;
}();
var Remote = function() {
"use strict";
function Remote(o) {
this.url = o.url;
this.prepare = o.prepare;
this.transform = o.transform;
this.transport = new Transport({
cache: o.cache,
limiter: o.limiter,
transport: o.transport
});
}
_.mixin(Remote.prototype, {
_settings: function settings() {
return {
url: this.url,
type: "GET",
dataType: "json"
};
},
get: function get(query, cb) {
var that = this, settings;
if (!cb) {
return;
}
query = query || "";
settings = this.prepare(query, this._settings());
return this.transport.get(settings, onResponse);
function onResponse(err, resp) {
err ? cb([]) : cb(that.transform(resp));
}
},
cancelLastRequest: function cancelLastRequest() {
this.transport.cancel();
}
});
return Remote;
}();
var oParser = function() {
"use strict";
return function parse(o) {
var defaults, sorter;
defaults = {
initialize: true,
identify: _.stringify,
datumTokenizer: null,
queryTokenizer: null,
sufficient: 5,
sorter: null,
local: [],
prefetch: null,
remote: null
};
o = _.mixin(defaults, o || {});
!o.datumTokenizer && $.error("datumTokenizer is required");
!o.queryTokenizer && $.error("queryTokenizer is required");
sorter = o.sorter;
o.sorter = sorter ? function(x) {
return x.sort(sorter);
} : _.identity;
o.local = _.isFunction(o.local) ? o.local() : o.local;
o.prefetch = parsePrefetch(o.prefetch);
o.remote = parseRemote(o.remote);
return o;
};
function parsePrefetch(o) {
var defaults;
if (!o) {
return null;
}
defaults = {
url: null,
ttl: 24 * 60 * 60 * 1e3,
cache: true,
cacheKey: null,
thumbprint: "",
prepare: _.identity,
transform: _.identity,
transport: null
};
o = _.isString(o) ? {
url: o
} : o;
o = _.mixin(defaults, o);
!o.url && $.error("prefetch requires url to be set");
o.transform = o.filter || o.transform;
o.cacheKey = o.cacheKey || o.url;
o.thumbprint = VERSION + o.thumbprint;
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
return o;
}
function parseRemote(o) {
var defaults;
if (!o) {
return;
}
defaults = {
url: null,
cache: true,
prepare: null,
replace: null,
wildcard: null,
limiter: null,
rateLimitBy: "debounce",
rateLimitWait: 300,
transform: _.identity,
transport: null
};
o = _.isString(o) ? {
url: o
} : o;
o = _.mixin(defaults, o);
!o.url && $.error("remote requires url to be set");
o.transform = o.filter || o.transform;
o.prepare = toRemotePrepare(o);
o.limiter = toLimiter(o);
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
delete o.replace;
delete o.wildcard;
delete o.rateLimitBy;
delete o.rateLimitWait;
return o;
}
function toRemotePrepare(o) {
var prepare, replace, wildcard;
prepare = o.prepare;
replace = o.replace;
wildcard = o.wildcard;
if (prepare) {
return prepare;
}
if (replace) {
prepare = prepareByReplace;
} else if (o.wildcard) {
prepare = prepareByWildcard;
} else {
prepare = idenityPrepare;
}
return prepare;
function prepareByReplace(query, settings) {
settings.url = replace(settings.url, query);
return settings;
}
function prepareByWildcard(query, settings) {
settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
return settings;
}
function idenityPrepare(query, settings) {
return settings;
}
}
function toLimiter(o) {
var limiter, method, wait;
limiter = o.limiter;
method = o.rateLimitBy;
wait = o.rateLimitWait;
if (!limiter) {
limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
}
return limiter;
function debounce(wait) {
return function debounce(fn) {
return _.debounce(fn, wait);
};
}
function throttle(wait) {
return function throttle(fn) {
return _.throttle(fn, wait);
};
}
}
function callbackToDeferred(fn) {
return function wrapper(o) {
var deferred = $.Deferred();
fn(o, onSuccess, onError);
return deferred;
function onSuccess(resp) {
_.defer(function() {
deferred.resolve(resp);
});
}
function onError(err) {
_.defer(function() {
deferred.reject(err);
});
}
};
}
}();
var Bloodhound = function() {
"use strict";
var old;
old = window && window.Bloodhound;
function Bloodhound(o) {
o = oParser(o);
this.sorter = o.sorter;
this.identify = o.identify;
this.sufficient = o.sufficient;
this.local = o.local;
this.remote = o.remote ? new Remote(o.remote) : null;
this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
this.index = new SearchIndex({
identify: this.identify,
datumTokenizer: o.datumTokenizer,
queryTokenizer: o.queryTokenizer
});
o.initialize !== false && this.initialize();
}
Bloodhound.noConflict = function noConflict() {
window && (window.Bloodhound = old);
return Bloodhound;
};
Bloodhound.tokenizers = tokenizers;
_.mixin(Bloodhound.prototype, {
__ttAdapter: function ttAdapter() {
var that = this;
return this.remote ? withAsync : withoutAsync;
function withAsync(query, sync, async) {
return that.search(query, sync, async);
}
function withoutAsync(query, sync) {
return that.search(query, sync);
}
},
_loadPrefetch: function loadPrefetch() {
var that = this, deferred, serialized;
deferred = $.Deferred();
if (!this.prefetch) {
deferred.resolve();
} else if (serialized = this.prefetch.fromCache()) {
this.index.bootstrap(serialized);
deferred.resolve();
} else {
this.prefetch.fromNetwork(done);
}
return deferred.promise();
function done(err, data) {
if (err) {
return deferred.reject();
}
that.add(data);
that.prefetch.store(that.index.serialize());
deferred.resolve();
}
},
_initialize: function initialize() {
var that = this, deferred;
this.clear();
(this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
return this.initPromise;
function addLocalToIndex() {
that.add(that.local);
}
},
initialize: function initialize(force) {
return !this.initPromise || force ? this._initialize() : this.initPromise;
},
add: function add(data) {
this.index.add(data);
return this;
},
get: function get(ids) {
ids = _.isArray(ids) ? ids : [].slice.call(arguments);
return this.index.get(ids);
},
search: function search(query, sync, async) {
var that = this, local;
local = this.sorter(this.index.search(query));
sync(this.remote ? local.slice() : local);
if (this.remote && local.length < this.sufficient) {
this.remote.get(query, processRemote);
} else if (this.remote) {
this.remote.cancelLastRequest();
}
return this;
function processRemote(remote) {
var nonDuplicates = [];
_.each(remote, function(r) {
!_.some(local, function(l) {
return that.identify(r) === that.identify(l);
}) && nonDuplicates.push(r);
});
async && async(nonDuplicates);
}
},
all: function all() {
return this.index.all();
},
clear: function clear() {
this.index.reset();
return this;
},
clearPrefetchCache: function clearPrefetchCache() {
this.prefetch && this.prefetch.clear();
return this;
},
clearRemoteCache: function clearRemoteCache() {
Transport.resetCache();
return this;
},
ttAdapter: function ttAdapter() {
return this.__ttAdapter();
}
});
return Bloodhound;
}();
return Bloodhound;
});
================================================
FILE: dist/typeahead.bundle.js
================================================
/*!
* typeahead.js 0.11.1
* https://github.com/twitter/typeahead.js
* Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT
*/
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define("bloodhound", [ "jquery" ], function(a0) {
return root["Bloodhound"] = factory(a0);
});
} else if (typeof exports === "object") {
module.exports = factory(require("jquery"));
} else {
root["Bloodhound"] = factory(jQuery);
}
})(this, function($) {
var _ = function() {
"use strict";
return {
isMsie: function() {
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
},
isBlankString: function(str) {
return !str || /^\s*$/.test(str);
},
escapeRegExChars: function(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
isString: function(obj) {
return typeof obj === "string";
},
isNumber: function(obj) {
return typeof obj === "number";
},
isArray: $.isArray,
isFunction: $.isFunction,
isObject: $.isPlainObject,
isUndefined: function(obj) {
return typeof obj === "undefined";
},
isElement: function(obj) {
return !!(obj && obj.nodeType === 1);
},
isJQuery: function(obj) {
return obj instanceof $;
},
toStr: function toStr(s) {
return _.isUndefined(s) || s === null ? "" : s + "";
},
bind: $.proxy,
each: function(collection, cb) {
$.each(collection, reverseArgs);
function reverseArgs(index, value) {
return cb(value, index);
}
},
map: $.map,
filter: $.grep,
every: function(obj, test) {
var result = true;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (!(result = test.call(null, val, key, obj))) {
return false;
}
});
return !!result;
},
some: function(obj, test) {
var result = false;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (result = test.call(null, val, key, obj)) {
return false;
}
});
return !!result;
},
mixin: $.extend,
identity: function(x) {
return x;
},
clone: function(obj) {
return $.extend(true, {}, obj);
},
getIdGenerator: function() {
var counter = 0;
return function() {
return counter++;
};
},
templatify: function templatify(obj) {
return $.isFunction(obj) ? obj : template;
function template() {
return String(obj);
}
},
defer: function(fn) {
setTimeout(fn, 0);
},
debounce: function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments, later, callNow;
later = function() {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
}
};
callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
}
return result;
};
},
throttle: function(func, wait) {
var context, args, timeout, result, previous, later;
previous = 0;
later = function() {
previous = new Date();
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date(), remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
},
stringify: function(val) {
return _.isString(val) ? val : JSON.stringify(val);
},
noop: function() {}
};
}();
var VERSION = "0.11.1";
var tokenizers = function() {
"use strict";
return {
nonword: nonword,
whitespace: whitespace,
obj: {
nonword: getObjTokenizer(nonword),
whitespace: getObjTokenizer(whitespace)
}
};
function whitespace(str) {
str = _.toStr(str);
return str ? str.split(/\s+/) : [];
}
function nonword(str) {
str = _.toStr(str);
return str ? str.split(/\W+/) : [];
}
function getObjTokenizer(tokenizer) {
return function setKey(keys) {
keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);
return function tokenize(o) {
var tokens = [];
_.each(keys, function(k) {
tokens = tokens.concat(tokenizer(_.toStr(o[k])));
});
return tokens;
};
};
}
}();
var LruCache = function() {
"use strict";
function LruCache(maxSize) {
this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
this.reset();
if (this.maxSize <= 0) {
this.set = this.get = $.noop;
}
}
_.mixin(LruCache.prototype, {
set: function set(key, val) {
var tailItem = this.list.tail, node;
if (this.size >= this.maxSize) {
this.list.remove(tailItem);
delete this.hash[tailItem.key];
this.size--;
}
if (node = this.hash[key]) {
node.val = val;
this.list.moveToFront(node);
} else {
node = new Node(key, val);
this.list.add(node);
this.hash[key] = node;
this.size++;
}
},
get: function get(key) {
var node = this.hash[key];
if (node) {
this.list.moveToFront(node);
return node.val;
}
},
reset: function reset() {
this.size = 0;
this.hash = {};
this.list = new List();
}
});
function List() {
this.head = this.tail = null;
}
_.mixin(List.prototype, {
add: function add(node) {
if (this.head) {
node.next = this.head;
this.head.prev = node;
}
this.head = node;
this.tail = this.tail || node;
},
remove: function remove(node) {
node.prev ? node.prev.next = node.next : this.head = node.next;
node.next ? node.next.prev = node.prev : this.tail = node.prev;
},
moveToFront: function(node) {
this.remove(node);
this.add(node);
}
});
function Node(key, val) {
this.key = key;
this.val = val;
this.prev = this.next = null;
}
return LruCache;
}();
var PersistentStorage = function() {
"use strict";
var LOCAL_STORAGE;
try {
LOCAL_STORAGE = window.localStorage;
LOCAL_STORAGE.setItem("~~~", "!");
LOCAL_STORAGE.removeItem("~~~");
} catch (err) {
LOCAL_STORAGE = null;
}
function PersistentStorage(namespace, override) {
this.prefix = [ "__", namespace, "__" ].join("");
this.ttlKey = "__ttl__";
this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
this.ls = override || LOCAL_STORAGE;
!this.ls && this._noop();
}
_.mixin(PersistentStorage.prototype, {
_prefix: function(key) {
return this.prefix + key;
},
_ttlKey: function(key) {
return this._prefix(key) + this.ttlKey;
},
_noop: function() {
this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
},
_safeSet: function(key, val) {
try {
this.ls.setItem(key, val);
} catch (err) {
if (err.name === "QuotaExceededError") {
this.clear();
this._noop();
}
}
},
get: function(key) {
if (this.isExpired(key)) {
this.remove(key);
}
return decode(this.ls.getItem(this._prefix(key)));
},
set: function(key, val, ttl) {
if (_.isNumber(ttl)) {
this._safeSet(this._ttlKey(key), encode(now() + ttl));
} else {
this.ls.removeItem(this._ttlKey(key));
}
return this._safeSet(this._prefix(key), encode(val));
},
remove: function(key) {
this.ls.removeItem(this._ttlKey(key));
this.ls.removeItem(this._prefix(key));
return this;
},
clear: function() {
var i, keys = gatherMatchingKeys(this.keyMatcher);
for (i = keys.length; i--; ) {
this.remove(keys[i]);
}
return this;
},
isExpired: function(key) {
var ttl = decode(this.ls.getItem(this._ttlKey(key)));
return _.isNumber(ttl) && now() > ttl ? true : false;
}
});
return PersistentStorage;
function now() {
return new Date().getTime();
}
function encode(val) {
return JSON.stringify(_.isUndefined(val) ? null : val);
}
function decode(val) {
return $.parseJSON(val);
}
function gatherMatchingKeys(keyMatcher) {
var i, key, keys = [], len = LOCAL_STORAGE.length;
for (i = 0; i < len; i++) {
if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
keys.push(key.replace(keyMatcher, ""));
}
}
return keys;
}
}();
var Transport = function() {
"use strict";
var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
function Transport(o) {
o = o || {};
this.cancelled = false;
this.lastReq = null;
this._send = o.transport;
this._get = o.limiter ? o.limiter(this._get) : this._get;
this._cache = o.cache === false ? new LruCache(0) : sharedCache;
}
Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
maxPendingRequests = num;
};
Transport.resetCache = function resetCache() {
sharedCache.reset();
};
_.mixin(Transport.prototype, {
_fingerprint: function fingerprint(o) {
o = o || {};
return o.url + o.type + $.param(o.data || {});
},
_get: function(o, cb) {
var that = this, fingerprint, jqXhr;
fingerprint = this._fingerprint(o);
if (this.cancelled || fingerprint !== this.lastReq) {
return;
}
if (jqXhr = pendingRequests[fingerprint]) {
jqXhr.done(done).fail(fail);
} else if (pendingRequestsCount < maxPendingRequests) {
pendingRequestsCount++;
pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
} else {
this.onDeckRequestArgs = [].slice.call(arguments, 0);
}
function done(resp) {
cb(null, resp);
that._cache.set(fingerprint, resp);
}
function fail() {
cb(true);
}
function always() {
pendingRequestsCount--;
delete pendingRequests[fingerprint];
if (that.onDeckRequestArgs) {
that._get.apply(that, that.onDeckRequestArgs);
that.onDeckRequestArgs = null;
}
}
},
get: function(o, cb) {
var resp, fingerprint;
cb = cb || $.noop;
o = _.isString(o) ? {
url: o
} : o || {};
fingerprint = this._fingerprint(o);
this.cancelled = false;
this.lastReq = fingerprint;
if (resp = this._cache.get(fingerprint)) {
cb(null, resp);
} else {
this._get(o, cb);
}
},
cancel: function() {
this.cancelled = true;
}
});
return Transport;
}();
var SearchIndex = window.SearchIndex = function() {
"use strict";
var CHILDREN = "c", IDS = "i";
function SearchIndex(o) {
o = o || {};
if (!o.datumTokenizer || !o.queryTokenizer) {
$.error("datumTokenizer and queryTokenizer are both required");
}
this.identify = o.identify || _.stringify;
this.datumTokenizer = o.datumTokenizer;
this.queryTokenizer = o.queryTokenizer;
this.reset();
}
_.mixin(SearchIndex.prototype, {
bootstrap: function bootstrap(o) {
this.datums = o.datums;
this.trie = o.trie;
},
add: function(data) {
var that = this;
data = _.isArray(data) ? data : [ data ];
_.each(data, function(datum) {
var id, tokens;
that.datums[id = that.identify(datum)] = datum;
tokens = normalizeTokens(that.datumTokenizer(datum));
_.each(tokens, function(token) {
var node, chars, ch;
node = that.trie;
chars = token.split("");
while (ch = chars.shift()) {
node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
node[IDS].push(id);
}
});
});
},
get: function get(ids) {
var that = this;
return _.map(ids, function(id) {
return that.datums[id];
});
},
search: function search(query) {
var that = this, tokens, matches;
tokens = normalizeTokens(this.queryTokenizer(query));
_.each(tokens, function(token) {
var node, chars, ch, ids;
if (matches && matches.length === 0) {
return false;
}
node = that.trie;
chars = token.split("");
while (node && (ch = chars.shift())) {
node = node[CHILDREN][ch];
}
if (node && chars.length === 0) {
ids = node[IDS].slice(0);
matches = matches ? getIntersection(matches, ids) : ids;
} else {
matches = [];
return false;
}
});
return matches ? _.map(unique(matches), function(id) {
return that.datums[id];
}) : [];
},
all: function all() {
var values = [];
for (var key in this.datums) {
values.push(this.datums[key]);
}
return values;
},
reset: function reset() {
this.datums = {};
this.trie = newNode();
},
serialize: function serialize() {
return {
datums: this.datums,
trie: this.trie
};
}
});
return SearchIndex;
function normalizeTokens(tokens) {
tokens = _.filter(tokens, function(token) {
return !!token;
});
tokens = _.map(tokens, function(token) {
return token.toLowerCase();
});
return tokens;
}
function newNode() {
var node = {};
node[IDS] = [];
node[CHILDREN] = {};
return node;
}
function unique(array) {
var seen = {}, uniques = [];
for (var i = 0, len = array.length; i < len; i++) {
if (!seen[array[i]]) {
seen[array[i]] = true;
uniques.push(array[i]);
}
}
return uniques;
}
function getIntersection(arrayA, arrayB) {
var ai = 0, bi = 0, intersection = [];
arrayA = arrayA.sort();
arrayB = arrayB.sort();
var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
while (ai < lenArrayA && bi < lenArrayB) {
if (arrayA[ai] < arrayB[bi]) {
ai++;
} else if (arrayA[ai] > arrayB[bi]) {
bi++;
} else {
intersection.push(arrayA[ai]);
ai++;
bi++;
}
}
return intersection;
}
}();
var Prefetch = function() {
"use strict";
var keys;
keys = {
data: "data",
protocol: "protocol",
thumbprint: "thumbprint"
};
function Prefetch(o) {
this.url = o.url;
this.ttl = o.ttl;
this.cache = o.cache;
this.prepare = o.prepare;
this.transform = o.transform;
this.transport = o.transport;
this.thumbprint = o.thumbprint;
this.storage = new PersistentStorage(o.cacheKey);
}
_.mixin(Prefetch.prototype, {
_settings: function settings() {
return {
url: this.url,
type: "GET",
dataType: "json"
};
},
store: function store(data) {
if (!this.cache) {
return;
}
this.storage.set(keys.data, data, this.ttl);
this.storage.set(keys.protocol, location.protocol, this.ttl);
this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
},
fromCache: function fromCache() {
var stored = {}, isExpired;
if (!this.cache) {
return null;
}
stored.data = this.storage.get(keys.data);
stored.protocol = this.storage.get(keys.protocol);
stored.thumbprint = this.storage.get(keys.thumbprint);
isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
return stored.data && !isExpired ? stored.data : null;
},
fromNetwork: function(cb) {
var that = this, settings;
if (!cb) {
return;
}
settings = this.prepare(this._settings());
this.transport(settings).fail(onError).done(onResponse);
function onError() {
cb(true);
}
function onResponse(resp) {
cb(null, that.transform(resp));
}
},
clear: function clear() {
this.storage.clear();
return this;
}
});
return Prefetch;
}();
var Remote = function() {
"use strict";
function Remote(o) {
this.url = o.url;
this.prepare = o.prepare;
this.transform = o.transform;
this.transport = new Transport({
cache: o.cache,
limiter: o.limiter,
transport: o.transport
});
}
_.mixin(Remote.prototype, {
_settings: function settings() {
return {
url: this.url,
type: "GET",
dataType: "json"
};
},
get: function get(query, cb) {
var that = this, settings;
if (!cb) {
return;
}
query = query || "";
settings = this.prepare(query, this._settings());
return this.transport.get(settings, onResponse);
function onResponse(err, resp) {
err ? cb([]) : cb(that.transform(resp));
}
},
cancelLastRequest: function cancelLastRequest() {
this.transport.cancel();
}
});
return Remote;
}();
var oParser = function() {
"use strict";
return function parse(o) {
var defaults, sorter;
defaults = {
initialize: true,
identify: _.stringify,
datumTokenizer: null,
queryTokenizer: null,
sufficient: 5,
sorter: null,
local: [],
prefetch: null,
remote: null
};
o = _.mixin(defaults, o || {});
!o.datumTokenizer && $.error("datumTokenizer is required");
!o.queryTokenizer && $.error("queryTokenizer is required");
sorter = o.sorter;
o.sorter = sorter ? function(x) {
return x.sort(sorter);
} : _.identity;
o.local = _.isFunction(o.local) ? o.local() : o.local;
o.prefetch = parsePrefetch(o.prefetch);
o.remote = parseRemote(o.remote);
return o;
};
function parsePrefetch(o) {
var defaults;
if (!o) {
return null;
}
defaults = {
url: null,
ttl: 24 * 60 * 60 * 1e3,
cache: true,
cacheKey: null,
thumbprint: "",
prepare: _.identity,
transform: _.identity,
transport: null
};
o = _.isString(o) ? {
url: o
} : o;
o = _.mixin(defaults, o);
!o.url && $.error("prefetch requires url to be set");
o.transform = o.filter || o.transform;
o.cacheKey = o.cacheKey || o.url;
o.thumbprint = VERSION + o.thumbprint;
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
return o;
}
function parseRemote(o) {
var defaults;
if (!o) {
return;
}
defaults = {
url: null,
cache: true,
prepare: null,
replace: null,
wildcard: null,
limiter: null,
rateLimitBy: "debounce",
rateLimitWait: 300,
transform: _.identity,
transport: null
};
o = _.isString(o) ? {
url: o
} : o;
o = _.mixin(defaults, o);
!o.url && $.error("remote requires url to be set");
o.transform = o.filter || o.transform;
o.prepare = toRemotePrepare(o);
o.limiter = toLimiter(o);
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
delete o.replace;
delete o.wildcard;
delete o.rateLimitBy;
delete o.rateLimitWait;
return o;
}
function toRemotePrepare(o) {
var prepare, replace, wildcard;
prepare = o.prepare;
replace = o.replace;
wildcard = o.wildcard;
if (prepare) {
return prepare;
}
if (replace) {
prepare = prepareByReplace;
} else if (o.wildcard) {
prepare = prepareByWildcard;
} else {
prepare = idenityPrepare;
}
return prepare;
function prepareByReplace(query, settings) {
settings.url = replace(settings.url, query);
return settings;
}
function prepareByWildcard(query, settings) {
settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
return settings;
}
function idenityPrepare(query, settings) {
return settings;
}
}
function toLimiter(o) {
var limiter, method, wait;
limiter = o.limiter;
method = o.rateLimitBy;
wait = o.rateLimitWait;
if (!limiter) {
limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
}
return limiter;
function debounce(wait) {
return function debounce(fn) {
return _.debounce(fn, wait);
};
}
function throttle(wait) {
return function throttle(fn) {
return _.throttle(fn, wait);
};
}
}
function callbackToDeferred(fn) {
return function wrapper(o) {
var deferred = $.Deferred();
fn(o, onSuccess, onError);
return deferred;
function onSuccess(resp) {
_.defer(function() {
deferred.resolve(resp);
});
}
function onError(err) {
_.defer(function() {
deferred.reject(err);
});
}
};
}
}();
var Bloodhound = function() {
"use strict";
var old;
old = window && window.Bloodhound;
function Bloodhound(o) {
o = oParser(o);
this.sorter = o.sorter;
this.identify = o.identify;
this.sufficient = o.sufficient;
this.local = o.local;
this.remote = o.remote ? new Remote(o.remote) : null;
this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
this.index = new SearchIndex({
identify: this.identify,
datumTokenizer: o.datumTokenizer,
queryTokenizer: o.queryTokenizer
});
o.initialize !== false && this.initialize();
}
Bloodhound.noConflict = function noConflict() {
window && (window.Bloodhound = old);
return Bloodhound;
};
Bloodhound.tokenizers = tokenizers;
_.mixin(Bloodhound.prototype, {
__ttAdapter: function ttAdapter() {
var that = this;
return this.remote ? withAsync : withoutAsync;
function withAsync(query, sync, async) {
return that.search(query, sync, async);
}
function withoutAsync(query, sync) {
return that.search(query, sync);
}
},
_loadPrefetch: function loadPrefetch() {
var that = this, deferred, serialized;
deferred = $.Deferred();
if (!this.prefetch) {
deferred.resolve();
} else if (serialized = this.prefetch.fromCache()) {
this.index.bootstrap(serialized);
deferred.resolve();
} else {
this.prefetch.fromNetwork(done);
}
return deferred.promise();
function done(err, data) {
if (err) {
return deferred.reject();
}
that.add(data);
that.prefetch.store(that.index.serialize());
deferred.resolve();
}
},
_initialize: function initialize() {
var that = this, deferred;
this.clear();
(this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
return this.initPromise;
function addLocalToIndex() {
that.add(that.local);
}
},
initialize: function initialize(force) {
return !this.initPromise || force ? this._initialize() : this.initPromise;
},
add: function add(data) {
this.index.add(data);
return this;
},
get: function get(ids) {
ids = _.isArray(ids) ? ids : [].slice.call(arguments);
return this.index.get(ids);
},
search: function search(query, sync, async) {
var that = this, local;
local = this.sorter(this.index.search(query));
sync(this.remote ? local.slice() : local);
if (this.remote && local.length < this.sufficient) {
this.remote.get(query, processRemote);
} else if (this.remote) {
this.remote.cancelLastRequest();
}
return this;
function processRemote(remote) {
var nonDuplicates = [];
_.each(remote, function(r) {
!_.some(local, function(l) {
return that.identify(r) === that.identify(l);
}) && nonDuplicates.push(r);
});
async && async(nonDuplicates);
}
},
all: function all() {
return this.index.all();
},
clear: function clear() {
this.index.reset();
return this;
},
clearPrefetchCache: function clearPrefetchCache() {
this.prefetch && this.prefetch.clear();
return this;
},
clearRemoteCache: function clearRemoteCache() {
Transport.resetCache();
return this;
},
ttAdapter: function ttAdapter() {
return this.__ttAdapter();
}
});
return Bloodhound;
}();
return Bloodhound;
});
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define("typeahead.js", [ "jquery" ], function(a0) {
return factory(a0);
});
} else if (typeof exports === "object") {
module.exports = factory(require("jquery"));
} else {
factory(jQuery);
}
})(this, function($) {
var _ = function() {
"use strict";
return {
isMsie: function() {
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
},
isBlankString: function(str) {
return !str || /^\s*$/.test(str);
},
escapeRegExChars: function(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
isString: function(obj) {
return typeof obj === "string";
},
isNumber: function(obj) {
return typeof obj === "number";
},
isArray: $.isArray,
isFunction: $.isFunction,
isObject: $.isPlainObject,
isUndefined: function(obj) {
return typeof obj === "undefined";
},
isElement: function(obj) {
return !!(obj && obj.nodeType === 1);
},
isJQuery: function(obj) {
return obj instanceof $;
},
toStr: function toStr(s) {
return _.isUndefined(s) || s === null ? "" : s + "";
},
bind: $.proxy,
each: function(collection, cb) {
$.each(collection, reverseArgs);
function reverseArgs(index, value) {
return cb(value, index);
}
},
map: $.map,
filter: $.grep,
every: function(obj, test) {
var result = true;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (!(result = test.call(null, val, key, obj))) {
return false;
}
});
return !!result;
},
some: function(obj, test) {
var result = false;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (result = test.call(null, val, key, obj)) {
return false;
}
});
return !!result;
},
mixin: $.extend,
identity: function(x) {
return x;
},
clone: function(obj) {
return $.extend(true, {}, obj);
},
getIdGenerator: function() {
var counter = 0;
return function() {
return counter++;
};
},
templatify: function templatify(obj) {
return $.isFunction(obj) ? obj : template;
function template() {
return String(obj);
}
},
defer: function(fn) {
setTimeout(fn, 0);
},
debounce: function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments, later, callNow;
later = function() {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
}
};
callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
}
return result;
};
},
throttle: function(func, wait) {
var context, args, timeout, result, previous, later;
previous = 0;
later = function() {
previous = new Date();
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date(), remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
},
stringify: function(val) {
return _.isString(val) ? val : JSON.stringify(val);
},
noop: function() {}
};
}();
var WWW = function() {
"use strict";
var defaultClassNames = {
wrapper: "twitter-typeahead",
input: "tt-input",
hint: "tt-hint",
menu: "tt-menu",
dataset: "tt-dataset",
suggestion: "tt-suggestion",
selectable: "tt-selectable",
empty: "tt-empty",
open: "tt-open",
cursor: "tt-cursor",
highlight: "tt-highlight"
};
return build;
function build(o) {
var www, classes;
classes = _.mixin({}, defaultClassNames, o);
www = {
css: buildCss(),
classes: classes,
html: buildHtml(classes),
selectors: buildSelectors(classes)
};
return {
css: www.css,
html: www.html,
classes: www.classes,
selectors: www.selectors,
mixin: function(o) {
_.mixin(o, www);
}
};
}
function buildHtml(c) {
return {
wrapper: '<span class="' + c.wrapper + '"></span>',
menu: '<div class="' + c.menu + '"></div>'
};
}
function buildSelectors(classes) {
var selectors = {};
_.each(classes, function(v, k) {
selectors[k] = "." + v;
});
return selectors;
}
function buildCss() {
var css = {
wrapper: {
position: "relative",
display: "inline-block"
},
hint: {
position: "absolute",
top: "0",
left: "0",
borderColor: "transparent",
boxShadow: "none",
opacity: "1"
},
input: {
position: "relative",
verticalAlign: "top",
backgroundColor: "transparent"
},
inputWithNoHint: {
position: "relative",
verticalAlign: "top"
},
menu: {
position: "absolute",
top: "100%",
left: "0",
zIndex: "100",
display: "none"
},
ltr: {
left: "0",
right: "auto"
},
rtl: {
left: "auto",
right: " 0"
}
};
if (_.isMsie()) {
_.mixin(css.input, {
backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
});
}
return css;
}
}();
var EventBus = function() {
"use strict";
var namespace, deprecationMap;
namespace = "typeahead:";
deprecationMap = {
render: "rendered",
cursorchange: "cursorchanged",
select: "selected",
autocomplete: "autocompleted"
};
function EventBus(o) {
if (!o || !o.el) {
$.error("EventBus initialized without el");
}
this.$el = $(o.el);
}
_.mixin(EventBus.prototype, {
_trigger: function(type, args) {
var $e;
$e = $.Event(namespace + type);
(args = args || []).unshift($e);
this.$el.trigger.apply(this.$el, args);
return $e;
},
before: function(type) {
var args, $e;
args = [].slice.call(arguments, 1);
$e = this._trigger("before" + type, args);
return $e.isDefaultPrevented();
},
trigger: function(type) {
var deprecatedType;
this._trigger(type, [].slice.call(arguments, 1));
if (deprecatedType = deprecationMap[type]) {
this._trigger(deprecatedType, [].slice.call(arguments, 1));
}
}
});
return EventBus;
}();
var EventEmitter = function() {
"use strict";
var splitter = /\s+/, nextTick = getNextTick();
return {
onSync: onSync,
onAsync: onAsync,
off: off,
trigger: trigger
};
function on(method, types, cb, context) {
var type;
if (!cb) {
return this;
}
types = types.split(splitter);
cb = context ? bindContext(cb, context) : cb;
this._callbacks = this._callbacks || {};
while (type = types.shift()) {
this._callbacks[type] = this._callbacks[type] || {
sync: [],
async: []
};
this._callbacks[type][method].push(cb);
}
return this;
}
function onAsync(types, cb, context) {
return on.call(this, "async", types, cb, context);
}
function onSync(types, cb, context) {
return on.call(this, "sync", types, cb, context);
}
function off(types) {
var type;
if (!this._callbacks) {
return this;
}
types = types.split(splitter);
while (type = types.shift()) {
delete this._callbacks[type];
}
return this;
}
function trigger(types) {
var type, callbacks, args, syncFlush, asyncFlush;
if (!this._callbacks) {
return this;
}
types = types.split(splitter);
args = [].slice.call(arguments, 1);
while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));
asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));
syncFlush() && nextTick(asyncFlush);
}
return this;
}
function getFlush(callbacks, context, args) {
return flush;
function flush() {
var cancelled;
for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
cancelled = callbacks[i].apply(context, args) === false;
}
return !cancelled;
}
}
function getNextTick() {
var nextTickFn;
if (window.setImmediate) {
nextTickFn = function nextTickSetImmediate(fn) {
setImmediate(function() {
fn();
});
};
} else {
nextTickFn = function nextTickSetTimeout(fn) {
setTimeout(function() {
fn();
}, 0);
};
}
return nextTickFn;
}
function bindContext(fn, context) {
return fn.bind ? fn.bind(context) : function() {
fn.apply(context, [].slice.call(arguments, 0));
};
}
}();
var highlight = function(doc) {
"use strict";
var defaults = {
node: null,
pattern: null,
tagName: "strong",
className: null,
wordsOnly: false,
caseSensitive: false
};
return function hightlight(o) {
var regex;
o = _.mixin({}, defaults, o);
if (!o.node || !o.pattern) {
return;
}
o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
traverse(o.node, hightlightTextNode);
function hightlightTextNode(textNode) {
var match, patternNode, wrapperNode;
if (match = regex.exec(textNode.data)) {
wrapperNode = doc.createElement(o.tagName);
o.className && (wrapperNode.className = o.className);
patternNode = textNode.splitText(match.index);
patternNode.splitText(match[0].length);
wrapperNode.appendChild(patternNode.cloneNode(true));
textNode.parentNode.replaceChild(wrapperNode, patternNode);
}
return !!match;
}
function traverse(el, hightlightTextNode) {
var childNode, TEXT_NODE_TYPE = 3;
for (var i = 0; i < el.childNodes.length; i++) {
childNode = el.childNodes[i];
if (childNode.nodeType === TEXT_NODE_TYPE) {
i += hightlightTextNode(childNode) ? 1 : 0;
} else {
traverse(childNode, hightlightTextNode);
}
}
}
};
function getRegex(patterns, caseSensitive, wordsOnly) {
var escapedPatterns = [], regexStr;
for (var i = 0, len = patterns.length; i < len; i++) {
escapedPatterns.push(_.escapeRegExChars(patterns[i]));
}
regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
}
}(window.document);
var Input = function() {
"use strict";
var specialKeyCodeMap;
specialKeyCodeMap = {
9: "tab",
27: "esc",
37: "left",
39: "right",
13: "enter",
38: "up",
40: "down"
};
function Input(o, www) {
o = o || {};
if (!o.input) {
$.error("input is missing");
}
www.mixin(this);
this.$hint = $(o.hint);
this.$input = $(o.input);
this.query = this.$input.val();
this.queryWhenFocused = this.hasFocus() ? this.query : null;
this.$overflowHelper = buildOverflowHelper(this.$input);
this._checkLanguageDirection();
if (this.$hint.length === 0) {
this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
}
}
Input.normalizeQuery = function(str) {
return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
};
_.mixin(Input.prototype, EventEmitter, {
_onBlur: function onBlur() {
this.resetInputValue();
this.trigger("blurred");
},
_onFocus: function onFocus() {
this.queryWhenFocused = this.query;
this.trigger("focused");
},
_onKeydown: function onKeydown($e) {
var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
this._managePreventDefault(keyName, $e);
if (keyName && this._shouldTrigger(keyName, $e)) {
this.trigger(keyName + "Keyed", $e);
}
},
_onInput: function onInput() {
this._setQuery(this.getInputValue());
this.clearHintIfInvalid();
this._checkLanguageDirection();
},
_managePreventDefault: function managePreventDefault(keyName, $e) {
var preventDefault;
switch (keyName) {
case "up":
case "down":
preventDefault = !withModifier($e);
break;
default:
preventDefault = false;
}
preventDefault && $e.preventDefault();
},
_shouldTrigger: function shouldTrigger(keyName, $e) {
var trigger;
switch (keyName) {
case "tab":
trigger = !withModifier($e);
break;
default:
trigger = true;
}
return trigger;
},
_checkLanguageDirection: function checkLanguageDirection() {
var dir = (this.$input.css("direction") || "ltr").toLowerCase();
if (this.dir !== dir) {
this.dir = dir;
this.$hint.attr("dir", dir);
this.trigger("langDirChanged", dir);
}
},
_setQuery: function setQuery(val, silent) {
var areEquivalent, hasDifferentWhitespace;
areEquivalent = areQueriesEquivalent(val, this.query);
hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;
this.query = val;
if (!silent && !areEquivalent) {
this.trigger("queryChanged", this.query);
} else if (!silent && hasDifferentWhitespace) {
this.trigger("whitespaceChanged", this.query);
}
},
bind: function() {
var that = this, onBlur, onFocus, onKeydown, onInput;
onBlur = _.bind(this._onBlur, this);
onFocus = _.bind(this._onFocus, this);
onKeydown = _.bind(this._onKeydown, this);
onInput = _.bind(this._onInput, this);
this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
if (!_.isMsie() || _.isMsie() > 9) {
this.$input.on("input.tt", onInput);
} else {
this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
if (specialKeyCodeMap[$e.which || $e.keyCode]) {
return;
}
_.defer(_.bind(that._onInput, that, $e));
});
}
return this;
},
focus: function focus() {
this.$input.focus();
},
blur: function blur() {
this.$input.blur();
},
getLangDir: function getLangDir() {
return this.dir;
},
getQuery: function getQuery() {
return this.query || "";
},
setQuery: function setQuery(val, silent) {
this.setInputValue(val);
this._setQuery(val, silent);
},
hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {
return this.query !== this.queryWhenFocused;
},
getInputValue: function getInputValue() {
return this.$input.val();
},
setInputValue: function setInputValue(value) {
this.$input.val(value);
this.clearHintIfInvalid();
this._checkLanguageDirection();
},
resetInputValue: function resetInputValue() {
this.setInputValue(this.query);
},
getHint: function getHint() {
return this.$hint.val();
},
setHint: function setHint(value) {
this.$hint.val(value);
},
clearHint: function clearHint() {
this.setHint("");
},
clearHintIfInvalid: function clearHintIfInvalid() {
var val, hint, valIsPrefixOfHint, isValid;
val = this.getInputValue();
hint = this.getHint();
valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
!isValid && this.clearHint();
},
hasFocus: function hasFocus() {
return this.$input.is(":focus");
},
hasOverflow: function hasOverflow() {
var constraint = this.$input.width() - 2;
this.$overflowHelper.text(this.getInputValue());
return this.$overflowHelper.width() >= constraint;
},
isCursorAtEnd: function() {
var valueLength, selectionStart, range;
valueLength = this.$input.val().length;
selectionStart = this.$input[0].selectionStart;
if (_.isNumber(selectionStart)) {
return selectionStart === valueLength;
} else if (document.selection) {
range = document.selection.createRange();
range.moveStart("character", -valueLength);
return valueLength === range.text.length;
}
return true;
},
destroy: function destroy() {
this.$hint.off(".tt");
this.$input.off(".tt");
this.$overflowHelper.remove();
this.$hint = this.$input = this.$overflowHelper = $("<div>");
}
});
return Input;
function buildOverflowHelper($input) {
return $('<pre aria-hidden="true"></pre>').css({
position: "absolute",
visibility: "hidden",
whiteSpace: "pre",
fontFamily: $input.css("font-family"),
fontSize: $input.css("font-size"),
fontStyle: $input.css("font-style"),
fontVariant: $input.css("font-variant"),
fontWeight: $input.css("font-weight"),
wordSpacing: $input.css("word-spacing"),
letterSpacing: $input.css("letter-spacing"),
textIndent: $input.css("text-indent"),
textRendering: $input.css("text-rendering"),
textTransform: $input.css("text-transform")
}).insertAfter($input);
}
function areQueriesEquivalent(a, b) {
return Input.normalizeQuery(a) === Input.normalizeQuery(b);
}
function withModifier($e) {
return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
}
}();
var Dataset = function() {
"use strict";
var keys, nameGenerator;
keys = {
val: "tt-selectable-display",
obj: "tt-selectable-object"
};
nameGenerator = _.getIdGenerator();
function Dataset(o, www) {
o = o || {};
o.templates = o.templates || {};
o.templates.notFound = o.templates.notFound || o.templates.empty;
if (!o.source) {
$.error("missing source");
}
if (!o.node) {
$.error("missing node");
}
if (o.name && !isValidName(o.name)) {
$.error("invalid dataset name: " + o.name);
}
www.mixin(this);
this.highlight = !!o.highlight;
this.name = o.name || nameGenerator();
this.limit = o.limit || 5;
this.displayFn = getDisplayFn(o.display || o.displayKey);
this.templates = getTemplates(o.templates, this.displayFn);
this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
this._resetLastSuggestion();
this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
}
Dataset.extractData = function extractData(el) {
var $el = $(el);
if ($el.data(keys.obj)) {
return {
val: $el.data(keys.val) || "",
obj: $el.data(keys.obj) || null
};
}
return null;
};
_.mixin(Dataset.prototype, EventEmitter, {
_overwrite: function overwrite(query, suggestions) {
suggestions = suggestions || [];
if (suggestions.length) {
this._renderSuggestions(query, suggestions);
} else if (this.async && this.templates.pending) {
this._renderPending(query);
} else if (!this.async && this.templates.notFound) {
this._renderNotFound(query);
} else {
this._empty();
}
this.trigger("rendered", this.name, suggestions, false);
},
_append: function append(query, suggestions) {
suggestions = suggestions || [];
if (suggestions.length && this.$lastSuggestion.length) {
this._appendSuggestions(query, suggestions);
} else if (suggestions.length) {
this._renderSuggestions(query, suggestions);
} else if (!this.$lastSuggestion.length && this.templates.notFound) {
this._renderNotFound(query);
}
this.trigger("rendered", this.name, suggestions, true);
},
_renderSuggestions: function renderSuggestions(query, suggestions) {
var $fragment;
$fragment = this._getSuggestionsFragment(query, suggestions);
this.$lastSuggestion = $fragment.children().last();
this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions));
},
_appendSuggestions: function appendSuggestions(query, suggestions) {
var $fragment, $lastSuggestion;
$fragment = this._getSuggestionsFragment(query, suggestions);
$lastSuggestion = $fragment.children().last();
this.$lastSuggestion.after($fragment);
this.$lastSuggestion = $lastSuggestion;
},
_renderPending: function renderPending(query) {
var template = this.templates.pending;
this._resetLastSuggestion();
template && this.$el.html(template({
query: query,
dataset: this.name
}));
},
_renderNotFound: function renderNotFound(query) {
var template = this.templates.notFound;
this._resetLastSuggestion();
template && this.$el.html(template({
query: query,
dataset: this.name
}));
},
_empty: function empty() {
this.$el.empty();
this._resetLastSuggestion();
},
_getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {
var that = this, fragment;
fragment = document.createDocumentFragment();
_.each(suggestions, function getSuggestionNode(suggestion) {
var $el, context;
context = that._injectQuery(query, suggestion);
$el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable);
fragment.appendChild($el[0]);
});
this.highlight && highlight({
className: this.classes.highlight,
node: fragment,
pattern: query
});
return $(fragment);
},
_getFooter: function getFooter(query, suggestions) {
return this.templates.footer ? this.templates.footer({
query: query,
suggestions: suggestions,
dataset: this.name
}) : null;
},
_getHeader: function getHeader(query, suggestions) {
return this.templates.header ? this.templates.header({
query: query,
suggestions: suggestions,
dataset: this.name
}) : null;
},
_resetLastSuggestion: function resetLastSuggestion() {
this.$lastSuggestion = $();
},
_injectQuery: function injectQuery(query, obj) {
return _.isObject(obj) ? _.mixin({
_query: query
}, obj) : obj;
},
update: function update(query) {
var that = this, canceled = false, syncCalled = false, rendered = 0;
this.cancel();
this.cancel = function cancel() {
canceled = true;
that.cancel = $.noop;
that.async && that.trigger("asyncCanceled", query);
};
this.source(query, sync, async);
!syncCalled && sync([]);
function sync(suggestions) {
if (syncCalled) {
return;
}
syncCalled = true;
suggestions = (suggestions || []).slice(0, that.limit);
rendered = suggestions.length;
that._overwrite(query, suggestions);
if (rendered < that.limit && that.async) {
that.trigger("asyncRequested", query);
}
}
function async(suggestions) {
suggestions = suggestions || [];
if (!canceled && rendered < that.limit) {
that.cancel = $.noop;
rendered += suggestions.length;
that._append(query, suggestions.slice(0, that.limit - rendered));
that.async && that.trigger("asyncReceived", query);
}
}
},
cancel: $.noop,
clear: function clear() {
this._empty();
this.cancel();
this.trigger("cleared");
},
isEmpty: function isEmpty() {
return this.$el.is(":empty");
},
destroy: function destroy() {
this.$el = $("<div>");
}
});
return Dataset;
function getDisplayFn(display) {
display = display || _.stringify;
return _.isFunction(display) ? display : displayFn;
function displayFn(obj) {
return obj[display];
}
}
function getTemplates(templates, displayFn) {
return {
notFound: templates.notFound && _.templatify(templates.notFound),
pending: templates.pending && _.templatify(templates.pending),
header: templates.header && _.templatify(templates.header),
footer: templates.footer && _.templatify(templates.footer),
suggestion: templates.suggestion || suggestionTemplate
};
function suggestionTemplate(context) {
return $("<div>").text(displayFn(context));
}
}
function isValidName(str) {
return /^[_a-zA-Z0-9-]+$/.test(str);
}
}();
var Menu = function() {
"use strict";
function Menu(o, www) {
var that = this;
o = o || {};
if (!o.node) {
$.error("node is required");
}
www.mixin(this);
this.$node = $(o.node);
this.query = null;
this.datasets = _.map(o.datasets, initializeDataset);
function initializeDataset(oDataset) {
var node = that.$node.find(oDataset.node).first();
oDataset.node = node.length ? node : $("<div>").appendTo(that.$node);
return new Dataset(oDataset, www);
}
}
_.mixin(Menu.prototype, EventEmitter, {
_onSelectableClick: function onSelectableClick($e) {
this.trigger("selectableClicked", $($e.currentTarget));
},
_onRendered: function onRendered(type, dataset, suggestions, async) {
this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
this.trigger("datasetRendered", dataset, suggestions, async);
},
_onCleared: function onCleared() {
this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
this.trigger("datasetCleared");
},
_propagate: function propagate() {
this.trigger.apply(this, arguments);
},
_allDatasetsEmpty: function allDatasetsEmpty() {
return _.every(this.datasets, isDatasetEmpty);
function isDatasetEmpty(dataset) {
return dataset.isEmpty();
}
},
_getSelectables: function getSelectables() {
return this.$node.find(this.selectors.selectable);
},
_removeCursor: function _removeCursor() {
var $selectable = this.getActiveSelectable();
$selectable && $selectable.removeClass(this.classes.cursor);
},
_ensureVisible: function ensureVisible($el) {
var elTop, elBottom, nodeScrollTop, nodeHeight;
elTop = $el.position().top;
elBottom = elTop + $el.outerHeight(true);
nodeScrollTop = this.$node.scrollTop();
nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10);
if (elTop < 0) {
this.$node.scrollTop(nodeScrollTop + elTop);
} else if (nodeHeight < elBottom) {
this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));
}
},
bind: function() {
var that = this, onSelectableClick;
onSelectableClick = _.bind(this._onSelectableClick, this);
this.$node.on("click.tt", this.selectors.selectable, onSelectableClick);
_.each(this.datasets, function(dataset) {
dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that);
});
return this;
},
isOpen: function isOpen() {
return this.$node.hasClass(this.classes.open);
},
open: function open() {
this.$node.addClass(this.classes.open);
},
close: function close() {
this.$node.removeClass(this.classes.open);
this._removeCursor();
},
setLanguageDirection: function setLanguageDirection(dir) {
this.$node.attr("dir", dir);
},
selectableRelativeToCursor: function selectableRelativeToCursor(delta) {
var $selectables, $oldCursor, oldIndex, newIndex;
$oldCursor = this.getActiveSelectable();
$selectables = this._getSelectables();
oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;
newIndex = oldIndex + delta;
newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;
newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;
return newIndex === -1 ? null : $selectables.eq(newIndex);
},
setCursor: function setCursor($selectable) {
this._removeCursor();
if ($selectable = $selectable && $selectable.first()) {
$selectable.addClass(this.classes.cursor);
this._ensureVisible($selectable);
}
},
getSelectableData: function getSelectableData($el) {
return $el && $el.length ? Dataset.extractData($el) : null;
},
getActiveSelectable: function getActiveSelectable() {
var $selectable = this._getSelectables().filter(this.selectors.cursor).first();
return $selectable.length ? $selectable : null;
},
getTopSelectable: function getTopSelectable() {
var $selectable = this._getSelectables().first();
return $selectable.length ? $selectable : null;
},
update: function update(query) {
var isValidUpdate = query !== this.query;
if (isValidUpdate) {
this.query = query;
_.each(this.datasets, updateDataset);
}
return isValidUpdate;
function updateDataset(dataset) {
dataset.update(query);
}
},
empty: function empty() {
_.each(this.datasets, clearDataset);
this.query = null;
this.$node.addClass(this.classes.empty);
function clearDataset(dataset) {
dataset.clear();
}
},
destroy: function destroy() {
this.$node.off(".tt");
this.$node = $("<div>");
_.each(this.datasets, destroyDataset);
function destroyDataset(dataset) {
dataset.destroy();
}
}
});
return Menu;
}();
var DefaultMenu = function() {
"use strict";
var s = Menu.prototype;
function DefaultMenu() {
Menu.apply(this, [].slice.call(arguments, 0));
}
_.mixin(DefaultMenu.prototype, Menu.prototype, {
open: function open() {
!this._allDatasetsEmpty() && this._show();
return s.open.apply(this, [].slice.call(arguments, 0));
},
close: function close() {
this._hide();
return s.close.apply(this, [].slice.call(arguments, 0));
},
_onRendered: function onRendered() {
if (this._allDatasetsEmpty()) {
this._hide();
} else {
this.isOpen() && this._show();
}
return s._onRendered.apply(this, [].slice.call(arguments, 0));
},
_onCleared: function onCleared() {
if (this._allDatasetsEmpty()) {
this._hide();
} else {
this.isOpen() && this._show();
}
return s._onCleared.apply(this, [].slice.call(arguments, 0));
},
setLanguageDirection: function setLanguageDirection(dir) {
this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl);
return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));
},
_hide: function hide() {
this.$node.hide();
},
_show: function show() {
this.$node.css("display", "block");
}
});
return DefaultMenu;
}();
var Typeahead = function() {
"use strict";
function Typeahead(o, www) {
var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged;
o = o || {};
if (!o.input) {
$.error("missing input");
}
if (!o.menu) {
$.error("missing menu");
}
if (!o.eventBus) {
$.error("missing event bus");
}
www.mixin(this);
this.eventBus = o.eventBus;
this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
this.input = o.input;
this.menu = o.menu;
this.enabled = true;
this.active = false;
this.input.hasFocus() && this.activate();
this.dir = this.input.getLangDir();
this._hacks();
this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this);
onFocused = c(this, "activate", "open", "_onFocused");
onBlurred = c(this, "deactivate", "_onBlurred");
onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed");
onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed");
onEscKeyed = c(this, "isActive", "_onEscKeyed");
onUpKeyed = c(this, "isActive", "open", "_onUpKeyed");
onDownKeyed = c(this, "isActive", "open", "_onDownKeyed");
onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed");
onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed");
onQueryChanged = c(this, "_openIfActive", "_onQueryChanged");
onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged");
this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this);
}
_.mixin(Typeahead.prototype, {
_hacks: function hacks() {
var $input, $menu;
$input = this.input.$input || $("<div>");
$menu = this.menu.$node || $("<div>");
$input.on("blur.tt", function($e) {
var active, isActive, hasActive;
active = document.activeElement;
isActive = $menu.is(active);
hasActive = $menu.has(active).length > 0;
if (_.isMsie() && (isActive || hasActive)) {
$e.preventDefault();
$e.stopImmediatePropagation();
_.defer(function() {
$input.focus();
});
}
});
$menu.on("mousedown.tt", function($e) {
$e.preventDefault();
});
},
_onSelectableClicked: function onSelectableClicked(type, $el) {
this.select($el);
},
_onDatasetCleared: function onDatasetCleared() {
this._updateHint();
},
_onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {
this._updateHint();
this.eventBus.trigger("render", suggestions, async, dataset);
},
_onAsyncRequested: function onAsyncRequested(type, dataset, query) {
this.eventBus.trigger("asyncrequest", query, dataset);
},
_onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {
this.eventBus.trigger("asynccancel", query, dataset);
},
_onAsyncReceived: function onAsyncReceived(type, dataset, query) {
this.eventBus.trigger("asyncreceive", query, dataset);
},
_onFocused: function onFocused() {
this._minLengthMet() && this.menu.update(this.input.getQuery());
},
_onBlurred: function onBlurred() {
if (this.input.hasQueryChangedSinceLastFocus()) {
this.eventBus.trigger("change", this.input.getQuery());
}
},
_onEnterKeyed: function onEnterKeyed(type, $e) {
var $selectable;
if ($selectable = this.menu.getActiveSelectable()) {
this.select($selectable) && $e.preventDefault();
}
},
_onTabKeyed: function onTabKeyed(type, $e) {
var $selectable;
if ($selectable = this.menu.getActiveSelectable()) {
this.select($selectable) && $e.preventDefault();
} else if ($selectable = this.menu.getTopSelectable()) {
this.autocomplete($selectable) && $e.preventDefault();
}
},
_onEscKeyed: function onEscKeyed() {
this.close();
},
_onUpKeyed: function onUpKeyed() {
this.moveCursor(-1);
},
_onDownKeyed: function onDownKeyed() {
this.moveCursor(+1);
},
_onLeftKeyed: function onLeftKeyed() {
if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
this.autocomplete(this.menu.getTopSelectable());
}
},
_onRightKeyed: function onRightKeyed() {
if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
this.autocomplete(this.menu.getTopSelectable());
}
},
_onQueryChanged: function onQueryChanged(e, query) {
this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();
},
_onWhitespaceChanged: function onWhitespaceChanged() {
this._updateHint();
},
_onLangDirChanged: function onLangDirChanged(e, dir) {
if (this.dir !== dir) {
this.dir = dir;
this.menu.setLanguageDirection(dir);
}
},
_openIfActive: function openIfActive() {
this.isActive() && this.open();
},
_minLengthMet: function minLengthMet(query) {
query = _.isString(query) ? query : this.input.getQuery() || "";
return query.length >= this.minLength;
},
_updateHint: function updateHint() {
var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;
$selectable = this.menu.getTopSelectable();
data = this.menu.getSelectableData($selectable);
val = this.input.getInputValue();
if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {
query = Input.normalizeQuery(val);
escapedQuery = _.escapeRegExChars(query);
frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
match = frontMatchRegEx.exec(data.val);
match && this.input.setHint(val + match[1]);
} else {
this.input.clearHint();
}
},
isEnabled: function isEnabled() {
return this.enabled;
},
enable: function enable() {
this.enabled = true;
},
disable: function disable() {
this.enabled = false;
},
isActive: function isActive() {
return this.active;
},
activate: function activate() {
if (this.isActive()) {
return true;
} else if (!this.isEnabled() || this.eventBus.before("active")) {
return false;
} else {
this.active = true;
this.eventBus.trigger("active");
return true;
}
},
deactivate: function deactivate() {
if (!this.isActive()) {
return true;
} else if (this.eventBus.before("idle")) {
return false;
} else {
this.active = false;
this.close();
this.eventBus.trigger("idle");
return true;
}
},
isOpen: function isOpen() {
return this.menu.isOpen();
},
open: function open() {
if (!this.isOpen() && !this.eventBus.before("open")) {
this.menu.open();
this._updateHint();
this.eventBus.trigger("open");
}
return this.isOpen();
},
close: function close() {
if (this.isOpen() && !this.eventBus.before("close")) {
this.menu.close();
this.input.clearHint();
this.input.resetInputValue();
this.eventBus.trigger("close");
}
return !this.isOpen();
},
setVal: function setVal(val) {
this.input.setQuery(_.toStr(val));
},
getVal: function getVal() {
return this.input.getQuery();
},
select: function select($selectable) {
var data = this.menu.getSelectableData($selectable);
if (data && !this.eventBus.before("select", data.obj)) {
this.input.setQuery(data.val, true);
this.eventBus.trigger("select", data.obj);
this.close();
return true;
}
return false;
},
autocomplete: function autocomplete($selectable) {
var query, data, isValid;
query = this.input.getQuery();
data = this.menu.getSelectableData($selectable);
isValid = data && query !== data.val;
if (isValid && !this.eventBus.before("autocomplete", data.obj)) {
this.input.setQuery(data.val);
this.eventBus.trigger("autocomplete", data.obj);
return true;
}
return false;
},
moveCursor: function moveCursor(delta) {
var query, $candidate, data, payload, cancelMove;
query = this.input.getQuery();
$candidate = this.menu.selectableRelativeToCursor(delta);
data = this.menu.getSelectableData($candidate);
payload = data ? data.obj : null;
cancelMove = this._minLengthMet() && this.menu.update(query);
if (!cancelMove && !this.eventBus.before("cursorchange", payload)) {
this.menu.setCursor($candidate);
if (data) {
this.input.setInputValue(data.val);
} else {
this.input.resetInputValue();
this._updateHint();
}
this.eventBus.trigger("cursorchange", payload);
return true;
}
return false;
},
destroy: function destroy() {
this.input.destroy();
this.menu.destroy();
}
});
return Typeahead;
function c(ctx) {
var methods = [].slice.call(arguments, 1);
return function() {
var args = [].slice.call(arguments);
_.each(methods, function(method) {
return ctx[method].apply(ctx, args);
});
};
}
}();
(function() {
"use strict";
var old, keys, methods;
old = $.fn.typeahead;
keys = {
www: "tt-www",
attrs: "tt-attrs",
typeahead: "tt-typeahead"
};
methods = {
initialize: function initialize(o, datasets) {
var www;
datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
o = o || {};
www = WWW(o.classNames);
return this.each(attach);
function attach() {
var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;
_.each(datasets, function(d) {
d.highlight = !!o.highlight;
});
$input = $(this);
$wrapper = $(www.html.wrapper);
$hint = $elOrNull(o.hint);
$menu = $elOrNull(o.menu);
defaultHint = o.hint !== false && !$hint;
defaultMenu = o.menu !== false && !$menu;
defaultHint && ($hint = buildHintFromInput($input, www));
defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));
$hint && $hint.val("");
$input = prepInput($input, www);
if (defaultHint || defaultMenu) {
$wrapper.css(www.css.wrapper);
$input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);
$input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null);
}
MenuConstructor = defaultMenu ? DefaultMenu : Menu;
eventBus = new EventBus({
el: $input
});
input = new Input({
hint: $hint,
input: $input
}, www);
menu = new MenuConstructor({
node: $menu,
datasets: datasets
}, www);
typeahead = new Typeahead({
input: input,
menu: menu,
eventBus: eventBus,
minLength: o.minLength
}, www);
$input.data(keys.www, www);
$input.data(keys.typeahead, typeahead);
}
},
isEnabled: function isEnabled() {
var enabled;
ttEach(this.first(), function(t) {
enabled = t.isEnabled();
});
return enabled;
},
enable: function enable() {
ttEach(this, function(t) {
t.enable();
});
return this;
},
disable: function disable() {
ttEach(this, function(t) {
t.disable();
});
return this;
},
isActive: function isActive() {
var active;
ttEach(this.first(), function(t) {
active = t.isActive();
});
return active;
},
activate: function activate() {
ttEach(this, function(t) {
t.activate();
});
return this;
},
deactivate: function deactivate() {
ttEach(this, function(t) {
t.deactivate();
});
return this;
},
isOpen: function isOpen() {
var open;
ttEach(this.first(), function(t) {
open = t.isOpen();
});
return open;
},
open: function open() {
ttEach(this, function(t) {
t.open();
});
return this;
},
close: function close() {
ttEach(this, function(t) {
t.close();
});
return this;
},
select: function select(el) {
var success = false, $el = $(el);
ttEach(this.first(), function(t) {
success = t.select($el);
});
return success;
},
autocomplete: function autocomplete(el) {
var success = false, $el = $(el);
ttEach(this.first(), function(t) {
success = t.autocomplete($el);
});
return success;
},
moveCursor: function moveCursoe(delta) {
var success = false;
ttEach(this.first(), function(t) {
success = t.moveCursor(delta);
});
return success;
},
val: function val(newVal) {
var query;
if (!arguments.length) {
ttEach(this.first(), function(t) {
query = t.getVal();
});
return query;
} else {
ttEach(this, function(t) {
t.setVal(newVal);
});
return this;
}
},
destroy: function destroy() {
ttEach(this, function(typeahead, $input) {
revert($input);
typeahead.destroy();
});
return this;
}
};
$.fn.typeahead = function(method) {
if (methods[method]) {
return methods[method].apply(this, [].slice.call(arguments, 1));
} else {
return methods.initialize.apply(this, arguments);
}
};
$.fn.typeahead.noConflict = function noConflict() {
$.fn.typeahead = old;
return this;
};
function ttEach($els, fn) {
$els.each(function() {
var $input = $(this), typeahead;
(typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);
});
}
function buildHintFromInput($input, www) {
return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({
autocomplete: "off",
spellcheck: "false",
tabindex: -1
});
}
function prepInput($input, www) {
$input.data(keys.attrs, {
dir: $input.attr("dir"),
autocomplete: $input.attr("autocomplete"),
spellcheck: $input.attr("spellcheck"),
style: $input.attr("style")
});
$input.addClass(www.classes.input).attr({
autocomplete: "off",
spellcheck: false
});
try {
!$input.attr("dir") && $input.attr("dir", "auto");
} catch (e) {}
return $input;
}
function getBackgroundStyles($el) {
return {
backgroundAttachment: $el.css("background-attachment"),
backgroundClip: $el.css("background-clip"),
backgroundColor: $el.css("background-color"),
backgroundImage: $el.css("background-image"),
backgroundOrigin: $el.css("background-origin"),
backgroundPosition: $el.css("background-position"),
backgroundRepeat: $el.css("background-repeat"),
backgroundSize: $el.css("background-size")
};
}
function revert($input) {
var www, $wrapper;
www = $input.data(keys.www);
$wrapper = $input.parent().filter(www.selectors.wrapper);
_.each($input.data(keys.attrs), function(val, key) {
_.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
});
$input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input);
if ($wrapper.length) {
$input.detach().insertAfter($wrapper);
$wrapper.remove();
}
}
function $elOrNull(obj) {
var isValid, $el;
isValid = _.isJQuery(obj) || _.isElement(obj);
$el = isValid ? $(obj).first() : [];
return $el.length ? $el : null;
}
})();
});
================================================
FILE: dist/typeahead.jquery.js
================================================
/*!
* typeahead.js 0.11.1
* https://github.com/twitter/typeahead.js
* Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT
*/
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define("typeahead.js", [ "jquery" ], function(a0) {
return factory(a0);
});
} else if (typeof exports === "object") {
module.exports = factory(require("jquery"));
} else {
factory(jQuery);
}
})(this, function($) {
var _ = function() {
"use strict";
return {
isMsie: function() {
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
},
isBlankString: function(str) {
return !str || /^\s*$/.test(str);
},
escapeRegExChars: function(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
isString: function(obj) {
return typeof obj === "string";
},
isNumber: function(obj) {
return typeof obj === "number";
},
isArray: $.isArray,
isFunction: $.isFunction,
isObject: $.isPlainObject,
isUndefined: function(obj) {
return typeof obj === "undefined";
},
isElement: function(obj) {
return !!(obj && obj.nodeType === 1);
},
isJQuery: function(obj) {
return obj instanceof $;
},
toStr: function toStr(s) {
return _.isUndefined(s) || s === null ? "" : s + "";
},
bind: $.proxy,
each: function(collection, cb) {
$.each(collection, reverseArgs);
function reverseArgs(index, value) {
return cb(value, index);
}
},
map: $.map,
filter: $.grep,
every: function(obj, test) {
var result = true;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (!(result = test.call(null, val, key, obj))) {
return false;
}
});
return !!result;
},
some: function(obj, test) {
var result = false;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (result = test.call(null, val, key, obj)) {
return false;
}
});
return !!result;
},
mixin: $.extend,
identity: function(x) {
return x;
},
clone: function(obj) {
return $.extend(true, {}, obj);
},
getIdGenerator: function() {
var counter = 0;
return function() {
return counter++;
};
},
templatify: function templatify(obj) {
return $.isFunction(obj) ? obj : template;
function template() {
return String(obj);
}
},
defer: function(fn) {
setTimeout(fn, 0);
},
debounce: function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments, later, callNow;
later = function() {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
}
};
callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
}
return result;
};
},
throttle: function(func, wait) {
var context, args, timeout, result, previous, later;
previous = 0;
later = function() {
previous = new Date();
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date(), remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
},
stringify: function(val) {
return _.isString(val) ? val : JSON.stringify(val);
},
noop: function() {}
};
}();
var WWW = function() {
"use strict";
var defaultClassNames = {
wrapper: "twitter-typeahead",
input: "tt-input",
hint: "tt-hint",
menu: "tt-menu",
dataset: "tt-dataset",
suggestion: "tt-suggestion",
selectable: "tt-selectable",
empty: "tt-empty",
open: "tt-open",
cursor: "tt-cursor",
highlight: "tt-highlight"
};
return build;
function build(o) {
var www, classes;
classes = _.mixin({}, defaultClassNames, o);
www = {
css: buildCss(),
classes: classes,
html: buildHtml(classes),
selectors: buildSelectors(classes)
};
return {
css: www.css,
html: www.html,
classes: www.classes,
selectors: www.selectors,
mixin: function(o) {
_.mixin(o, www);
}
};
}
function buildHtml(c) {
return {
wrapper: '<span class="' + c.wrapper + '"></span>',
menu: '<div class="' + c.menu + '"></div>'
};
}
function buildSelectors(classes) {
var selectors = {};
_.each(classes, function(v, k) {
selectors[k] = "." + v;
});
return selectors;
}
function buildCss() {
var css = {
wrapper: {
position: "relative",
display: "inline-block"
},
hint: {
position: "absolute",
top: "0",
left: "0",
borderColor: "transparent",
boxShadow: "none",
opacity: "1"
},
input: {
position: "relative",
verticalAlign: "top",
backgroundColor: "transparent"
},
inputWithNoHint: {
position: "relative",
verticalAlign: "top"
},
menu: {
position: "absolute",
top: "100%",
left: "0",
zIndex: "100",
display: "none"
},
ltr: {
left: "0",
right: "auto"
},
rtl: {
left: "auto",
right: " 0"
}
};
if (_.isMsie()) {
_.mixin(css.input, {
backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
});
}
return css;
}
}();
var EventBus = function() {
"use strict";
var namespace, deprecationMap;
namespace = "typeahead:";
deprecationMap = {
render: "rendered",
cursorchange: "cursorchanged",
select: "selected",
autocomplete: "autocompleted"
};
function EventBus(o) {
if (!o || !o.el) {
$.error("EventBus initialized without el");
}
this.$el = $(o.el);
}
_.mixin(EventBus.prototype, {
_trigger: function(type, args) {
var $e;
$e = $.Event(namespace + type);
(args = args || []).unshift($e);
this.$el.trigger.apply(this.$el, args);
return $e;
},
before: function(type) {
var args, $e;
args = [].slice.call(arguments, 1);
$e = this._trigger("before" + type, args);
return $e.isDefaultPrevented();
},
trigger: function(type) {
var deprecatedType;
this._trigger(type, [].slice.call(arguments, 1));
if (deprecatedType = deprecationMap[type]) {
this._trigger(deprecatedType, [].slice.call(arguments, 1));
}
}
});
return EventBus;
}();
var EventEmitter = function() {
"use strict";
var splitter = /\s+/, nextTick = getNextTick();
return {
onSync: onSync,
onAsync: onAsync,
off: off,
trigger: trigger
};
function on(method, types, cb, context) {
var type;
if (!cb) {
return this;
}
types = types.split(splitter);
cb = context ? bindContext(cb, context) : cb;
this._callbacks = this._callbacks || {};
while (type = types.shift()) {
this._callbacks[type] = this._callbacks[type] || {
sync: [],
async: []
};
this._callbacks[type][method].push(cb);
}
return this;
}
function onAsync(types, cb, context) {
return on.call(this, "async", types, cb, context);
}
function onSync(types, cb, context) {
return on.call(this, "sync", types, cb, context);
}
function off(types) {
var type;
if (!this._callbacks) {
return this;
}
types = types.split(splitter);
while (type = types.shift()) {
delete this._callbacks[type];
}
return this;
}
function trigger(types) {
var type, callbacks, args, syncFlush, asyncFlush;
if (!this._callbacks) {
return this;
}
types = types.split(splitter);
args = [].slice.call(arguments, 1);
while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));
asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));
syncFlush() && nextTick(asyncFlush);
}
return this;
}
function getFlush(callbacks, context, args) {
return flush;
function flush() {
var cancelled;
for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
cancelled = callbacks[i].apply(context, args) === false;
}
return !cancelled;
}
}
function getNextTick() {
var nextTickFn;
if (window.setImmediate) {
nextTickFn = function nextTickSetImmediate(fn) {
setImmediate(function() {
fn();
});
};
} else {
nextTickFn = function nextTickSetTimeout(fn) {
setTimeout(function() {
fn();
}, 0);
};
}
return nextTickFn;
}
function bindContext(fn, context) {
return fn.bind ? fn.bind(context) : function() {
fn.apply(context, [].slice.call(arguments, 0));
};
}
}();
var highlight = function(doc) {
"use strict";
var defaults = {
node: null,
pattern: null,
tagName: "strong",
className: null,
wordsOnly: false,
caseSensitive: false
};
return function hightlight(o) {
var regex;
o = _.mixin({}, defaults, o);
if (!o.node || !o.pattern) {
return;
}
o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
traverse(o.node, hightlightTextNode);
function hightlightTextNode(textNode) {
var match, patternNode, wrapperNode;
if (match = regex.exec(textNode.data)) {
wrapperNode = doc.createElement(o.tagName);
o.className && (wrapperNode.className = o.className);
patternNode = textNode.splitText(match.index);
patternNode.splitText(match[0].length);
wrapperNode.appendChild(patternNode.cloneNode(true));
textNode.parentNode.replaceChild(wrapperNode, patternNode);
}
return !!match;
}
function traverse(el, hightlightTextNode) {
var childNode, TEXT_NODE_TYPE = 3;
for (var i = 0; i < el.childNodes.length; i++) {
childNode = el.childNodes[i];
if (childNode.nodeType === TEXT_NODE_TYPE) {
i += hightlightTextNode(childNode) ? 1 : 0;
} else {
traverse(childNode, hightlightTextNode);
}
}
}
};
function getRegex(patterns, caseSensitive, wordsOnly) {
var escapedPatterns = [], regexStr;
for (var i = 0, len = patterns.length; i < len; i++) {
escapedPatterns.push(_.escapeRegExChars(patterns[i]));
}
regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
}
}(window.document);
var Input = function() {
"use strict";
var specialKeyCodeMap;
specialKeyCodeMap = {
9: "tab",
27: "esc",
37: "left",
39: "right",
13: "enter",
38: "up",
40: "down"
};
function Input(o, www) {
o = o || {};
if (!o.input) {
$.error("input is missing");
}
www.mixin(this);
this.$hint = $(o.hint);
this.$input = $(o.input);
this.query = this.$input.val();
this.queryWhenFocused = this.hasFocus() ? this.query : null;
this.$overflowHelper = buildOverflowHelper(this.$input);
this._checkLanguageDirection();
if (this.$hint.length === 0) {
this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
}
}
Input.normalizeQuery = function(str) {
return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
};
_.mixin(Input.prototype, EventEmitter, {
_onBlur: function onBlur() {
this.resetInputValue();
this.trigger("blurred");
},
_onFocus: function onFocus() {
this.queryWhenFocused = this.query;
this.trigger("focused");
},
_onKeydown: function onKeydown($e) {
var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
this._managePreventDefault(keyName, $e);
if (keyName && this._shouldTrigger(keyName, $e)) {
this.trigger(keyName + "Keyed", $e);
}
},
_onInput: function onInput() {
this._setQuery(this.getInputValue());
this.clearHintIfInvalid();
this._checkLanguageDirection();
},
_managePreventDefault: function managePreventDefault(keyName, $e) {
var preventDefault;
switch (keyName) {
case "up":
case "down":
preventDefault = !withModifier($e);
break;
default:
preventDefault = false;
}
preventDefault && $e.preventDefault();
},
_shouldTrigger: function shouldTrigger(keyName, $e) {
var trigger;
switch (keyName) {
case "tab":
trigger = !withModifier($e);
break;
default:
trigger = true;
}
return trigger;
},
_checkLanguageDirection: function checkLanguageDirection() {
var dir = (this.$input.css("direction") || "ltr").toLowerCase();
if (this.dir !== dir) {
this.dir = dir;
this.$hint.attr("dir", dir);
this.trigger("langDirChanged", dir);
}
},
_setQuery: function setQuery(val, silent) {
var areEquivalent, hasDifferentWhitespace;
areEquivalent = areQueriesEquivalent(val, this.query);
hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;
this.query = val;
if (!silent && !areEquivalent) {
this.trigger("queryChanged", this.query);
} else if (!silent && hasDifferentWhitespace) {
this.trigger("whitespaceChanged", this.query);
}
},
bind: function() {
var that = this, onBlur, onFocus, onKeydown, onInput;
onBlur = _.bind(this._onBlur, this);
onFocus = _.bind(this._onFocus, this);
onKeydown = _.bind(this._onKeydown, this);
onInput = _.bind(this._onInput, this);
this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
if (!_.isMsie() || _.isMsie() > 9) {
this.$input.on("input.tt", onInput);
} else {
this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
if (specialKeyCodeMap[$e.which || $e.keyCode]) {
return;
}
_.defer(_.bind(that._onInput, that, $e));
});
}
return this;
},
focus: function focus() {
this.$input.focus();
},
blur: function blur() {
this.$input.blur();
},
getLangDir: function getLangDir() {
return this.dir;
},
getQuery: function getQuery() {
return this.query || "";
},
setQuery: function setQuery(val, silent) {
this.setInputValue(val);
this._setQuery(val, silent);
},
hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {
return this.query !== this.queryWhenFocused;
},
getInputValue: function getInputValue() {
return this.$input.val();
},
setInputValue: function setInputValue(value) {
this.$input.val(value);
this.clearHintIfInvalid();
this._checkLanguageDirection();
},
resetInputValue: function resetInputValue() {
this.setInputValue(this.query);
},
getHint: function getHint() {
return this.$hint.val();
},
setHint: function setHint(value) {
this.$hint.val(value);
},
clearHint: function clearHint() {
this.setHint("");
},
clearHintIfInvalid: function clearHintIfInvalid() {
var val, hint, valIsPrefixOfHint, isValid;
val = this.getInputValue();
hint = this.getHint();
valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
!isValid && this.clearHint();
},
hasFocus: function hasFocus() {
return this.$input.is(":focus");
},
hasOverflow: function hasOverflow() {
var constraint = this.$input.width() - 2;
this.$overflowHelper.text(this.getInputValue());
return this.$overflowHelper.width() >= constraint;
},
isCursorAtEnd: function() {
var valueLength, selectionStart, range;
valueLength = this.$input.val().length;
selectionStart = this.$input[0].selectionStart;
if (_.isNumber(selectionStart)) {
return selectionStart === valueLength;
} else if (document.selection) {
range = document.selection.createRange();
range.moveStart("character", -valueLength);
return valueLength === range.text.length;
}
return true;
},
destroy: function destroy() {
this.$hint.off(".tt");
this.$input.off(".tt");
this.$overflowHelper.remove();
this.$hint = this.$input = this.$overflowHelper = $("<div>");
}
});
return Input;
function buildOverflowHelper($input) {
return $('<pre aria-hidden="true"></pre>').css({
position: "absolute",
visibility: "hidden",
whiteSpace: "pre",
fontFamily: $input.css("font-family"),
fontSize: $input.css("font-size"),
fontStyle: $input.css("font-style"),
fontVariant: $input.css("font-variant"),
fontWeight: $input.css("font-weight"),
wordSpacing: $input.css("word-spacing"),
letterSpacing: $input.css("letter-spacing"),
textIndent: $input.css("text-indent"),
textRendering: $input.css("text-rendering"),
textTransform: $input.css("text-transform")
}).insertAfter($input);
}
function areQueriesEquivalent(a, b) {
return Input.normalizeQuery(a) === Input.normalizeQuery(b);
}
function withModifier($e) {
return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
}
}();
var Dataset = function() {
"use strict";
var keys, nameGenerator;
keys = {
val: "tt-selectable-display",
obj: "tt-selectable-object"
};
nameGenerator = _.getIdGenerator();
function Dataset(o, www) {
o = o || {};
o.templates = o.templates || {};
o.templates.notFound = o.templates.notFound || o.templates.empty;
if (!o.source) {
$.error("missing source");
}
if (!o.node) {
$.error("missing node");
}
if (o.name && !isValidName(o.name)) {
$.error("invalid dataset name: " + o.name);
}
www.mixin(this);
this.highlight = !!o.highlight;
this.name = o.name || nameGenerator();
this.limit = o.limit || 5;
this.displayFn = getDisplayFn(o.display || o.displayKey);
this.templates = getTemplates(o.templates, this.displayFn);
this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
this._resetLastSuggestion();
this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
}
Dataset.extractData = function extractData(el) {
var $el = $(el);
if ($el.data(keys.obj)) {
return {
val: $el.data(keys.val) || "",
obj: $el.data(keys.obj) || null
};
}
return null;
};
_.mixin(Dataset.prototype, EventEmitter, {
_overwrite: function overwrite(query, suggestions) {
suggestions = suggestions || [];
if (suggestions.length) {
this._renderSuggestions(query, suggestions);
} else if (this.async && this.templates.pending) {
this._renderPending(query);
} else if (!this.async && this.templates.notFound) {
this._renderNotFound(query);
} else {
this._empty();
}
this.trigger("rendered", this.name, suggestions, false);
},
_append: function append(query, suggestions) {
suggestions = suggestions || [];
if (suggestions.length && this.$lastSuggestion.length) {
this._appendSuggestions(query, suggestions);
} else if (suggestions.length) {
this._renderSuggestions(query, suggestions);
} else if (!this.$lastSuggestion.length && this.templates.notFound) {
this._renderNotFound(query);
}
this.trigger("rendered", this.name, suggestions, true);
},
_renderSuggestions: function renderSuggestions(query, suggestions) {
var $fragment;
$fragment = this._getSuggestionsFragment(query, suggestions);
this.$lastSuggestion = $fragment.children().last();
this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions));
},
_appendSuggestions: function appendSuggestions(query, suggestions) {
var $fragment, $lastSuggestion;
$fragment = this._getSuggestionsFragment(query, suggestions);
$lastSuggestion = $fragment.children().last();
this.$lastSuggestion.after($fragment);
this.$lastSuggestion = $lastSuggestion;
},
_renderPending: function renderPending(query) {
var template = this.templates.pending;
this._resetLastSuggestion();
template && this.$el.html(template({
query: query,
dataset: this.name
}));
},
_renderNotFound: function renderNotFound(query) {
var template = this.templates.notFound;
this._resetLastSuggestion();
template && this.$el.html(template({
query: query,
dataset: this.name
}));
},
_empty: function empty() {
this.$el.empty();
this._resetLastSuggestion();
},
_getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {
var that = this, fragment;
fragment = document.createDocumentFragment();
_.each(suggestions, function getSuggestionNode(suggestion) {
var $el, context;
context = that._injectQuery(query, suggestion);
$el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable);
fragment.appendChild($el[0]);
});
this.highlight && highlight({
className: this.classes.highlight,
node: fragment,
pattern: query
});
return $(fragment);
},
_getFooter: function getFooter(query, suggestions) {
return this.templates.footer ? this.templates.footer({
query: query,
suggestions: suggestions,
dataset: this.name
}) : null;
},
_getHeader: function getHeader(query, suggestions) {
return this.templates.header ? this.templates.header({
query: query,
suggestions: suggestions,
dataset: this.name
}) : null;
},
_resetLastSuggestion: function resetLastSuggestion() {
this.$lastSuggestion = $();
},
_injectQuery: function injectQuery(query, obj) {
return _.isObject(obj) ? _.mixin({
_query: query
}, obj) : obj;
},
update: function update(query) {
var that = this, canceled = false, syncCalled = false, rendered = 0;
this.cancel();
this.cancel = function cancel() {
canceled = true;
that.cancel = $.noop;
that.async && that.trigger("asyncCanceled", query);
};
this.source(query, sync, async);
!syncCalled && sync([]);
function sync(suggestions) {
if (syncCalled) {
return;
}
syncCalled = true;
suggestions = (suggestions || []).slice(0, that.limit);
rendered = suggestions.length;
that._overwrite(query, suggestions);
if (rendered < that.limit && that.async) {
that.trigger("asyncRequested", query);
}
}
function async(suggestions) {
suggestions = suggestions || [];
if (!canceled && rendered < that.limit) {
that.cancel = $.noop;
rendered += suggestions.length;
that._append(query, suggestions.slice(0, that.limit - rendered));
that.async && that.trigger("asyncReceived", query);
}
}
},
cancel: $.noop,
clear: function clear() {
this._empty();
this.cancel();
this.trigger("cleared");
},
isEmpty: function isEmpty() {
return this.$el.is(":empty");
},
destroy: function destroy() {
this.$el = $("<div>");
}
});
return Dataset;
function getDisplayFn(display) {
display = display || _.stringify;
return _.isFunction(display) ? display : displayFn;
function displayFn(obj) {
return obj[display];
}
}
function getTemplates(templates, displayFn) {
return {
notFound: templates.notFound && _.templatify(templates.notFound),
pending: templates.pending && _.templatify(templates.pending),
header: templates.header && _.templatify(templates.header),
footer: templates.footer && _.templatify(templates.footer),
suggestion: templates.suggestion || suggestionTemplate
};
function suggestionTemplate(context) {
return $("<div>").text(displayFn(context));
}
}
function isValidName(str) {
return /^[_a-zA-Z0-9-]+$/.test(str);
}
}();
var Menu = function() {
"use strict";
function Menu(o, www) {
var that = this;
o = o || {};
if (!o.node) {
$.error("node is required");
}
www.mixin(this);
this.$node = $(o.node);
this.query = null;
this.datasets = _.map(o.datasets, initializeDataset);
function initializeDataset(oDataset) {
var node = that.$node.find(oDataset.node).first();
oDataset.node = node.length ? node : $("<div>").appendTo(that.$node);
return new Dataset(oDataset, www);
}
}
_.mixin(Menu.prototype, EventEmitter, {
_onSelectableClick: function onSelectableClick($e) {
this.trigger("selectableClicked", $($e.currentTarget));
},
_onRendered: function onRendered(type, dataset, suggestions, async) {
this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
this.trigger("datasetRendered", dataset, suggestions, async);
},
_onCleared: function onCleared() {
this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
this.trigger("datasetCleared");
},
_propagate: function propagate() {
this.trigger.apply(this, arguments);
},
_allDatasetsEmpty: function allDatasetsEmpty() {
return _.every(this.datasets, isDatasetEmpty);
function isDatasetEmpty(dataset) {
return dataset.isEmpty();
}
},
_getSelectables: function getSelectables() {
return this.$node.find(this.selectors.selectable);
},
_removeCursor: function _removeCursor() {
var $selectable = this.getActiveSelectable();
$selectable && $selectable.removeClass(this.classes.cursor);
},
_ensureVisible: function ensureVisible($el) {
var elTop, elBottom, nodeScrollTop, nodeHeight;
elTop = $el.position().top;
gitextract_0m8m_xl4/ ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── composer.json ├── dist/ │ ├── bloodhound.js │ ├── typeahead.bundle.js │ └── typeahead.jquery.js ├── doc/ │ ├── bloodhound.md │ ├── jquery_typeahead.md │ └── migration/ │ └── 0.10.0.md ├── karma.conf.js ├── package.json ├── src/ │ ├── bloodhound/ │ │ ├── bloodhound.js │ │ ├── lru_cache.js │ │ ├── options_parser.js │ │ ├── persistent_storage.js │ │ ├── prefetch.js │ │ ├── remote.js │ │ ├── search_index.js │ │ ├── tokenizers.js │ │ ├── transport.js │ │ └── version.js │ ├── common/ │ │ └── utils.js │ └── typeahead/ │ ├── dataset.js │ ├── default_menu.js │ ├── event_bus.js │ ├── event_emitter.js │ ├── highlight.js │ ├── input.js │ ├── menu.js │ ├── plugin.js │ ├── typeahead.js │ └── www.js ├── test/ │ ├── bloodhound/ │ │ ├── bloodhound_spec.js │ │ ├── lru_cache_spec.js │ │ ├── options_parser_spec.js │ │ ├── persistent_storage_spec.js │ │ ├── prefetch_spec.js │ │ ├── remote_spec.js │ │ ├── search_index_spec.js │ │ ├── tokenizers_spec.js │ │ └── transport_spec.js │ ├── ci │ ├── fixtures/ │ │ ├── ajax_responses.js │ │ ├── data.js │ │ └── html.js │ ├── helpers/ │ │ └── typeahead_mocks.js │ ├── integration/ │ │ ├── test.html │ │ └── test.js │ ├── playground.html │ └── typeahead/ │ ├── dataset_spec.js │ ├── default_results_spec.js │ ├── event_bus_spec.js │ ├── event_emitter_spec.js │ ├── highlight_spec.js │ ├── input_spec.js │ ├── plugin_spec.js │ ├── results_spec.js │ └── typeahead_spec.js └── typeahead.js.jquery.json
SYMBOL INDEX (295 symbols across 38 files)
FILE: dist/bloodhound.js
function reverseArgs (line 54) | function reverseArgs(index, value) {
function template (line 99) | function template() {
function whitespace (line 165) | function whitespace(str) {
function nonword (line 169) | function nonword(str) {
function getObjTokenizer (line 173) | function getObjTokenizer(tokenizer) {
function LruCache (line 188) | function LruCache(maxSize) {
function List (line 226) | function List() {
function Node (line 247) | function Node(key, val) {
function PersistentStorage (line 264) | function PersistentStorage(namespace, override) {
function now (line 323) | function now() {
function encode (line 326) | function encode(val) {
function decode (line 329) | function decode(val) {
function gatherMatchingKeys (line 332) | function gatherMatchingKeys(keyMatcher) {
function Transport (line 345) | function Transport(o) {
function done (line 378) | function done(resp) {
function fail (line 382) | function fail() {
function always (line 385) | function always() {
function SearchIndex (line 418) | function SearchIndex(o) {
function normalizeTokens (line 501) | function normalizeTokens(tokens) {
function newNode (line 510) | function newNode() {
function unique (line 516) | function unique(array) {
function getIntersection (line 526) | function getIntersection(arrayA, arrayB) {
function Prefetch (line 553) | function Prefetch(o) {
function onError (line 597) | function onError() {
function onResponse (line 600) | function onResponse(resp) {
function Remote (line 613) | function Remote(o) {
function onResponse (line 639) | function onResponse(err, resp) {
function parsePrefetch (line 676) | function parsePrefetch(o) {
function parseRemote (line 702) | function parseRemote(o) {
function toRemotePrepare (line 734) | function toRemotePrepare(o) {
function toLimiter (line 762) | function toLimiter(o) {
function callbackToDeferred (line 782) | function callbackToDeferred(fn) {
function Bloodhound (line 804) | function Bloodhound(o) {
function withAsync (line 828) | function withAsync(query, sync, async) {
function withoutAsync (line 831) | function withoutAsync(query, sync) {
function done (line 847) | function done(err, data) {
function addLocalToIndex (line 861) | function addLocalToIndex() {
function processRemote (line 886) | function processRemote(remote) {
FILE: dist/typeahead.bundle.js
function reverseArgs (line 54) | function reverseArgs(index, value) {
function template (line 99) | function template() {
function whitespace (line 165) | function whitespace(str) {
function nonword (line 169) | function nonword(str) {
function getObjTokenizer (line 173) | function getObjTokenizer(tokenizer) {
function LruCache (line 188) | function LruCache(maxSize) {
function List (line 226) | function List() {
function Node (line 247) | function Node(key, val) {
function PersistentStorage (line 264) | function PersistentStorage(namespace, override) {
function now (line 323) | function now() {
function encode (line 326) | function encode(val) {
function decode (line 329) | function decode(val) {
function gatherMatchingKeys (line 332) | function gatherMatchingKeys(keyMatcher) {
function Transport (line 345) | function Transport(o) {
function done (line 378) | function done(resp) {
function fail (line 382) | function fail() {
function always (line 385) | function always() {
function SearchIndex (line 418) | function SearchIndex(o) {
function normalizeTokens (line 501) | function normalizeTokens(tokens) {
function newNode (line 510) | function newNode() {
function unique (line 516) | function unique(array) {
function getIntersection (line 526) | function getIntersection(arrayA, arrayB) {
function Prefetch (line 553) | function Prefetch(o) {
function onError (line 597) | function onError() {
function onResponse (line 600) | function onResponse(resp) {
function Remote (line 613) | function Remote(o) {
function onResponse (line 639) | function onResponse(err, resp) {
function parsePrefetch (line 676) | function parsePrefetch(o) {
function parseRemote (line 702) | function parseRemote(o) {
function toRemotePrepare (line 734) | function toRemotePrepare(o) {
function toLimiter (line 762) | function toLimiter(o) {
function callbackToDeferred (line 782) | function callbackToDeferred(fn) {
function Bloodhound (line 804) | function Bloodhound(o) {
function withAsync (line 828) | function withAsync(query, sync, async) {
function withoutAsync (line 831) | function withoutAsync(query, sync) {
function done (line 847) | function done(err, data) {
function addLocalToIndex (line 861) | function addLocalToIndex() {
function processRemote (line 886) | function processRemote(remote) {
function reverseArgs (line 967) | function reverseArgs(index, value) {
function template (line 1012) | function template() {
function build (line 1083) | function build(o) {
function buildHtml (line 1102) | function buildHtml(c) {
function buildSelectors (line 1108) | function buildSelectors(classes) {
function buildCss (line 1115) | function buildCss() {
function EventBus (line 1172) | function EventBus(o) {
function on (line 1211) | function on(method, types, cb, context) {
function onAsync (line 1228) | function onAsync(types, cb, context) {
function onSync (line 1231) | function onSync(types, cb, context) {
function off (line 1234) | function off(types) {
function trigger (line 1245) | function trigger(types) {
function getFlush (line 1259) | function getFlush(callbacks, context, args) {
function getNextTick (line 1269) | function getNextTick() {
function bindContext (line 1286) | function bindContext(fn, context) {
function hightlightTextNode (line 1311) | function hightlightTextNode(textNode) {
function traverse (line 1323) | function traverse(el, hightlightTextNode) {
function getRegex (line 1335) | function getRegex(patterns, caseSensitive, wordsOnly) {
function Input (line 1356) | function Input(o, www) {
function buildOverflowHelper (line 1535) | function buildOverflowHelper($input) {
function areQueriesEquivalent (line 1552) | function areQueriesEquivalent(a, b) {
function withModifier (line 1555) | function withModifier($e) {
function Dataset (line 1567) | function Dataset(o, www) {
function sync (line 1707) | function sync(suggestions) {
function async (line 1719) | function async(suggestions) {
function getDisplayFn (line 1743) | function getDisplayFn(display) {
function getTemplates (line 1750) | function getTemplates(templates, displayFn) {
function isValidName (line 1762) | function isValidName(str) {
function Menu (line 1768) | function Menu(o, www) {
function isDatasetEmpty (line 1801) | function isDatasetEmpty(dataset) {
function updateDataset (line 1881) | function updateDataset(dataset) {
function clearDataset (line 1889) | function clearDataset(dataset) {
function destroyDataset (line 1897) | function destroyDataset(dataset) {
function DefaultMenu (line 1907) | function DefaultMenu() {
function Typeahead (line 1950) | function Typeahead(o, www) {
function c (line 2211) | function c(ctx) {
function attach (line 2237) | function attach() {
function ttEach (line 2390) | function ttEach($els, fn) {
function buildHintFromInput (line 2396) | function buildHintFromInput($input, www) {
function prepInput (line 2403) | function prepInput($input, www) {
function getBackgroundStyles (line 2419) | function getBackgroundStyles($el) {
function revert (line 2431) | function revert($input) {
function $elOrNull (line 2444) | function $elOrNull(obj) {
FILE: dist/typeahead.jquery.js
function reverseArgs (line 54) | function reverseArgs(index, value) {
function template (line 99) | function template() {
function build (line 170) | function build(o) {
function buildHtml (line 189) | function buildHtml(c) {
function buildSelectors (line 195) | function buildSelectors(classes) {
function buildCss (line 202) | function buildCss() {
function EventBus (line 259) | function EventBus(o) {
function on (line 298) | function on(method, types, cb, context) {
function onAsync (line 315) | function onAsync(types, cb, context) {
function onSync (line 318) | function onSync(types, cb, context) {
function off (line 321) | function off(types) {
function trigger (line 332) | function trigger(types) {
function getFlush (line 346) | function getFlush(callbacks, context, args) {
function getNextTick (line 356) | function getNextTick() {
function bindContext (line 373) | function bindContext(fn, context) {
function hightlightTextNode (line 398) | function hightlightTextNode(textNode) {
function traverse (line 410) | function traverse(el, hightlightTextNode) {
function getRegex (line 422) | function getRegex(patterns, caseSensitive, wordsOnly) {
function Input (line 443) | function Input(o, www) {
function buildOverflowHelper (line 622) | function buildOverflowHelper($input) {
function areQueriesEquivalent (line 639) | function areQueriesEquivalent(a, b) {
function withModifier (line 642) | function withModifier($e) {
function Dataset (line 654) | function Dataset(o, www) {
function sync (line 794) | function sync(suggestions) {
function async (line 806) | function async(suggestions) {
function getDisplayFn (line 830) | function getDisplayFn(display) {
function getTemplates (line 837) | function getTemplates(templates, displayFn) {
function isValidName (line 849) | function isValidName(str) {
function Menu (line 855) | function Menu(o, www) {
function isDatasetEmpty (line 888) | function isDatasetEmpty(dataset) {
function updateDataset (line 968) | function updateDataset(dataset) {
function clearDataset (line 976) | function clearDataset(dataset) {
function destroyDataset (line 984) | function destroyDataset(dataset) {
function DefaultMenu (line 994) | function DefaultMenu() {
function Typeahead (line 1037) | function Typeahead(o, www) {
function c (line 1298) | function c(ctx) {
function attach (line 1324) | function attach() {
function ttEach (line 1477) | function ttEach($els, fn) {
function buildHintFromInput (line 1483) | function buildHintFromInput($input, www) {
function prepInput (line 1490) | function prepInput($input, www) {
function getBackgroundStyles (line 1506) | function getBackgroundStyles($el) {
function revert (line 1518) | function revert($input) {
function $elOrNull (line 1531) | function $elOrNull(obj) {
FILE: src/bloodhound/bloodhound.js
function Bloodhound (line 17) | function Bloodhound(o) {
function withAsync (line 61) | function withAsync(query, sync, async) {
function withoutAsync (line 65) | function withoutAsync(query, sync) {
function done (line 92) | function done(err, data) {
function addLocalToIndex (line 112) | function addLocalToIndex() { that.add(that.local); }
function processRemote (line 152) | function processRemote(remote) {
FILE: src/bloodhound/lru_cache.js
function LruCache (line 12) | function LruCache(maxSize) {
function List (line 67) | function List() {
function Node (line 93) | function Node(key, val) {
FILE: src/bloodhound/options_parser.js
function parsePrefetch (line 41) | function parsePrefetch(o) {
function parseRemote (line 74) | function parseRemote(o) {
function toRemotePrepare (line 114) | function toRemotePrepare(o) {
function toLimiter (line 152) | function toLimiter(o) {
function callbackToDeferred (line 174) | function callbackToDeferred(fn) {
FILE: src/bloodhound/persistent_storage.js
function PersistentStorage (line 26) | function PersistentStorage(namespace, override) {
function now (line 123) | function now() {
function encode (line 127) | function encode(val) {
function decode (line 132) | function decode(val) {
function gatherMatchingKeys (line 136) | function gatherMatchingKeys(keyMatcher) {
FILE: src/bloodhound/prefetch.js
function Prefetch (line 18) | function Prefetch(o) {
function onError (line 80) | function onError() { cb(true); }
function onResponse (line 81) | function onResponse(resp) { cb(null, that.transform(resp)); }
FILE: src/bloodhound/remote.js
function Remote (line 13) | function Remote(o) {
function onResponse (line 47) | function onResponse(err, resp) {
FILE: src/bloodhound/search_index.js
function SearchIndex (line 15) | function SearchIndex(o) {
function normalizeTokens (line 133) | function normalizeTokens(tokens) {
function newNode (line 143) | function newNode() {
function unique (line 152) | function unique(array) {
function getIntersection (line 165) | function getIntersection(arrayA, arrayB) {
FILE: src/bloodhound/tokenizers.js
function whitespace (line 19) | function whitespace(str) {
function nonword (line 24) | function nonword(str) {
function getObjTokenizer (line 29) | function getObjTokenizer(tokenizer) {
FILE: src/bloodhound/transport.js
function Transport (line 18) | function Transport(o) {
function done (line 79) | function done(resp) {
function fail (line 84) | function fail() {
function always (line 88) | function always() {
FILE: src/common/utils.js
function reverseArgs (line 50) | function reverseArgs(index, value) { return cb(value, index); }
function template (line 99) | function template() { return String(obj); }
FILE: src/typeahead/dataset.js
function Dataset (line 22) | function Dataset(o, www) {
function sync (line 251) | function sync(suggestions) {
function async (line 265) | function async(suggestions) {
function getDisplayFn (line 304) | function getDisplayFn(display) {
function getTemplates (line 312) | function getTemplates(templates, displayFn) {
function isValidName (line 326) | function isValidName(str) {
FILE: src/typeahead/default_menu.js
function DefaultMenu (line 12) | function DefaultMenu() {
FILE: src/typeahead/event_bus.js
function EventBus (line 28) | function EventBus(o) {
FILE: src/typeahead/event_emitter.js
function on (line 21) | function on(method, types, cb, context) {
function onAsync (line 39) | function onAsync(types, cb, context) {
function onSync (line 43) | function onSync(types, cb, context) {
function off (line 47) | function off(types) {
function trigger (line 61) | function trigger(types) {
function getFlush (line 79) | function getFlush(callbacks, context, args) {
function getNextTick (line 94) | function getNextTick() {
function bindContext (line 114) | function bindContext(fn, context) {
FILE: src/typeahead/highlight.js
function hightlightTextNode (line 37) | function hightlightTextNode(textNode) {
function traverse (line 54) | function traverse(el, hightlightTextNode) {
function getRegex (line 71) | function getRegex(patterns, caseSensitive, wordsOnly) {
FILE: src/typeahead/input.js
function Input (line 25) | function Input(o, www) {
function buildOverflowHelper (line 309) | function buildOverflowHelper($input) {
function areQueriesEquivalent (line 332) | function areQueriesEquivalent(a, b) {
function withModifier (line 336) | function withModifier($e) {
FILE: src/typeahead/menu.js
function Menu (line 13) | function Menu(o, www) {
function isDatasetEmpty (line 68) | function isDatasetEmpty(dataset) { return dataset.isEmpty(); }
function updateDataset (line 192) | function updateDataset(dataset) { dataset.update(query); }
function clearDataset (line 201) | function clearDataset(dataset) { dataset.clear(); }
function destroyDataset (line 212) | function destroyDataset(dataset) { dataset.destroy(); }
FILE: src/typeahead/plugin.js
function attach (line 34) | function attach() {
function ttEach (line 207) | function ttEach($els, fn) {
function buildHintFromInput (line 215) | function buildHintFromInput($input, www) {
function prepInput (line 226) | function prepInput($input, www) {
function getBackgroundStyles (line 246) | function getBackgroundStyles($el) {
function revert (line 259) | function revert($input) {
function $elOrNull (line 283) | function $elOrNull(obj) {
FILE: src/typeahead/typeahead.js
function Typeahead (line 13) | function Typeahead(o, www) {
function c (line 427) | function c(ctx) {
FILE: src/typeahead/www.js
function build (line 26) | function build(o) {
function buildHtml (line 47) | function buildHtml(c) {
function buildSelectors (line 54) | function buildSelectors(classes) {
function buildCss (line 61) | function buildCss() {
FILE: test/bloodhound/bloodhound_spec.js
function build (line 3) | function build(o) {
function fakeFromNetwork (line 76) | function fakeFromNetwork(cb) { cb(null, fixtures.data.simple); }
function fakeFromNetwork (line 88) | function fakeFromNetwork(cb) { cb(null, fixtures.data.simple); }
function fakeFromNetwork (line 103) | function fakeFromNetwork(cb) { cb(null, []); }
function fakeGet (line 231) | function fakeGet(o, cb) { cb(fixtures.data.animals); }
function fakeGet (line 266) | function fakeGet(o, cb) { cb(fixtures.data.animals); }
function fakeGet (line 290) | function fakeGet(o, cb) { cb(fixtures.data.animals); }
function datumTokenizer (line 297) | function datumTokenizer(d) { return $.trim(d.value).split(/\s+/); }
function queryTokenizer (line 298) | function queryTokenizer(s) { return $.trim(s).split(/\s+/); }
FILE: test/bloodhound/options_parser_spec.js
function build (line 3) | function build(o) {
function prefetch (line 10) | function prefetch(o) {
function remote (line 20) | function remote(o) {
function parse (line 32) | function parse() { build({ datumTokenizer: null }); }
function parse (line 37) | function parse() { build({ queryTokenizer: null }); }
function parse (line 70) | function parse() { prefetch({ url: null }); }
function errTransport (line 125) | function errTransport(q, success, error) { error(); }
function successTransport (line 126) | function successTransport(q, success, error) { success(); }
function parse (line 133) | function parse() { remote({ url: null }); }
function errTransport (line 190) | function errTransport(q, success, error) { error(); }
function successTransport (line 191) | function successTransport(q, success, error) { success(); }
FILE: test/bloodhound/persistent_storage_spec.js
method length (line 15) | get length() { return localStorage.length; }
function spyThrough (line 187) | function spyThrough(method) {
FILE: test/bloodhound/prefetch_spec.js
function build (line 3) | function build(o) {
function fakeStorageGet (line 163) | function fakeStorageGet(data, thumbprint, protocol) {
FILE: test/bloodhound/search_index_spec.js
function build (line 3) | function build(o) {
FILE: test/bloodhound/transport_spec.js
function drop (line 18) | function drop(i, req) {
function limiter (line 53) | function limiter(fn) { return _.debounce(fn, 20); }
function limiter (line 149) | function limiter(fn) { return _.debounce(fn, 20); }
function limiter (line 173) | function limiter(fn) { return _.debounce(fn, 20); }
FILE: test/helpers/typeahead_mocks.js
function makeMockable (line 20) | function makeMockable(component) {
function mock (line 38) | function mock(Constructor) {
FILE: test/integration/test.js
function setIf (line 25) | function setIf(obj, key, val) {
FILE: test/typeahead/dataset_spec.js
function noSource (line 27) | function noSource() { new Dataset({}, www); }
function fn (line 33) | function fn() {
function syncEmptySuggestions (line 435) | function syncEmptySuggestions(q, sync, async) {
function syncMockSuggestions (line 439) | function syncMockSuggestions(q, sync, async) {
function syncMockSuggestionsDisplayFn (line 443) | function syncMockSuggestionsDisplayFn(q, sync, async) {
function asyncSync (line 447) | function asyncSync(q, sync, async) {
function multipleSync (line 451) | function multipleSync(q, sync, async) {
function fakeGetWithAsyncSuggestions (line 456) | function fakeGetWithAsyncSuggestions(query, sync, async) {
FILE: test/typeahead/event_bus_spec.js
function prevent (line 40) | function prevent($e) { $e.preventDefault(); }
FILE: test/typeahead/event_emitter_spec.js
function setCbContext (line 22) | function setCbContext() { cbContext = this; }
function assertCbContext (line 23) | function assertCbContext() { return cbContext === context; }
function cancel (line 95) | function cancel() { return false; }
function assertCallCount (line 98) | function assertCallCount(spy, expected) {
function assertArgs (line 102) | function assertArgs(spy, call, expected) {
FILE: test/typeahead/highlight_spec.js
function buildTestNode (line 111) | function buildTestNode(content) {
FILE: test/typeahead/input_spec.js
function noInput (line 32) | function noInput() { new Input({}, www); }
function simulateInputEvent (line 496) | function simulateInputEvent($node) {
function simulateKeyEvent (line 505) | function simulateKeyEvent($node, type, key, withModifier) {
function setCursorPosition (line 522) | function setCursorPosition($input, pos) {
FILE: test/typeahead/results_spec.js
function noNode (line 21) | function noNode() { new Menu({ datasets: [{}] }, www); }
FILE: test/typeahead/typeahead_spec.js
function fake (line 1382) | function fake($el) {
function prevent (line 1410) | function prevent($e) { $e.preventDefault(); }
Condensed preview — 66 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (525K chars).
[
{
"path": ".gitignore",
"chars": 125,
"preview": "*.swp\n.DS_Store\n\n.grunt\n_SpecRunner.html\ntest/coverage\n\ndist_temp\n\nnode_modules\nnpm-debug.log\n\nbower_components\n\n*.iml\n."
},
{
"path": ".jshintrc",
"chars": 229,
"preview": "{\n \"curly\": true,\n \"newcap\": true,\n \"noarg\": true,\n \"quotmark\": \"single\",\n \"regexp\": true,\n \"trailing\": true,\n\n \""
},
{
"path": ".travis.yml",
"chars": 1201,
"preview": "language: node_js\nenv: \n matrix: \n - TEST_SUITE=unit\n - TEST_SUITE=integration BROWSER='firefox'\n - TEST_SUITE=integ"
},
{
"path": "CHANGELOG.md",
"chars": 11555,
"preview": "Changelog\n=========\n\nFor transparency and insight into our release cycle, releases will be numbered \nwith the follow for"
},
{
"path": "CONTRIBUTING.md",
"chars": 4083,
"preview": "Contributing to typeahead.js\n============================\n\n*These contributing guidelines were proudly stolen from the \n"
},
{
"path": "Gruntfile.js",
"chars": 8598,
"preview": "var semver = require('semver'),\n f = require('util').format,\n files = {\n common: [\n 'src/common/utils.js"
},
{
"path": "LICENSE",
"chars": 1061,
"preview": "Copyright (c) 2013-2014 Twitter, Inc\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof th"
},
{
"path": "README.md",
"chars": 5905,
"preview": "[](http://travis-ci.org/twitter/type"
},
{
"path": "bower.json",
"chars": 247,
"preview": "{\n \"name\": \"typeahead.js\",\n \"version\": \"0.11.1\",\n \"main\": \"dist/typeahead.bundle.js\",\n \"dependencies\": {\n \"jquery"
},
{
"path": "composer.json",
"chars": 491,
"preview": "{\n \"name\": \"twitter/typeahead.js\",\n \"description\": \"fast and fully-featured autocomplete library\",\n \"keywords\":"
},
{
"path": "dist/bloodhound.js",
"chars": 33197,
"preview": "/*!\n * typeahead.js 0.11.1\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2015 Twitter, Inc. and other con"
},
{
"path": "dist/typeahead.bundle.js",
"chars": 96186,
"preview": "/*!\n * typeahead.js 0.11.1\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2015 Twitter, Inc. and other con"
},
{
"path": "dist/typeahead.jquery.js",
"chars": 63136,
"preview": "/*!\n * typeahead.js 0.11.1\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2015 Twitter, Inc. and other con"
},
{
"path": "doc/bloodhound.md",
"chars": 9701,
"preview": "Bloodhound\n==========\n\nBloodhound is the typeahead.js suggestion engine. Bloodhound is robust, \nflexible, and offers adv"
},
{
"path": "doc/jquery_typeahead.md",
"chars": 10231,
"preview": "jQuery#typeahead\n----------------\n\nThe UI component of typeahead.js is available as a jQuery plugin. It's \nresponsible f"
},
{
"path": "doc/migration/0.10.0.md",
"chars": 7670,
"preview": "Migrating to typeahead.js v0.10.0\n=================================\n\nPreamble\n--------\n\nv0.10.0 of typeahead.js ended up"
},
{
"path": "karma.conf.js",
"chars": 1340,
"preview": "module.exports = function(config) {\n config.set({\n basePath: '',\n\n preprocessors: {\n 'src/**/*.js': 'coverag"
},
{
"path": "package.json",
"chars": 1801,
"preview": "{\n \"name\": \"typeahead.js\",\n \"description\": \"fast and fully-featured autocomplete library\",\n \"keywords\": [\n \"typeah"
},
{
"path": "src/bloodhound/bloodhound.js",
"chars": 4530,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/lru_cache.js",
"chars": 2026,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/options_parser.js",
"chars": 4559,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/persistent_storage.js",
"chars": 3193,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/prefetch.js",
"chars": 2261,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/remote.js",
"chars": 1141,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/search_index.js",
"chars": 4028,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/tokenizers.js",
"chars": 915,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/transport.js",
"chars": 3030,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/bloodhound/version.js",
"chars": 168,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/common/utils.js",
"chars": 3775,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/dataset.js",
"chars": 8819,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/default_menu.js",
"chars": 1752,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/event_bus.js",
"chars": 1562,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/event_emitter.js",
"chars": 2626,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/highlight.js",
"chars": 2170,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/input.js",
"chars": 8612,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/menu.js",
"chars": 5875,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/plugin.js",
"chars": 7547,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/typeahead.js",
"chars": 12067,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "src/typeahead/www.js",
"chars": 2469,
"preview": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributor"
},
{
"path": "test/bloodhound/bloodhound_spec.js",
"chars": 8862,
"preview": "describe('Bloodhound', function() {\n\n function build(o) {\n return new Bloodhound(_.mixin({\n datumTokenizer: dat"
},
{
"path": "test/bloodhound/lru_cache_spec.js",
"chars": 1220,
"preview": "describe('LruCache', function() {\n\n beforeEach(function() {\n this.cache = new LruCache(3);\n });\n\n it('should make "
},
{
"path": "test/bloodhound/options_parser_spec.js",
"chars": 5685,
"preview": "describe('options parser', function() {\n\n function build(o) {\n return oParser(_.mixin({\n datumTokenizer: $.noop"
},
{
"path": "test/bloodhound/persistent_storage_spec.js",
"chars": 5485,
"preview": "describe('PersistentStorage', function() {\n var engine, ls;\n\n // test suite is dependent on localStorage being availab"
},
{
"path": "test/bloodhound/prefetch_spec.js",
"chars": 4837,
"preview": "describe('Prefetch', function() {\n\n function build(o) {\n return new Prefetch(_.mixin({\n url: '/prefetch',\n "
},
{
"path": "test/bloodhound/remote_spec.js",
"chars": 1853,
"preview": "describe('Remote', function() {\n\n beforeEach(function() {\n jasmine.Transport.useMock();\n\n this.remote = new Remot"
},
{
"path": "test/bloodhound/search_index_spec.js",
"chars": 2055,
"preview": "describe('SearchIndex', function() {\n\n function build(o) {\n return new SearchIndex(_.mixin({\n datumTokenizer: B"
},
{
"path": "test/bloodhound/tokenizers_spec.js",
"chars": 2390,
"preview": "describe('tokenizers', function() {\n\n it('.whitespace should tokenize on whitespace', function() {\n var tokens = tok"
},
{
"path": "test/bloodhound/transport_spec.js",
"chars": 5076,
"preview": "describe('Transport', function() {\n\n beforeEach(function() {\n jasmine.Ajax.useMock();\n jasmine.Clock.useMock();\n\n"
},
{
"path": "test/ci",
"chars": 428,
"preview": "#!/bin/bash -x\n\nif [ \"$TEST_SUITE\" == \"unit\" ]; then\n ./node_modules/karma/bin/karma start --single-run --browsers Phan"
},
{
"path": "test/fixtures/ajax_responses.js",
"chars": 474,
"preview": "var fixtures = fixtures || {};\n\nfixtures.ajaxResps = {\n ok: {\n status: 200,\n responseText: '[{ \"value\": \"big\" }, "
},
{
"path": "test/fixtures/data.js",
"chars": 5634,
"preview": "var fixtures = fixtures || {};\n\nfixtures.data = {\n simple: [\n { value: 'big' },\n { value: 'bigger' },\n { value"
},
{
"path": "test/fixtures/html.js",
"chars": 475,
"preview": "var fixtures = fixtures || {};\n\nfixtures.html = {\n input: '<input class=\"tt-input\" type=\"text\" autocomplete=\"false\" spe"
},
{
"path": "test/helpers/typeahead_mocks.js",
"chars": 1826,
"preview": "(function(root) {\n var components;\n\n components = [\n 'Bloodhound',\n 'Prefetch',\n 'Remote',\n 'PersistentSto"
},
{
"path": "test/integration/test.html",
"chars": 2798,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title></title>\n <script src=\"../../bower_components/jquery/jquery.js\"></script>\n"
},
{
"path": "test/integration/test.js",
"chars": 11866,
"preview": "/* jshint esnext: true, evil: true, sub: true */\n\nvar wd = require('yiewd'),\n colors = require('colors'),\n expect "
},
{
"path": "test/playground.html",
"chars": 8345,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <script src=\"../bower_components/jquery/jquery.js\"></script>\n <script src=\"../dis"
},
{
"path": "test/typeahead/dataset_spec.js",
"chars": 12868,
"preview": "describe('Dataset', function() {\n var www = WWW(), mockSuggestions, mockSuggestionsDisplayFn;\n\n mockSuggestions = [\n "
},
{
"path": "test/typeahead/default_results_spec.js",
"chars": 2623,
"preview": "describe('DefaultMenu', function() {\n var www = WWW();\n\n beforeEach(function() {\n var $fixture;\n\n jasmine.Datase"
},
{
"path": "test/typeahead/event_bus_spec.js",
"chars": 1046,
"preview": "describe('EventBus', function() {\n\n beforeEach(function() {\n var $fixture;\n\n setFixtures(fixtures.html.input);\n\n "
},
{
"path": "test/typeahead/event_emitter_spec.js",
"chars": 3307,
"preview": "describe('EventEmitter', function() {\n\n beforeEach(function() {\n this.spy = jasmine.createSpy();\n this.target = _"
},
{
"path": "test/typeahead/highlight_spec.js",
"chars": 3683,
"preview": "describe('highlight', function() {\n it('should allow tagName to be specified', function() {\n var before = 'abcde',\n "
},
{
"path": "test/typeahead/input_spec.js",
"chars": 15068,
"preview": "describe('Input', function() {\n var KEYS, www;\n\n KEYS = {\n enter: 13,\n esc: 27,\n tab: 9,\n left: 37,\n r"
},
{
"path": "test/typeahead/plugin_spec.js",
"chars": 6086,
"preview": "describe('$plugin', function() {\n\n beforeEach(function() {\n var $fixture;\n\n setFixtures('<input class=\"test-input"
},
{
"path": "test/typeahead/results_spec.js",
"chars": 9337,
"preview": "describe('Menu', function() {\n var www = WWW();\n\n beforeEach(function() {\n var $fixture;\n\n jasmine.Dataset.useMo"
},
{
"path": "test/typeahead/typeahead_spec.js",
"chars": 38305,
"preview": "describe('Typeahead', function() {\n var www, testData;\n\n www = WWW();\n\n beforeEach(function() {\n var $fixture, $in"
},
{
"path": "typeahead.js.jquery.json",
"chars": 967,
"preview": "{\n \"licenses\": [\n {\n \"url\": \"https://github.com/twitter/typeahead.js/blob/master/LICENSE\"\n }\n ],\n \"depende"
}
]
About this extraction
This page contains the full source code of the twitter/typeahead.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 66 files (490.7 KB), approximately 113.0k tokens, and a symbol index with 295 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.