[
  {
    "path": ".gitignore",
    "content": "*.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.idea\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n  \"curly\": true,\n  \"newcap\": true,\n  \"noarg\": true,\n  \"quotmark\": \"single\",\n  \"regexp\": true,\n  \"trailing\": true,\n\n  \"boss\": true,\n  \"eqnull\": true,\n  \"expr\": true,\n  \"validthis\": true,\n  \n  \"browser\": true,\n  \"jquery\": true\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nenv: \n  matrix: \n  - TEST_SUITE=unit\n  - TEST_SUITE=integration BROWSER='firefox'\n  - TEST_SUITE=integration BROWSER='firefox:3.5'\n  - TEST_SUITE=integration BROWSER='firefox:3.6'\n  - TEST_SUITE=integration BROWSER='safari:5'\n  - TEST_SUITE=integration BROWSER='safari:6'\n  - TEST_SUITE=integration BROWSER='safari:7'\n  - TEST_SUITE=integration BROWSER='internet explorer:8'\n  - TEST_SUITE=integration BROWSER='internet explorer:9'\n  - TEST_SUITE=integration BROWSER='internet explorer:10'\n  - TEST_SUITE=integration BROWSER='internet explorer:11'\n  - TEST_SUITE=integration BROWSER='chrome'\n  global: \n  - secure: VY4J2ERfrMEin++f4+UDDtTMWLuE3jaYAVchRxfO2c6PQUYgR+SW4SMekz855U/BuptMtiVMR2UUoNGMgOSKIFkIXpPfHhx47G5a541v0WNjXfQ2qzivXAWaXNK3l3C58z4dKxgPWsFY9JtMVCddJd2vQieAILto8D8G09p7bpo=\n  - secure: kehbNCoYUG2gLnhmCH/oKhlJG6LoxgcOPMCtY7KOI4ropG8qlypb+O2b/19+BWeO3aIuMB0JajNh3p2NL0UKgLmUK7EYBA9fQz+vesFReRk0V/KqMTSxHJuseM4aLOWA2Wr9US843VGltfODVvDN5sNrfY7RcoRx2cTK/k1CXa8=\nnode_js: \n- 0.11.13\nbefore_script: \n- npm install -g grunt-cli@0.1.13\n- npm install -g node-static@0.7.3\n- npm install -g bower@1.3.8\n- bower install\n- grunt build\nscript: test/ci\naddons:\n  sauce_connect: true\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "Changelog\n=========\n\nFor transparency and insight into our release cycle, releases will be numbered \nwith the follow format:\n\n`<major>.<minor>.<patch>`\n\nAnd constructed with the following guidelines:\n\n* Breaking backwards compatibility bumps the major\n* New additions without breaking backwards compatibility bumps the minor\n* Bug fixes and misc changes bump the patch\n\nFor more information on semantic versioning, please visit http://semver.org/.\n\n---\n\n### 0.11.1 April 26, 2015\n\n* Add prepare option to prefetch. [#1181]\n* Handle QuotaExceededError. [#1110]\n* Escape HTML entities from suggestion display value when rendering with default\n  template. [#964]\n* List jquery as a dependency in package.json. [#1143]\n\n### 0.11.0 April 25, 2015\n\nAn overhaul of typeahead.js – consider this a release candidate for v1. There\nare bunch of API changes with this release so don't expect backwards \ncompatibility with previous versions. There are also many new undocumented \nfeatures that have been introduced. Documentation for those features will be \nadded before v1 ships.\n\nBeware that since this release is pretty much a rewrite, there are bound to be\nsome bugs. To be safe, you should consider this release beta software and \nthroughly test your integration of it before using it in production \nenvironments. This caveat only applies to this release as subsequent releases\nwill address any issues that come up.\n\n### 0.10.5 August 7, 2014\n\n* Increase supported version range for jQuery dependency. [#917]\n\n### 0.10.4 July 13, 2014\n\n**Hotfix**\n\n* Fix regression that breaks Bloodhound instances when more than 1 instance is\n  relying on remote data. [#899]\n\n### 0.10.3 July 10, 2014\n\n**Bug fixes**\n\n* `Bloodhound#clearPrefetchCache` now works with cache keys that contain regex \n  characters. [#771]\n* Prevent outdated network requests from being sent. [#809]\n* Add support to object tokenizers for multiple property tokenization. [#811]\n* Fix broken `jQuery#typeahead('val')` method. [#815]\n* Remove `disabled` attribute from the hint input control. [#839]\n* Add `tt-highlight` class to highlighted text. [#833]\n* Handle non-string types that are passed to `jQuery#typeahead('val', val)`. [#881]\n\n### 0.10.2 March 10, 2014\n\n* Prevent flickering of dropdown menu when requesting remote suggestions. [#718]\n* Reduce hint flickering. [#754]\n* Added `Bloodhound#{clear, clearPrefetchCache, clearRemoteCache}` and made it\n  possible to reinitialize Bloodhound instances. [#703]\n* Invoke `local` function during initialization. [#687]\n* In addition to HTML strings, templates can now return DOM nodes. [#742]\n* Prevent `jQuery#typeahead('val', val)` from opening dropdown menus of \n  non-active typeaheads. [#646]\n* Fix bug in IE that resulted in dropdown menus with overflow being closed\n  when clicking on the scrollbar. [#705]\n* Only show dropdown menu if `minLength` is satisfied. [#710]\n\n### 0.10.1 February 9, 2014\n\n**Hotfix**\n\n* Fixed bug that prevented some ajax configs from being respected. [#630]\n* Event delegation on suggestion clicks is no longer broken. [#118]\n* Ensure dataset names are valid class name suffixes. [#610]\n* Added support for `displayKey` to be a function. [#633]\n* `jQuery#typeahead('val')` now mirrors `jQuery#val()`. [#659]\n* Datasets can now be passed to jQuery plugin as an array. [#664]\n* Added a `noConflict` method to the jQuery plugin. [#612]\n* Bloodhound's `local` property can now be a function. [#485]\n\n### 0.10.0 February 2, 2014\n\n**Introducting Bloodhound**\n\nThis release was almost a complete rewrite of typeahead.js and will hopefully\nlay the foundation for the 1.0.0 release. It's impossible to enumerate all of \nthe issues that were fixed. If you want to get an idea of what issues 0.10.0 \nresolved, take a look at the closed issues in the [0.10.0 milestone].\n\nThe most important change in 0.10.0 is that typeahead.js was broken up into 2 \nindividual components: Bloodhound and jQuery#typeahead. Bloodhound is an \nfeature-rich suggestion engine. jQuery#typeahead is a jQuery plugin that turns\ninput controls into typeaheads.\n\nIt's impossible to write a typeahead library that supports every use-case out \nof the box – that was the main motivation behind decomposing typeahead.js. \nPreviously, some prospective typeahead.js users were unable to use the library \nbecause either the suggestion engine or the typeahead UI did not meet their\nrequirements. In those cases, they were either forced to fork typeahead.js and\nmake the necessary modifications or they had to give up on using typeahead.js\nentirely. Now they have the option of swapping out the component that doesn't \nwork for them with a custom implementation.\n\n### 0.9.3 June 24, 2013\n\n* Ensure cursor visibility in menus with overflow. [#209]\n* Fixed bug that led to the menu staying open when it should have been closed. [#260]\n* Private browsing in Safari no longer breaks prefetch. [#270]\n* Pressing tab while a suggestion is highlighted now results in a selection. [#266]\n* Dataset name is now passed as an argument for typeahead:selected event. [#207]\n\n### 0.9.2 April 14, 2013\n\n* Prefetch usage no longer breaks when cookies are disabled. [#190]\n* Precompiled templates are now wrapped in the appropriate DOM element. [#172]\n\n### 0.9.1 April 1, 2013\n\n* Multiple requests no longer get sent for a query when datasets share a remote source. [#152]\n* Datasets now support precompiled templates. [#137]\n* Cached remote suggestions now get rendered immediately. [#156]\n* Added typeahead:autocompleted event. [#132]\n* Added a plugin method for programmatically setting the query. Experimental. [#159]\n* Added minLength option for datasets. Experimental. [#131]\n* Prefetch objects now support thumbprint option. Experimental. [#157]\n\n### 0.9.0 March 24, 2013\n\n**Custom events, no more typeahead.css, and an improved API**\n\n* Implemented the triggering of custom events. [#106]\n* Got rid of typeahead.css and now apply styling through JavaScript. [#15]\n* Made the API more flexible and addressed a handful of remote issues by rewriting the transport component. [#25]\n* Added support for dataset headers and footers. [#81]\n* No longer cache unnamed datasets. [#116]\n* Made the key name of the value property configurable. [#115]\n* Input values set before initialization of typeaheads are now respected. [#109]\n* Fixed an input value/hint casing bug. [#108]\n\n### 0.8.2 March 04, 2013\n\n* Fixed bug causing error to be thrown when initializing a typeahead on multiple elements. [#51]\n* Tokens with falsy values are now filtered out – was causing wonky behavior. [#75]\n* No longer making remote requests for blank queries. [#74]\n* Datums with regex characters in their value no longer cause errors. [#77]\n* Now compatible with the Closure Compiler. [#48]\n* Reference to jQuery is now obtained through window.jQuery, not window.$. [#47]\n* Added a plugin method for destroying typeaheads. Won't be documented until v0.9 and might change before then. [#59]\n\n### 0.8.1 February 25, 2013\n\n* Fixed bug preventing local and prefetch from being used together. [#39]\n* No longer prevent default browser behavior when up or down arrow is pressed with a modifier. [#6]\n* Hint is hidden when user entered query is wider than the input. [#26]\n* Data stored in localStorage now expires properly. [#34]\n* Normalized search tokens and fixed query tokenization. [#38]\n* Remote suggestions now are appended, not prepended to suggestions list. [#40]\n* Fixed some typos through the codebase. [#3]\n\n### 0.8.0 February 19, 2013\n\n**Initial public release**\n\n* Prefetch and search data locally insanely fast.\n* Search hard-coded, prefetched, and/or remote data.\n* Hinting.\n* RTL/IME/international support.\n* Search multiple datasets.\n* Share datasets (and caching) between multiple inputs.\n* And much, much more...\n\n[0.10.0 milestone]: https://github.com/twitter/typeahead.js/issues?milestone=8&page=1&state=closed\n\n[#1181]: https://github.com/twitter/typeahead.js/pull/1181\n[#1143]: https://github.com/twitter/typeahead.js/pull/1143\n[#1110]: https://github.com/twitter/typeahead.js/pull/1110\n[#964]: https://github.com/twitter/typeahead.js/pull/964\n[#917]: https://github.com/twitter/typeahead.js/pull/917\n[#899]: https://github.com/twitter/typeahead.js/pull/899\n[#881]: https://github.com/twitter/typeahead.js/pull/881\n[#839]: https://github.com/twitter/typeahead.js/pull/839\n[#833]: https://github.com/twitter/typeahead.js/pull/833\n[#815]: https://github.com/twitter/typeahead.js/pull/815\n[#811]: https://github.com/twitter/typeahead.js/pull/811\n[#809]: https://github.com/twitter/typeahead.js/pull/809\n[#771]: https://github.com/twitter/typeahead.js/pull/771\n[#754]: https://github.com/twitter/typeahead.js/pull/754\n[#742]: https://github.com/twitter/typeahead.js/pull/742\n[#718]: https://github.com/twitter/typeahead.js/pull/718\n[#710]: https://github.com/twitter/typeahead.js/pull/710\n[#705]: https://github.com/twitter/typeahead.js/pull/705\n[#703]: https://github.com/twitter/typeahead.js/pull/703\n[#687]: https://github.com/twitter/typeahead.js/pull/687\n[#664]: https://github.com/twitter/typeahead.js/pull/664\n[#659]: https://github.com/twitter/typeahead.js/pull/659\n[#646]: https://github.com/twitter/typeahead.js/pull/646\n[#633]: https://github.com/twitter/typeahead.js/pull/633\n[#630]: https://github.com/twitter/typeahead.js/pull/630\n[#612]: https://github.com/twitter/typeahead.js/pull/612\n[#610]: https://github.com/twitter/typeahead.js/pull/610\n[#485]: https://github.com/twitter/typeahead.js/pull/485\n[#270]: https://github.com/twitter/typeahead.js/pull/270\n[#266]: https://github.com/twitter/typeahead.js/pull/266\n[#260]: https://github.com/twitter/typeahead.js/pull/260\n[#209]: https://github.com/twitter/typeahead.js/pull/209\n[#207]: https://github.com/twitter/typeahead.js/pull/207\n[#190]: https://github.com/twitter/typeahead.js/pull/190\n[#172]: https://github.com/twitter/typeahead.js/pull/172\n[#159]: https://github.com/twitter/typeahead.js/pull/159\n[#157]: https://github.com/twitter/typeahead.js/pull/157\n[#156]: https://github.com/twitter/typeahead.js/pull/156\n[#152]: https://github.com/twitter/typeahead.js/pull/152\n[#137]: https://github.com/twitter/typeahead.js/pull/137\n[#132]: https://github.com/twitter/typeahead.js/pull/132\n[#131]: https://github.com/twitter/typeahead.js/pull/131\n[#118]: https://github.com/twitter/typeahead.js/pull/118\n[#116]: https://github.com/twitter/typeahead.js/pull/116\n[#115]: https://github.com/twitter/typeahead.js/pull/115\n[#109]: https://github.com/twitter/typeahead.js/pull/109\n[#108]: https://github.com/twitter/typeahead.js/pull/108\n[#106]: https://github.com/twitter/typeahead.js/pull/106\n[#81]: https://github.com/twitter/typeahead.js/pull/81\n[#77]: https://github.com/twitter/typeahead.js/pull/77\n[#75]: https://github.com/twitter/typeahead.js/pull/75\n[#74]: https://github.com/twitter/typeahead.js/pull/74\n[#59]: https://github.com/twitter/typeahead.js/pull/59\n[#51]: https://github.com/twitter/typeahead.js/pull/51\n[#48]: https://github.com/twitter/typeahead.js/pull/48\n[#47]: https://github.com/twitter/typeahead.js/pull/47\n[#40]: https://github.com/twitter/typeahead.js/pull/40\n[#39]: https://github.com/twitter/typeahead.js/pull/39\n[#38]: https://github.com/twitter/typeahead.js/pull/38\n[#34]: https://github.com/twitter/typeahead.js/pull/34\n[#26]: https://github.com/twitter/typeahead.js/pull/26\n[#25]: https://github.com/twitter/typeahead.js/pull/25\n[#15]: https://github.com/twitter/typeahead.js/pull/15\n[#6]: https://github.com/twitter/typeahead.js/pull/6\n[#3]: https://github.com/twitter/typeahead.js/pull/3\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing to typeahead.js\n============================\n\n*These contributing guidelines were proudly stolen from the \n[Flight](https://github.com/flightjs/flight) project*\n\nLooking to contribute something to typeahead.js? Here's how you can help.\n\nBugs Reports\n------------\n\nA bug is a _demonstrable problem_ that is caused by the code in the\nrepository. Good bug reports are extremely helpful – thank you!\n\nGuidelines for bug reports:\n\n1. **Use the GitHub issue search** &mdash; check if the issue has already been\n   reported.\n\n2. **Check if the issue has been fixed** &mdash; try to reproduce it using the\n   latest `master` or integration branch in the repository.\n\n3. **Isolate the problem** &mdash; ideally create a reduced test\n   case and a live example.\n\n4. Please try to be as detailed as possible in your report. Include specific\n   information about the environment – operating system and version, browser\n   and version, version of typeahead.js – and steps required to reproduce the \n  issue.\n\nFeature Requests & Contribution Enquiries\n-----------------------------------------\n\nFeature requests are welcome. But take a moment to find out whether your idea\nfits with the scope and aims of the project. It's up to *you* to make a strong\ncase for the inclusion of your feature. Please provide as much detail and\ncontext as possible.\n\nContribution enquiries should take place before any significant pull request,\notherwise you risk spending a lot of time working on something that we might\nhave good reasons for rejecting.\n\nPull Requests\n-------------\n\nGood pull requests – patches, improvements, new features – are a fantastic\nhelp. They should remain focused in scope and avoid containing unrelated\ncommits.\n\nMake sure to adhere to the coding conventions used throughout the codebase\n(indentation, accurate comments, etc.) and any other requirements (such as test\ncoverage).\n\nPlease follow this process; it's the best way to get your work included in the\nproject:\n\n1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,\n   and configure the remotes:\n\n   ```bash\n   # Clone your fork of the repo into the current directory\n   git clone https://github.com/<your-username>/typeahead.js\n   # Navigate to the newly cloned directory\n   cd <repo-name>\n   # Assign the original repo to a remote called \"upstream\"\n   git remote add upstream git://github.com/twitter/typeahead.js\n   ```\n\n2. If you cloned a while ago, get the latest changes from upstream:\n\n   ```bash\n   git checkout master\n   git pull upstream master\n   ```\n\n3. Install the dependencies (you must have Node.js and [Bower](http://bower.io)\n   installed), and create a new topic branch (off the main project development\n   branch) to contain your feature, change, or fix:\n\n   ```bash\n   npm install\n   bower install\n   git checkout -b <topic-branch-name>\n   ```\n\n4. Make sure to update, or add to the tests when appropriate. Patches and\n   features will not be accepted without tests. Run `npm test` to check that\n   all tests pass after you've made changes.\n\n5. Commit your changes in logical chunks. Provide clear and explanatory commit\n   messages. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up\n   your commits before making them public.\n\n6. Locally merge (or rebase) the upstream development branch into your topic branch:\n\n   ```bash\n   git pull [--rebase] upstream master\n   ```\n\n7. Push your topic branch up to your fork:\n\n   ```bash\n   git push origin <topic-branch-name>\n   ```\n\n8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)\n    with a clear title and description.\n\n9. If you are asked to amend your changes before they can be merged in, please\n   use `git commit --amend` (or rebasing for multi-commit Pull Requests) and\n   force push to your remote feature branch. You may also be asked to squash\n   commits.\n\nLicense\n-------\n\nBy contributing your code,\n\nYou agree to license your contribution under the terms of the MIT License\nhttps://github.com/twitter/typeahead.js/blob/master/LICENSE\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "var semver = require('semver'),\n    f = require('util').format,\n    files = {\n      common: [\n      'src/common/utils.js'\n      ],\n      bloodhound: [\n      'src/bloodhound/version.js',\n      'src/bloodhound/tokenizers.js',\n      'src/bloodhound/lru_cache.js',\n      'src/bloodhound/persistent_storage.js',\n      'src/bloodhound/transport.js',\n      'src/bloodhound/search_index.js',\n      'src/bloodhound/prefetch.js',\n      'src/bloodhound/remote.js',\n      'src/bloodhound/options_parser.js',\n      'src/bloodhound/bloodhound.js'\n      ],\n      typeahead: [\n      'src/typeahead/www.js',\n      'src/typeahead/event_bus.js',\n      'src/typeahead/event_emitter.js',\n      'src/typeahead/highlight.js',\n      'src/typeahead/input.js',\n      'src/typeahead/dataset.js',\n      'src/typeahead/menu.js',\n      'src/typeahead/default_menu.js',\n      'src/typeahead/typeahead.js',\n      'src/typeahead/plugin.js'\n      ]\n    };\n\nmodule.exports = function(grunt) {\n  grunt.initConfig({\n    version: grunt.file.readJSON('package.json').version,\n\n    tempDir: 'dist_temp',\n    buildDir: 'dist',\n\n    banner: [\n      '/*!',\n      ' * typeahead.js <%= version %>',\n      ' * https://github.com/twitter/typeahead.js',\n      ' * Copyright 2013-<%= grunt.template.today(\"yyyy\") %> Twitter, Inc. and other contributors; Licensed MIT',\n      ' */\\n\\n'\n    ].join('\\n'),\n\n    uglify: {\n      options: {\n        banner: '<%= banner %>'\n      },\n\n      concatBloodhound: {\n        options: {\n          mangle: false,\n          beautify: true,\n          compress: false,\n          banner: ''\n        },\n        src: files.common.concat(files.bloodhound),\n        dest: '<%= tempDir %>/bloodhound.js'\n      },\n      concatTypeahead: {\n        options: {\n          mangle: false,\n          beautify: true,\n          compress: false,\n          banner: ''\n        },\n        src: files.common.concat(files.typeahead),\n        dest: '<%= tempDir %>/typeahead.jquery.js'\n      },\n\n      bloodhound: {\n        options: {\n          mangle: false,\n          beautify: true,\n          compress: false\n        },\n        src: '<%= tempDir %>/bloodhound.js',\n        dest: '<%= buildDir %>/bloodhound.js'\n      },\n      bloodhoundMin: {\n        options: {\n          mangle: true,\n          compress: {}\n        },\n        src: '<%= tempDir %>/bloodhound.js',\n        dest: '<%= buildDir %>/bloodhound.min.js'\n      },\n      typeahead: {\n        options: {\n          mangle: false,\n          beautify: true,\n          compress: false\n        },\n        src: '<%= tempDir %>/typeahead.jquery.js',\n        dest: '<%= buildDir %>/typeahead.jquery.js'\n      },\n      typeaheadMin: {\n        options: {\n          mangle: true,\n          compress: {}\n        },\n        src: '<%= tempDir %>/typeahead.jquery.js',\n        dest: '<%= buildDir %>/typeahead.jquery.min.js'\n      },\n      bundle: {\n        options: {\n          mangle: false,\n          beautify: true,\n          compress: false\n        },\n        src: [\n          '<%= tempDir %>/bloodhound.js',\n          '<%= tempDir %>/typeahead.jquery.js'\n        ],\n        dest: '<%= buildDir %>/typeahead.bundle.js'\n\n      },\n      bundleMin: {\n        options: {\n          mangle: true,\n          compress: {}\n        },\n        src: [\n          '<%= tempDir %>/bloodhound.js',\n          '<%= tempDir %>/typeahead.jquery.js'\n        ],\n        dest: '<%= buildDir %>/typeahead.bundle.min.js'\n      }\n    },\n\n    umd: {\n      bloodhound: {\n        src: '<%= tempDir %>/bloodhound.js',\n        objectToExport: 'Bloodhound',\n        amdModuleId: 'bloodhound',\n        deps: {\n          default: ['$'],\n          amd: ['jquery'],\n          cjs: ['jquery'],\n          global: ['jQuery']\n        }\n      },\n      typeahead: {\n        src: '<%= tempDir %>/typeahead.jquery.js',\n        amdModuleId: 'typeahead.js',\n        deps: {\n          default: ['$'],\n          amd: ['jquery'],\n          cjs: ['jquery'],\n          global: ['jQuery']\n        }\n      }\n    },\n\n    sed: {\n      version: {\n        pattern: '%VERSION%',\n        replacement: '<%= version %>',\n        recursive: true,\n        path: '<%= buildDir %>'\n      }\n    },\n\n    jshint: {\n      options: {\n        jshintrc: '.jshintrc'\n      },\n      src: 'src/**/*.js',\n      test: ['test/**/*_spec.js', 'test/integration/test.js'],\n      gruntfile: ['Gruntfile.js']\n    },\n\n    watch: {\n      js: {\n        files: 'src/**/*',\n        tasks: 'build'\n      }\n    },\n\n    exec: {\n      npm_publish: 'npm publish',\n      git_is_clean: 'test -z \"$(git status --porcelain)\"',\n      git_on_master: 'test $(git symbolic-ref --short -q HEAD) = master',\n      git_add: 'git add .',\n      git_push: 'git push && git push --tags',\n      git_commit: {\n        cmd: function(m) { return f('git commit -m \"%s\"', m); }\n      },\n      git_tag: {\n        cmd: function(v) { return f('git tag v%s -am \"%s\"', v, v); }\n      },\n      publish_assets: [\n        'cp -r <%= buildDir %> typeahead.js',\n        'zip -r typeahead.js/typeahead.js.zip typeahead.js',\n        'git checkout gh-pages',\n        'rm -rf releases/latest',\n        'cp -r typeahead.js releases/<%= version %>',\n        'cp -r typeahead.js releases/latest',\n        'git add releases/<%= version %> releases/latest',\n        'sed -E -i \"\" \\'s/v[0-9]+\\\\.[0-9]+\\\\.[0-9]+/v<%= version %>/\\' index.html',\n        'git add index.html',\n        'git commit -m \"Add assets for <%= version %>.\"',\n        'git push',\n        'git checkout -',\n        'rm -rf typeahead.js'\n      ].join(' && ')\n    },\n\n    clean: {\n      dist: 'dist'\n    },\n\n    connect: {\n      server: {\n        options: { port: 8888, keepalive: true }\n      }\n    },\n\n    concurrent: {\n      options: { logConcurrentOutput: true },\n      dev: ['server', 'watch']\n    },\n\n    step: {\n      options: {\n        option: false\n      }\n    }\n  });\n\n  grunt.registerTask('release', '#shipit', function(version) {\n    var curVersion = grunt.config.get('version');\n\n    version = semver.inc(curVersion, version) || version;\n\n    if (!semver.valid(version) || semver.lte(version, curVersion)) {\n      grunt.fatal('hey dummy, that version is no good!');\n    }\n\n    grunt.config.set('version', version);\n\n    grunt.task.run([\n      'exec:git_on_master',\n      'exec:git_is_clean',\n      f('step:Update to version %s?', version),\n      f('manifests:%s', version),\n      'build',\n      'exec:git_add',\n      f('exec:git_commit:%s', version),\n      f('exec:git_tag:%s', version),\n      'step:Push changes?',\n      'exec:git_push',\n      'step:Publish to npm?',\n      'exec:npm_publish',\n      'step:Publish assets?',\n      'exec:publish_assets'\n    ]);\n  });\n\n  grunt.registerTask('manifests', 'Update manifests.', function(version) {\n    var _ = grunt.util._,\n        pkg = grunt.file.readJSON('package.json'),\n        bower = grunt.file.readJSON('bower.json'),\n        jqueryPlugin = grunt.file.readJSON('typeahead.js.jquery.json');\n\n    bower = JSON.stringify(_.extend(bower, {\n      name: pkg.name,\n      version: version\n    }), null, 2);\n\n    jqueryPlugin = JSON.stringify(_.extend(jqueryPlugin, {\n      name: pkg.name,\n      title: pkg.name,\n      version: version,\n      author: pkg.author,\n      description: pkg.description,\n      keywords: pkg.keywords,\n      homepage: pkg.homepage,\n      bugs: pkg.bugs,\n      maintainers: pkg.contributors\n    }), null, 2);\n\n    pkg = JSON.stringify(_.extend(pkg, {\n      version: version\n    }), null, 2);\n\n    grunt.file.write('package.json', pkg);\n    grunt.file.write('bower.json', bower);\n    grunt.file.write('typeahead.js.jquery.json', jqueryPlugin);\n  });\n\n  // aliases\n  // -------\n\n  grunt.registerTask('default', 'build');\n  grunt.registerTask('server', 'connect:server');\n  grunt.registerTask('lint', 'jshint');\n  grunt.registerTask('dev', ['build', 'concurrent:dev']);\n  grunt.registerTask('build', [\n    'uglify:concatBloodhound',\n    'uglify:concatTypeahead',\n    'umd:bloodhound',\n    'umd:typeahead',\n    'uglify:bloodhound',\n    'uglify:bloodhoundMin',\n    'uglify:typeahead',\n    'uglify:typeaheadMin',\n    'uglify:bundle',\n    'uglify:bundleMin',\n    'sed:version'\n  ]);\n\n  // load tasks\n  // ----------\n\n  grunt.loadNpmTasks('grunt-umd');\n  grunt.loadNpmTasks('grunt-sed');\n  grunt.loadNpmTasks('grunt-exec');\n  grunt.loadNpmTasks('grunt-step');\n  grunt.loadNpmTasks('grunt-concurrent');\n  grunt.loadNpmTasks('grunt-contrib-watch');\n  grunt.loadNpmTasks('grunt-contrib-clean');\n  grunt.loadNpmTasks('grunt-contrib-uglify');\n  grunt.loadNpmTasks('grunt-contrib-jshint');\n  grunt.loadNpmTasks('grunt-contrib-concat');\n  grunt.loadNpmTasks('grunt-contrib-connect');\n};\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2013-2014 Twitter, Inc\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![build status](https://secure.travis-ci.org/twitter/typeahead.js.svg?branch=master)](http://travis-ci.org/twitter/typeahead.js)\n[![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/)\n\n\n[typeahead.js][gh-page]\n=======================\n\nInspired by [twitter.com]'s autocomplete search functionality, typeahead.js is \na flexible JavaScript library that provides a strong foundation for building \nrobust typeaheads.\n\nThe typeahead.js library consists of 2 components: the suggestion engine, \n[Bloodhound], and the UI view, [Typeahead]. \nThe suggestion engine is responsible for computing suggestions for a given \nquery. The UI view is responsible for rendering suggestions and handling DOM \ninteractions. Both components can be used separately, but when used together, \nthey can provide a rich typeahead experience.\n\n<!-- section links -->\n\n[gh-page]: http://twitter.github.io/typeahead.js/\n[twitter.com]: https://twitter.com\n[Bloodhound]: https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md\n[Typeahead]: https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md\n\nGetting Started\n---------------\n\nHow you acquire typeahead.js is up to you.\n\nPreferred method:\n* Install with [Bower]: `$ bower install typeahead.js`\n\nOther methods:\n* [Download zipball of latest release][zipball].\n* Download the latest dist files individually:\n  * *[bloodhound.js]* (standalone suggestion engine)\n  * *[typeahead.jquery.js]* (standalone UI view)\n  * *[typeahead.bundle.js]* (*bloodhound.js* + *typeahead.jquery.js*)\n  * *[typeahead.bundle.min.js]*\n\n**Note:** both *bloodhound.js* and *typeahead.jquery.js* have a dependency on \n[jQuery] 1.9+.\n\n<!-- section links -->\n\n[Bower]: http://bower.io/\n[zipball]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.js.zip\n[bloodhound.js]: http://twitter.github.com/typeahead.js/releases/latest/bloodhound.js\n[typeahead.jquery.js]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.jquery.js\n[typeahead.bundle.js]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.bundle.js\n[typeahead.bundle.min.js]: http://twitter.github.com/typeahead.js/releases/latest/typeahead.bundle.min.js\n[jQuery]: http://jquery.com/\n\nDocumentation \n-------------\n\n* [Typeahead Docs]\n* [Bloodhound Docs]\n\n[Typeahead Docs]: https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md\n[Bloodhound Docs]: https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md\n\nExamples\n--------\n\nFor some working examples of typeahead.js, visit the [examples page].\n\n<!-- section links -->\n\n[examples page]: http://twitter.github.io/typeahead.js/examples\n\nBrowser Support\n---------------\n\n* Chrome\n* Firefox 3.5+\n* Safari 4+\n* Internet Explorer 8+\n* Opera 11+\n\n**NOTE:** typeahead.js is not tested on mobile browsers.\n\nCustomer Support\n----------------\n\nFor general questions about typeahead.js, tweet at [@typeahead].\n\nFor technical questions, you should post a question on [Stack Overflow] and tag \nit with [typeahead.js][so tag].\n\n<!-- section links -->\n\n[Stack Overflow]: http://stackoverflow.com/\n[@typeahead]: https://twitter.com/typeahead\n[so tag]: http://stackoverflow.com/questions/tagged/typeahead.js\n\nIssues\n------\n\nDiscovered a bug? Please create an issue here on GitHub!\n\nhttps://github.com/twitter/typeahead.js/issues\n\nVersioning\n----------\n\nFor transparency and insight into our release cycle, releases will be numbered \nwith the following format:\n\n`<major>.<minor>.<patch>`\n\nAnd constructed with the following guidelines:\n\n* Breaking backwards compatibility bumps the major\n* New additions without breaking backwards compatibility bumps the minor\n* Bug fixes and misc changes bump the patch\n\nFor more information on semantic versioning, please visit http://semver.org/.\n\nTesting\n-------\n\nTests are written using [Jasmine] and ran with [Karma]. To run\nthe test suite with PhantomJS, run `$ npm test`.\n\n<!-- section links -->\n\n[Jasmine]: http://jasmine.github.io/\n[Karma]: http://karma-runner.github.io/\n\nDevelopers\n----------\n\nIf you plan on contributing to typeahead.js, be sure to read the \n[contributing guidelines]. A good starting place for new contributors are issues\nlabeled with [entry-level]. Entry-level issues tend to require minor changes \nand provide developers a chance to get more familiar with typeahead.js before\ntaking on more challenging work.\n\nIn order to build and test typeahead.js, you'll need to install its dev \ndependencies (`$ npm install`) and have [grunt-cli] \ninstalled (`$ npm install -g grunt-cli`). Below is an overview of the available \nGrunt tasks that'll be useful in development.\n\n* `grunt build` – Builds *typeahead.js* from source.\n* `grunt lint` – Runs source and test files through JSHint.\n* `grunt watch` – Rebuilds *typeahead.js* whenever a source file is modified.\n* `grunt server` – Serves files from the root of typeahead.js on localhost:8888. \n  Useful for using *test/playground.html* for debugging/testing.\n* `grunt dev` – Runs `grunt watch` and `grunt server` in parallel.\n\n<!-- section links -->\n\n[contributing guidelines]: https://github.com/twitter/typeahead.js/blob/master/CONTRIBUTING.md\n[entry-level]: https://github.com/twitter/typeahead.js/issues?&labels=entry-level&state=open\n[grunt-cli]: https://github.com/gruntjs/grunt-cli\n\nMaintainers\n-----------\n\n* **Jake Harding** \n  * [@JakeHarding](https://twitter.com/JakeHarding) \n  * [GitHub](https://github.com/jharding)\n\n* **You?**\n\nAuthors\n-------\n\n* **Jake Harding** \n  * [@JakeHarding](https://twitter.com/JakeHarding) \n  * [GitHub](https://github.com/jharding)\n\n* **Veljko Skarich**\n  * [@vskarich](https://twitter.com/vskarich) \n  * [GitHub](https://github.com/vskarich)\n\n* **Tim Trueman**\n  * [@timtrueman](https://twitter.com/timtrueman) \n  * [GitHub](https://github.com/timtrueman)\n\nLicense\n-------\n\nCopyright 2013 Twitter, Inc.\n\nLicensed under the MIT License\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"typeahead.js\",\n  \"version\": \"0.11.1\",\n  \"main\": \"dist/typeahead.bundle.js\",\n  \"dependencies\": {\n    \"jquery\": \">=1.7\"\n  },\n  \"devDependencies\": {\n    \"jquery\": \"~1.7\",\n    \"jasmine-ajax\": \"~1.3.1\",\n    \"jasmine-jquery\": \"~1.5.2\"\n  }\n}"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"twitter/typeahead.js\",\n    \"description\": \"fast and fully-featured autocomplete library\",\n    \"keywords\": [\"typeahead\", \"autocomplete\"],\n    \"homepage\": \"http://twitter.github.com/typeahead.js\",\n    \"authors\": [\n        {\n            \"name\": \"Twitter Inc.\",\n            \"homepage\": \"https://twitter.com/twitteross\"\n        }\n    ],\n    \"support\": {\n        \"issues\": \"https://github.com/twitter/typeahead.js/issues\"\n    },\n    \"author\": \"Twitter Inc.\",\n    \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "dist/bloodhound.js",
    "content": "/*!\n * typeahead.js 0.11.1\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT\n */\n\n(function(root, factory) {\n    if (typeof define === \"function\" && define.amd) {\n        define(\"bloodhound\", [ \"jquery\" ], function(a0) {\n            return root[\"Bloodhound\"] = factory(a0);\n        });\n    } else if (typeof exports === \"object\") {\n        module.exports = factory(require(\"jquery\"));\n    } else {\n        root[\"Bloodhound\"] = factory(jQuery);\n    }\n})(this, function($) {\n    var _ = function() {\n        \"use strict\";\n        return {\n            isMsie: function() {\n                return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\\d+(.\\d+)?)/i)[2] : false;\n            },\n            isBlankString: function(str) {\n                return !str || /^\\s*$/.test(str);\n            },\n            escapeRegExChars: function(str) {\n                return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n            },\n            isString: function(obj) {\n                return typeof obj === \"string\";\n            },\n            isNumber: function(obj) {\n                return typeof obj === \"number\";\n            },\n            isArray: $.isArray,\n            isFunction: $.isFunction,\n            isObject: $.isPlainObject,\n            isUndefined: function(obj) {\n                return typeof obj === \"undefined\";\n            },\n            isElement: function(obj) {\n                return !!(obj && obj.nodeType === 1);\n            },\n            isJQuery: function(obj) {\n                return obj instanceof $;\n            },\n            toStr: function toStr(s) {\n                return _.isUndefined(s) || s === null ? \"\" : s + \"\";\n            },\n            bind: $.proxy,\n            each: function(collection, cb) {\n                $.each(collection, reverseArgs);\n                function reverseArgs(index, value) {\n                    return cb(value, index);\n                }\n            },\n            map: $.map,\n            filter: $.grep,\n            every: function(obj, test) {\n                var result = true;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (!(result = test.call(null, val, key, obj))) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            some: function(obj, test) {\n                var result = false;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (result = test.call(null, val, key, obj)) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            mixin: $.extend,\n            identity: function(x) {\n                return x;\n            },\n            clone: function(obj) {\n                return $.extend(true, {}, obj);\n            },\n            getIdGenerator: function() {\n                var counter = 0;\n                return function() {\n                    return counter++;\n                };\n            },\n            templatify: function templatify(obj) {\n                return $.isFunction(obj) ? obj : template;\n                function template() {\n                    return String(obj);\n                }\n            },\n            defer: function(fn) {\n                setTimeout(fn, 0);\n            },\n            debounce: function(func, wait, immediate) {\n                var timeout, result;\n                return function() {\n                    var context = this, args = arguments, later, callNow;\n                    later = function() {\n                        timeout = null;\n                        if (!immediate) {\n                            result = func.apply(context, args);\n                        }\n                    };\n                    callNow = immediate && !timeout;\n                    clearTimeout(timeout);\n                    timeout = setTimeout(later, wait);\n                    if (callNow) {\n                        result = func.apply(context, args);\n                    }\n                    return result;\n                };\n            },\n            throttle: function(func, wait) {\n                var context, args, timeout, result, previous, later;\n                previous = 0;\n                later = function() {\n                    previous = new Date();\n                    timeout = null;\n                    result = func.apply(context, args);\n                };\n                return function() {\n                    var now = new Date(), remaining = wait - (now - previous);\n                    context = this;\n                    args = arguments;\n                    if (remaining <= 0) {\n                        clearTimeout(timeout);\n                        timeout = null;\n                        previous = now;\n                        result = func.apply(context, args);\n                    } else if (!timeout) {\n                        timeout = setTimeout(later, remaining);\n                    }\n                    return result;\n                };\n            },\n            stringify: function(val) {\n                return _.isString(val) ? val : JSON.stringify(val);\n            },\n            noop: function() {}\n        };\n    }();\n    var VERSION = \"0.11.1\";\n    var tokenizers = function() {\n        \"use strict\";\n        return {\n            nonword: nonword,\n            whitespace: whitespace,\n            obj: {\n                nonword: getObjTokenizer(nonword),\n                whitespace: getObjTokenizer(whitespace)\n            }\n        };\n        function whitespace(str) {\n            str = _.toStr(str);\n            return str ? str.split(/\\s+/) : [];\n        }\n        function nonword(str) {\n            str = _.toStr(str);\n            return str ? str.split(/\\W+/) : [];\n        }\n        function getObjTokenizer(tokenizer) {\n            return function setKey(keys) {\n                keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);\n                return function tokenize(o) {\n                    var tokens = [];\n                    _.each(keys, function(k) {\n                        tokens = tokens.concat(tokenizer(_.toStr(o[k])));\n                    });\n                    return tokens;\n                };\n            };\n        }\n    }();\n    var LruCache = function() {\n        \"use strict\";\n        function LruCache(maxSize) {\n            this.maxSize = _.isNumber(maxSize) ? maxSize : 100;\n            this.reset();\n            if (this.maxSize <= 0) {\n                this.set = this.get = $.noop;\n            }\n        }\n        _.mixin(LruCache.prototype, {\n            set: function set(key, val) {\n                var tailItem = this.list.tail, node;\n                if (this.size >= this.maxSize) {\n                    this.list.remove(tailItem);\n                    delete this.hash[tailItem.key];\n                    this.size--;\n                }\n                if (node = this.hash[key]) {\n                    node.val = val;\n                    this.list.moveToFront(node);\n                } else {\n                    node = new Node(key, val);\n                    this.list.add(node);\n                    this.hash[key] = node;\n                    this.size++;\n                }\n            },\n            get: function get(key) {\n                var node = this.hash[key];\n                if (node) {\n                    this.list.moveToFront(node);\n                    return node.val;\n                }\n            },\n            reset: function reset() {\n                this.size = 0;\n                this.hash = {};\n                this.list = new List();\n            }\n        });\n        function List() {\n            this.head = this.tail = null;\n        }\n        _.mixin(List.prototype, {\n            add: function add(node) {\n                if (this.head) {\n                    node.next = this.head;\n                    this.head.prev = node;\n                }\n                this.head = node;\n                this.tail = this.tail || node;\n            },\n            remove: function remove(node) {\n                node.prev ? node.prev.next = node.next : this.head = node.next;\n                node.next ? node.next.prev = node.prev : this.tail = node.prev;\n            },\n            moveToFront: function(node) {\n                this.remove(node);\n                this.add(node);\n            }\n        });\n        function Node(key, val) {\n            this.key = key;\n            this.val = val;\n            this.prev = this.next = null;\n        }\n        return LruCache;\n    }();\n    var PersistentStorage = function() {\n        \"use strict\";\n        var LOCAL_STORAGE;\n        try {\n            LOCAL_STORAGE = window.localStorage;\n            LOCAL_STORAGE.setItem(\"~~~\", \"!\");\n            LOCAL_STORAGE.removeItem(\"~~~\");\n        } catch (err) {\n            LOCAL_STORAGE = null;\n        }\n        function PersistentStorage(namespace, override) {\n            this.prefix = [ \"__\", namespace, \"__\" ].join(\"\");\n            this.ttlKey = \"__ttl__\";\n            this.keyMatcher = new RegExp(\"^\" + _.escapeRegExChars(this.prefix));\n            this.ls = override || LOCAL_STORAGE;\n            !this.ls && this._noop();\n        }\n        _.mixin(PersistentStorage.prototype, {\n            _prefix: function(key) {\n                return this.prefix + key;\n            },\n            _ttlKey: function(key) {\n                return this._prefix(key) + this.ttlKey;\n            },\n            _noop: function() {\n                this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;\n            },\n            _safeSet: function(key, val) {\n                try {\n                    this.ls.setItem(key, val);\n                } catch (err) {\n                    if (err.name === \"QuotaExceededError\") {\n                        this.clear();\n                        this._noop();\n                    }\n                }\n            },\n            get: function(key) {\n                if (this.isExpired(key)) {\n                    this.remove(key);\n                }\n                return decode(this.ls.getItem(this._prefix(key)));\n            },\n            set: function(key, val, ttl) {\n                if (_.isNumber(ttl)) {\n                    this._safeSet(this._ttlKey(key), encode(now() + ttl));\n                } else {\n                    this.ls.removeItem(this._ttlKey(key));\n                }\n                return this._safeSet(this._prefix(key), encode(val));\n            },\n            remove: function(key) {\n                this.ls.removeItem(this._ttlKey(key));\n                this.ls.removeItem(this._prefix(key));\n                return this;\n            },\n            clear: function() {\n                var i, keys = gatherMatchingKeys(this.keyMatcher);\n                for (i = keys.length; i--; ) {\n                    this.remove(keys[i]);\n                }\n                return this;\n            },\n            isExpired: function(key) {\n                var ttl = decode(this.ls.getItem(this._ttlKey(key)));\n                return _.isNumber(ttl) && now() > ttl ? true : false;\n            }\n        });\n        return PersistentStorage;\n        function now() {\n            return new Date().getTime();\n        }\n        function encode(val) {\n            return JSON.stringify(_.isUndefined(val) ? null : val);\n        }\n        function decode(val) {\n            return $.parseJSON(val);\n        }\n        function gatherMatchingKeys(keyMatcher) {\n            var i, key, keys = [], len = LOCAL_STORAGE.length;\n            for (i = 0; i < len; i++) {\n                if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {\n                    keys.push(key.replace(keyMatcher, \"\"));\n                }\n            }\n            return keys;\n        }\n    }();\n    var Transport = function() {\n        \"use strict\";\n        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);\n        function Transport(o) {\n            o = o || {};\n            this.cancelled = false;\n            this.lastReq = null;\n            this._send = o.transport;\n            this._get = o.limiter ? o.limiter(this._get) : this._get;\n            this._cache = o.cache === false ? new LruCache(0) : sharedCache;\n        }\n        Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {\n            maxPendingRequests = num;\n        };\n        Transport.resetCache = function resetCache() {\n            sharedCache.reset();\n        };\n        _.mixin(Transport.prototype, {\n            _fingerprint: function fingerprint(o) {\n                o = o || {};\n                return o.url + o.type + $.param(o.data || {});\n            },\n            _get: function(o, cb) {\n                var that = this, fingerprint, jqXhr;\n                fingerprint = this._fingerprint(o);\n                if (this.cancelled || fingerprint !== this.lastReq) {\n                    return;\n                }\n                if (jqXhr = pendingRequests[fingerprint]) {\n                    jqXhr.done(done).fail(fail);\n                } else if (pendingRequestsCount < maxPendingRequests) {\n                    pendingRequestsCount++;\n                    pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);\n                } else {\n                    this.onDeckRequestArgs = [].slice.call(arguments, 0);\n                }\n                function done(resp) {\n                    cb(null, resp);\n                    that._cache.set(fingerprint, resp);\n                }\n                function fail() {\n                    cb(true);\n                }\n                function always() {\n                    pendingRequestsCount--;\n                    delete pendingRequests[fingerprint];\n                    if (that.onDeckRequestArgs) {\n                        that._get.apply(that, that.onDeckRequestArgs);\n                        that.onDeckRequestArgs = null;\n                    }\n                }\n            },\n            get: function(o, cb) {\n                var resp, fingerprint;\n                cb = cb || $.noop;\n                o = _.isString(o) ? {\n                    url: o\n                } : o || {};\n                fingerprint = this._fingerprint(o);\n                this.cancelled = false;\n                this.lastReq = fingerprint;\n                if (resp = this._cache.get(fingerprint)) {\n                    cb(null, resp);\n                } else {\n                    this._get(o, cb);\n                }\n            },\n            cancel: function() {\n                this.cancelled = true;\n            }\n        });\n        return Transport;\n    }();\n    var SearchIndex = window.SearchIndex = function() {\n        \"use strict\";\n        var CHILDREN = \"c\", IDS = \"i\";\n        function SearchIndex(o) {\n            o = o || {};\n            if (!o.datumTokenizer || !o.queryTokenizer) {\n                $.error(\"datumTokenizer and queryTokenizer are both required\");\n            }\n            this.identify = o.identify || _.stringify;\n            this.datumTokenizer = o.datumTokenizer;\n            this.queryTokenizer = o.queryTokenizer;\n            this.reset();\n        }\n        _.mixin(SearchIndex.prototype, {\n            bootstrap: function bootstrap(o) {\n                this.datums = o.datums;\n                this.trie = o.trie;\n            },\n            add: function(data) {\n                var that = this;\n                data = _.isArray(data) ? data : [ data ];\n                _.each(data, function(datum) {\n                    var id, tokens;\n                    that.datums[id = that.identify(datum)] = datum;\n                    tokens = normalizeTokens(that.datumTokenizer(datum));\n                    _.each(tokens, function(token) {\n                        var node, chars, ch;\n                        node = that.trie;\n                        chars = token.split(\"\");\n                        while (ch = chars.shift()) {\n                            node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());\n                            node[IDS].push(id);\n                        }\n                    });\n                });\n            },\n            get: function get(ids) {\n                var that = this;\n                return _.map(ids, function(id) {\n                    return that.datums[id];\n                });\n            },\n            search: function search(query) {\n                var that = this, tokens, matches;\n                tokens = normalizeTokens(this.queryTokenizer(query));\n                _.each(tokens, function(token) {\n                    var node, chars, ch, ids;\n                    if (matches && matches.length === 0) {\n                        return false;\n                    }\n                    node = that.trie;\n                    chars = token.split(\"\");\n                    while (node && (ch = chars.shift())) {\n                        node = node[CHILDREN][ch];\n                    }\n                    if (node && chars.length === 0) {\n                        ids = node[IDS].slice(0);\n                        matches = matches ? getIntersection(matches, ids) : ids;\n                    } else {\n                        matches = [];\n                        return false;\n                    }\n                });\n                return matches ? _.map(unique(matches), function(id) {\n                    return that.datums[id];\n                }) : [];\n            },\n            all: function all() {\n                var values = [];\n                for (var key in this.datums) {\n                    values.push(this.datums[key]);\n                }\n                return values;\n            },\n            reset: function reset() {\n                this.datums = {};\n                this.trie = newNode();\n            },\n            serialize: function serialize() {\n                return {\n                    datums: this.datums,\n                    trie: this.trie\n                };\n            }\n        });\n        return SearchIndex;\n        function normalizeTokens(tokens) {\n            tokens = _.filter(tokens, function(token) {\n                return !!token;\n            });\n            tokens = _.map(tokens, function(token) {\n                return token.toLowerCase();\n            });\n            return tokens;\n        }\n        function newNode() {\n            var node = {};\n            node[IDS] = [];\n            node[CHILDREN] = {};\n            return node;\n        }\n        function unique(array) {\n            var seen = {}, uniques = [];\n            for (var i = 0, len = array.length; i < len; i++) {\n                if (!seen[array[i]]) {\n                    seen[array[i]] = true;\n                    uniques.push(array[i]);\n                }\n            }\n            return uniques;\n        }\n        function getIntersection(arrayA, arrayB) {\n            var ai = 0, bi = 0, intersection = [];\n            arrayA = arrayA.sort();\n            arrayB = arrayB.sort();\n            var lenArrayA = arrayA.length, lenArrayB = arrayB.length;\n            while (ai < lenArrayA && bi < lenArrayB) {\n                if (arrayA[ai] < arrayB[bi]) {\n                    ai++;\n                } else if (arrayA[ai] > arrayB[bi]) {\n                    bi++;\n                } else {\n                    intersection.push(arrayA[ai]);\n                    ai++;\n                    bi++;\n                }\n            }\n            return intersection;\n        }\n    }();\n    var Prefetch = function() {\n        \"use strict\";\n        var keys;\n        keys = {\n            data: \"data\",\n            protocol: \"protocol\",\n            thumbprint: \"thumbprint\"\n        };\n        function Prefetch(o) {\n            this.url = o.url;\n            this.ttl = o.ttl;\n            this.cache = o.cache;\n            this.prepare = o.prepare;\n            this.transform = o.transform;\n            this.transport = o.transport;\n            this.thumbprint = o.thumbprint;\n            this.storage = new PersistentStorage(o.cacheKey);\n        }\n        _.mixin(Prefetch.prototype, {\n            _settings: function settings() {\n                return {\n                    url: this.url,\n                    type: \"GET\",\n                    dataType: \"json\"\n                };\n            },\n            store: function store(data) {\n                if (!this.cache) {\n                    return;\n                }\n                this.storage.set(keys.data, data, this.ttl);\n                this.storage.set(keys.protocol, location.protocol, this.ttl);\n                this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);\n            },\n            fromCache: function fromCache() {\n                var stored = {}, isExpired;\n                if (!this.cache) {\n                    return null;\n                }\n                stored.data = this.storage.get(keys.data);\n                stored.protocol = this.storage.get(keys.protocol);\n                stored.thumbprint = this.storage.get(keys.thumbprint);\n                isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;\n                return stored.data && !isExpired ? stored.data : null;\n            },\n            fromNetwork: function(cb) {\n                var that = this, settings;\n                if (!cb) {\n                    return;\n                }\n                settings = this.prepare(this._settings());\n                this.transport(settings).fail(onError).done(onResponse);\n                function onError() {\n                    cb(true);\n                }\n                function onResponse(resp) {\n                    cb(null, that.transform(resp));\n                }\n            },\n            clear: function clear() {\n                this.storage.clear();\n                return this;\n            }\n        });\n        return Prefetch;\n    }();\n    var Remote = function() {\n        \"use strict\";\n        function Remote(o) {\n            this.url = o.url;\n            this.prepare = o.prepare;\n            this.transform = o.transform;\n            this.transport = new Transport({\n                cache: o.cache,\n                limiter: o.limiter,\n                transport: o.transport\n            });\n        }\n        _.mixin(Remote.prototype, {\n            _settings: function settings() {\n                return {\n                    url: this.url,\n                    type: \"GET\",\n                    dataType: \"json\"\n                };\n            },\n            get: function get(query, cb) {\n                var that = this, settings;\n                if (!cb) {\n                    return;\n                }\n                query = query || \"\";\n                settings = this.prepare(query, this._settings());\n                return this.transport.get(settings, onResponse);\n                function onResponse(err, resp) {\n                    err ? cb([]) : cb(that.transform(resp));\n                }\n            },\n            cancelLastRequest: function cancelLastRequest() {\n                this.transport.cancel();\n            }\n        });\n        return Remote;\n    }();\n    var oParser = function() {\n        \"use strict\";\n        return function parse(o) {\n            var defaults, sorter;\n            defaults = {\n                initialize: true,\n                identify: _.stringify,\n                datumTokenizer: null,\n                queryTokenizer: null,\n                sufficient: 5,\n                sorter: null,\n                local: [],\n                prefetch: null,\n                remote: null\n            };\n            o = _.mixin(defaults, o || {});\n            !o.datumTokenizer && $.error(\"datumTokenizer is required\");\n            !o.queryTokenizer && $.error(\"queryTokenizer is required\");\n            sorter = o.sorter;\n            o.sorter = sorter ? function(x) {\n                return x.sort(sorter);\n            } : _.identity;\n            o.local = _.isFunction(o.local) ? o.local() : o.local;\n            o.prefetch = parsePrefetch(o.prefetch);\n            o.remote = parseRemote(o.remote);\n            return o;\n        };\n        function parsePrefetch(o) {\n            var defaults;\n            if (!o) {\n                return null;\n            }\n            defaults = {\n                url: null,\n                ttl: 24 * 60 * 60 * 1e3,\n                cache: true,\n                cacheKey: null,\n                thumbprint: \"\",\n                prepare: _.identity,\n                transform: _.identity,\n                transport: null\n            };\n            o = _.isString(o) ? {\n                url: o\n            } : o;\n            o = _.mixin(defaults, o);\n            !o.url && $.error(\"prefetch requires url to be set\");\n            o.transform = o.filter || o.transform;\n            o.cacheKey = o.cacheKey || o.url;\n            o.thumbprint = VERSION + o.thumbprint;\n            o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;\n            return o;\n        }\n        function parseRemote(o) {\n            var defaults;\n            if (!o) {\n                return;\n            }\n            defaults = {\n                url: null,\n                cache: true,\n                prepare: null,\n                replace: null,\n                wildcard: null,\n                limiter: null,\n                rateLimitBy: \"debounce\",\n                rateLimitWait: 300,\n                transform: _.identity,\n                transport: null\n            };\n            o = _.isString(o) ? {\n                url: o\n            } : o;\n            o = _.mixin(defaults, o);\n            !o.url && $.error(\"remote requires url to be set\");\n            o.transform = o.filter || o.transform;\n            o.prepare = toRemotePrepare(o);\n            o.limiter = toLimiter(o);\n            o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;\n            delete o.replace;\n            delete o.wildcard;\n            delete o.rateLimitBy;\n            delete o.rateLimitWait;\n            return o;\n        }\n        function toRemotePrepare(o) {\n            var prepare, replace, wildcard;\n            prepare = o.prepare;\n            replace = o.replace;\n            wildcard = o.wildcard;\n            if (prepare) {\n                return prepare;\n            }\n            if (replace) {\n                prepare = prepareByReplace;\n            } else if (o.wildcard) {\n                prepare = prepareByWildcard;\n            } else {\n                prepare = idenityPrepare;\n            }\n            return prepare;\n            function prepareByReplace(query, settings) {\n                settings.url = replace(settings.url, query);\n                return settings;\n            }\n            function prepareByWildcard(query, settings) {\n                settings.url = settings.url.replace(wildcard, encodeURIComponent(query));\n                return settings;\n            }\n            function idenityPrepare(query, settings) {\n                return settings;\n            }\n        }\n        function toLimiter(o) {\n            var limiter, method, wait;\n            limiter = o.limiter;\n            method = o.rateLimitBy;\n            wait = o.rateLimitWait;\n            if (!limiter) {\n                limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);\n            }\n            return limiter;\n            function debounce(wait) {\n                return function debounce(fn) {\n                    return _.debounce(fn, wait);\n                };\n            }\n            function throttle(wait) {\n                return function throttle(fn) {\n                    return _.throttle(fn, wait);\n                };\n            }\n        }\n        function callbackToDeferred(fn) {\n            return function wrapper(o) {\n                var deferred = $.Deferred();\n                fn(o, onSuccess, onError);\n                return deferred;\n                function onSuccess(resp) {\n                    _.defer(function() {\n                        deferred.resolve(resp);\n                    });\n                }\n                function onError(err) {\n                    _.defer(function() {\n                        deferred.reject(err);\n                    });\n                }\n            };\n        }\n    }();\n    var Bloodhound = function() {\n        \"use strict\";\n        var old;\n        old = window && window.Bloodhound;\n        function Bloodhound(o) {\n            o = oParser(o);\n            this.sorter = o.sorter;\n            this.identify = o.identify;\n            this.sufficient = o.sufficient;\n            this.local = o.local;\n            this.remote = o.remote ? new Remote(o.remote) : null;\n            this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;\n            this.index = new SearchIndex({\n                identify: this.identify,\n                datumTokenizer: o.datumTokenizer,\n                queryTokenizer: o.queryTokenizer\n            });\n            o.initialize !== false && this.initialize();\n        }\n        Bloodhound.noConflict = function noConflict() {\n            window && (window.Bloodhound = old);\n            return Bloodhound;\n        };\n        Bloodhound.tokenizers = tokenizers;\n        _.mixin(Bloodhound.prototype, {\n            __ttAdapter: function ttAdapter() {\n                var that = this;\n                return this.remote ? withAsync : withoutAsync;\n                function withAsync(query, sync, async) {\n                    return that.search(query, sync, async);\n                }\n                function withoutAsync(query, sync) {\n                    return that.search(query, sync);\n                }\n            },\n            _loadPrefetch: function loadPrefetch() {\n                var that = this, deferred, serialized;\n                deferred = $.Deferred();\n                if (!this.prefetch) {\n                    deferred.resolve();\n                } else if (serialized = this.prefetch.fromCache()) {\n                    this.index.bootstrap(serialized);\n                    deferred.resolve();\n                } else {\n                    this.prefetch.fromNetwork(done);\n                }\n                return deferred.promise();\n                function done(err, data) {\n                    if (err) {\n                        return deferred.reject();\n                    }\n                    that.add(data);\n                    that.prefetch.store(that.index.serialize());\n                    deferred.resolve();\n                }\n            },\n            _initialize: function initialize() {\n                var that = this, deferred;\n                this.clear();\n                (this.initPromise = this._loadPrefetch()).done(addLocalToIndex);\n                return this.initPromise;\n                function addLocalToIndex() {\n                    that.add(that.local);\n                }\n            },\n            initialize: function initialize(force) {\n                return !this.initPromise || force ? this._initialize() : this.initPromise;\n            },\n            add: function add(data) {\n                this.index.add(data);\n                return this;\n            },\n            get: function get(ids) {\n                ids = _.isArray(ids) ? ids : [].slice.call(arguments);\n                return this.index.get(ids);\n            },\n            search: function search(query, sync, async) {\n                var that = this, local;\n                local = this.sorter(this.index.search(query));\n                sync(this.remote ? local.slice() : local);\n                if (this.remote && local.length < this.sufficient) {\n                    this.remote.get(query, processRemote);\n                } else if (this.remote) {\n                    this.remote.cancelLastRequest();\n                }\n                return this;\n                function processRemote(remote) {\n                    var nonDuplicates = [];\n                    _.each(remote, function(r) {\n                        !_.some(local, function(l) {\n                            return that.identify(r) === that.identify(l);\n                        }) && nonDuplicates.push(r);\n                    });\n                    async && async(nonDuplicates);\n                }\n            },\n            all: function all() {\n                return this.index.all();\n            },\n            clear: function clear() {\n                this.index.reset();\n                return this;\n            },\n            clearPrefetchCache: function clearPrefetchCache() {\n                this.prefetch && this.prefetch.clear();\n                return this;\n            },\n            clearRemoteCache: function clearRemoteCache() {\n                Transport.resetCache();\n                return this;\n            },\n            ttAdapter: function ttAdapter() {\n                return this.__ttAdapter();\n            }\n        });\n        return Bloodhound;\n    }();\n    return Bloodhound;\n});"
  },
  {
    "path": "dist/typeahead.bundle.js",
    "content": "/*!\n * typeahead.js 0.11.1\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT\n */\n\n(function(root, factory) {\n    if (typeof define === \"function\" && define.amd) {\n        define(\"bloodhound\", [ \"jquery\" ], function(a0) {\n            return root[\"Bloodhound\"] = factory(a0);\n        });\n    } else if (typeof exports === \"object\") {\n        module.exports = factory(require(\"jquery\"));\n    } else {\n        root[\"Bloodhound\"] = factory(jQuery);\n    }\n})(this, function($) {\n    var _ = function() {\n        \"use strict\";\n        return {\n            isMsie: function() {\n                return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\\d+(.\\d+)?)/i)[2] : false;\n            },\n            isBlankString: function(str) {\n                return !str || /^\\s*$/.test(str);\n            },\n            escapeRegExChars: function(str) {\n                return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n            },\n            isString: function(obj) {\n                return typeof obj === \"string\";\n            },\n            isNumber: function(obj) {\n                return typeof obj === \"number\";\n            },\n            isArray: $.isArray,\n            isFunction: $.isFunction,\n            isObject: $.isPlainObject,\n            isUndefined: function(obj) {\n                return typeof obj === \"undefined\";\n            },\n            isElement: function(obj) {\n                return !!(obj && obj.nodeType === 1);\n            },\n            isJQuery: function(obj) {\n                return obj instanceof $;\n            },\n            toStr: function toStr(s) {\n                return _.isUndefined(s) || s === null ? \"\" : s + \"\";\n            },\n            bind: $.proxy,\n            each: function(collection, cb) {\n                $.each(collection, reverseArgs);\n                function reverseArgs(index, value) {\n                    return cb(value, index);\n                }\n            },\n            map: $.map,\n            filter: $.grep,\n            every: function(obj, test) {\n                var result = true;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (!(result = test.call(null, val, key, obj))) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            some: function(obj, test) {\n                var result = false;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (result = test.call(null, val, key, obj)) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            mixin: $.extend,\n            identity: function(x) {\n                return x;\n            },\n            clone: function(obj) {\n                return $.extend(true, {}, obj);\n            },\n            getIdGenerator: function() {\n                var counter = 0;\n                return function() {\n                    return counter++;\n                };\n            },\n            templatify: function templatify(obj) {\n                return $.isFunction(obj) ? obj : template;\n                function template() {\n                    return String(obj);\n                }\n            },\n            defer: function(fn) {\n                setTimeout(fn, 0);\n            },\n            debounce: function(func, wait, immediate) {\n                var timeout, result;\n                return function() {\n                    var context = this, args = arguments, later, callNow;\n                    later = function() {\n                        timeout = null;\n                        if (!immediate) {\n                            result = func.apply(context, args);\n                        }\n                    };\n                    callNow = immediate && !timeout;\n                    clearTimeout(timeout);\n                    timeout = setTimeout(later, wait);\n                    if (callNow) {\n                        result = func.apply(context, args);\n                    }\n                    return result;\n                };\n            },\n            throttle: function(func, wait) {\n                var context, args, timeout, result, previous, later;\n                previous = 0;\n                later = function() {\n                    previous = new Date();\n                    timeout = null;\n                    result = func.apply(context, args);\n                };\n                return function() {\n                    var now = new Date(), remaining = wait - (now - previous);\n                    context = this;\n                    args = arguments;\n                    if (remaining <= 0) {\n                        clearTimeout(timeout);\n                        timeout = null;\n                        previous = now;\n                        result = func.apply(context, args);\n                    } else if (!timeout) {\n                        timeout = setTimeout(later, remaining);\n                    }\n                    return result;\n                };\n            },\n            stringify: function(val) {\n                return _.isString(val) ? val : JSON.stringify(val);\n            },\n            noop: function() {}\n        };\n    }();\n    var VERSION = \"0.11.1\";\n    var tokenizers = function() {\n        \"use strict\";\n        return {\n            nonword: nonword,\n            whitespace: whitespace,\n            obj: {\n                nonword: getObjTokenizer(nonword),\n                whitespace: getObjTokenizer(whitespace)\n            }\n        };\n        function whitespace(str) {\n            str = _.toStr(str);\n            return str ? str.split(/\\s+/) : [];\n        }\n        function nonword(str) {\n            str = _.toStr(str);\n            return str ? str.split(/\\W+/) : [];\n        }\n        function getObjTokenizer(tokenizer) {\n            return function setKey(keys) {\n                keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);\n                return function tokenize(o) {\n                    var tokens = [];\n                    _.each(keys, function(k) {\n                        tokens = tokens.concat(tokenizer(_.toStr(o[k])));\n                    });\n                    return tokens;\n                };\n            };\n        }\n    }();\n    var LruCache = function() {\n        \"use strict\";\n        function LruCache(maxSize) {\n            this.maxSize = _.isNumber(maxSize) ? maxSize : 100;\n            this.reset();\n            if (this.maxSize <= 0) {\n                this.set = this.get = $.noop;\n            }\n        }\n        _.mixin(LruCache.prototype, {\n            set: function set(key, val) {\n                var tailItem = this.list.tail, node;\n                if (this.size >= this.maxSize) {\n                    this.list.remove(tailItem);\n                    delete this.hash[tailItem.key];\n                    this.size--;\n                }\n                if (node = this.hash[key]) {\n                    node.val = val;\n                    this.list.moveToFront(node);\n                } else {\n                    node = new Node(key, val);\n                    this.list.add(node);\n                    this.hash[key] = node;\n                    this.size++;\n                }\n            },\n            get: function get(key) {\n                var node = this.hash[key];\n                if (node) {\n                    this.list.moveToFront(node);\n                    return node.val;\n                }\n            },\n            reset: function reset() {\n                this.size = 0;\n                this.hash = {};\n                this.list = new List();\n            }\n        });\n        function List() {\n            this.head = this.tail = null;\n        }\n        _.mixin(List.prototype, {\n            add: function add(node) {\n                if (this.head) {\n                    node.next = this.head;\n                    this.head.prev = node;\n                }\n                this.head = node;\n                this.tail = this.tail || node;\n            },\n            remove: function remove(node) {\n                node.prev ? node.prev.next = node.next : this.head = node.next;\n                node.next ? node.next.prev = node.prev : this.tail = node.prev;\n            },\n            moveToFront: function(node) {\n                this.remove(node);\n                this.add(node);\n            }\n        });\n        function Node(key, val) {\n            this.key = key;\n            this.val = val;\n            this.prev = this.next = null;\n        }\n        return LruCache;\n    }();\n    var PersistentStorage = function() {\n        \"use strict\";\n        var LOCAL_STORAGE;\n        try {\n            LOCAL_STORAGE = window.localStorage;\n            LOCAL_STORAGE.setItem(\"~~~\", \"!\");\n            LOCAL_STORAGE.removeItem(\"~~~\");\n        } catch (err) {\n            LOCAL_STORAGE = null;\n        }\n        function PersistentStorage(namespace, override) {\n            this.prefix = [ \"__\", namespace, \"__\" ].join(\"\");\n            this.ttlKey = \"__ttl__\";\n            this.keyMatcher = new RegExp(\"^\" + _.escapeRegExChars(this.prefix));\n            this.ls = override || LOCAL_STORAGE;\n            !this.ls && this._noop();\n        }\n        _.mixin(PersistentStorage.prototype, {\n            _prefix: function(key) {\n                return this.prefix + key;\n            },\n            _ttlKey: function(key) {\n                return this._prefix(key) + this.ttlKey;\n            },\n            _noop: function() {\n                this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;\n            },\n            _safeSet: function(key, val) {\n                try {\n                    this.ls.setItem(key, val);\n                } catch (err) {\n                    if (err.name === \"QuotaExceededError\") {\n                        this.clear();\n                        this._noop();\n                    }\n                }\n            },\n            get: function(key) {\n                if (this.isExpired(key)) {\n                    this.remove(key);\n                }\n                return decode(this.ls.getItem(this._prefix(key)));\n            },\n            set: function(key, val, ttl) {\n                if (_.isNumber(ttl)) {\n                    this._safeSet(this._ttlKey(key), encode(now() + ttl));\n                } else {\n                    this.ls.removeItem(this._ttlKey(key));\n                }\n                return this._safeSet(this._prefix(key), encode(val));\n            },\n            remove: function(key) {\n                this.ls.removeItem(this._ttlKey(key));\n                this.ls.removeItem(this._prefix(key));\n                return this;\n            },\n            clear: function() {\n                var i, keys = gatherMatchingKeys(this.keyMatcher);\n                for (i = keys.length; i--; ) {\n                    this.remove(keys[i]);\n                }\n                return this;\n            },\n            isExpired: function(key) {\n                var ttl = decode(this.ls.getItem(this._ttlKey(key)));\n                return _.isNumber(ttl) && now() > ttl ? true : false;\n            }\n        });\n        return PersistentStorage;\n        function now() {\n            return new Date().getTime();\n        }\n        function encode(val) {\n            return JSON.stringify(_.isUndefined(val) ? null : val);\n        }\n        function decode(val) {\n            return $.parseJSON(val);\n        }\n        function gatherMatchingKeys(keyMatcher) {\n            var i, key, keys = [], len = LOCAL_STORAGE.length;\n            for (i = 0; i < len; i++) {\n                if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {\n                    keys.push(key.replace(keyMatcher, \"\"));\n                }\n            }\n            return keys;\n        }\n    }();\n    var Transport = function() {\n        \"use strict\";\n        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);\n        function Transport(o) {\n            o = o || {};\n            this.cancelled = false;\n            this.lastReq = null;\n            this._send = o.transport;\n            this._get = o.limiter ? o.limiter(this._get) : this._get;\n            this._cache = o.cache === false ? new LruCache(0) : sharedCache;\n        }\n        Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {\n            maxPendingRequests = num;\n        };\n        Transport.resetCache = function resetCache() {\n            sharedCache.reset();\n        };\n        _.mixin(Transport.prototype, {\n            _fingerprint: function fingerprint(o) {\n                o = o || {};\n                return o.url + o.type + $.param(o.data || {});\n            },\n            _get: function(o, cb) {\n                var that = this, fingerprint, jqXhr;\n                fingerprint = this._fingerprint(o);\n                if (this.cancelled || fingerprint !== this.lastReq) {\n                    return;\n                }\n                if (jqXhr = pendingRequests[fingerprint]) {\n                    jqXhr.done(done).fail(fail);\n                } else if (pendingRequestsCount < maxPendingRequests) {\n                    pendingRequestsCount++;\n                    pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);\n                } else {\n                    this.onDeckRequestArgs = [].slice.call(arguments, 0);\n                }\n                function done(resp) {\n                    cb(null, resp);\n                    that._cache.set(fingerprint, resp);\n                }\n                function fail() {\n                    cb(true);\n                }\n                function always() {\n                    pendingRequestsCount--;\n                    delete pendingRequests[fingerprint];\n                    if (that.onDeckRequestArgs) {\n                        that._get.apply(that, that.onDeckRequestArgs);\n                        that.onDeckRequestArgs = null;\n                    }\n                }\n            },\n            get: function(o, cb) {\n                var resp, fingerprint;\n                cb = cb || $.noop;\n                o = _.isString(o) ? {\n                    url: o\n                } : o || {};\n                fingerprint = this._fingerprint(o);\n                this.cancelled = false;\n                this.lastReq = fingerprint;\n                if (resp = this._cache.get(fingerprint)) {\n                    cb(null, resp);\n                } else {\n                    this._get(o, cb);\n                }\n            },\n            cancel: function() {\n                this.cancelled = true;\n            }\n        });\n        return Transport;\n    }();\n    var SearchIndex = window.SearchIndex = function() {\n        \"use strict\";\n        var CHILDREN = \"c\", IDS = \"i\";\n        function SearchIndex(o) {\n            o = o || {};\n            if (!o.datumTokenizer || !o.queryTokenizer) {\n                $.error(\"datumTokenizer and queryTokenizer are both required\");\n            }\n            this.identify = o.identify || _.stringify;\n            this.datumTokenizer = o.datumTokenizer;\n            this.queryTokenizer = o.queryTokenizer;\n            this.reset();\n        }\n        _.mixin(SearchIndex.prototype, {\n            bootstrap: function bootstrap(o) {\n                this.datums = o.datums;\n                this.trie = o.trie;\n            },\n            add: function(data) {\n                var that = this;\n                data = _.isArray(data) ? data : [ data ];\n                _.each(data, function(datum) {\n                    var id, tokens;\n                    that.datums[id = that.identify(datum)] = datum;\n                    tokens = normalizeTokens(that.datumTokenizer(datum));\n                    _.each(tokens, function(token) {\n                        var node, chars, ch;\n                        node = that.trie;\n                        chars = token.split(\"\");\n                        while (ch = chars.shift()) {\n                            node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());\n                            node[IDS].push(id);\n                        }\n                    });\n                });\n            },\n            get: function get(ids) {\n                var that = this;\n                return _.map(ids, function(id) {\n                    return that.datums[id];\n                });\n            },\n            search: function search(query) {\n                var that = this, tokens, matches;\n                tokens = normalizeTokens(this.queryTokenizer(query));\n                _.each(tokens, function(token) {\n                    var node, chars, ch, ids;\n                    if (matches && matches.length === 0) {\n                        return false;\n                    }\n                    node = that.trie;\n                    chars = token.split(\"\");\n                    while (node && (ch = chars.shift())) {\n                        node = node[CHILDREN][ch];\n                    }\n                    if (node && chars.length === 0) {\n                        ids = node[IDS].slice(0);\n                        matches = matches ? getIntersection(matches, ids) : ids;\n                    } else {\n                        matches = [];\n                        return false;\n                    }\n                });\n                return matches ? _.map(unique(matches), function(id) {\n                    return that.datums[id];\n                }) : [];\n            },\n            all: function all() {\n                var values = [];\n                for (var key in this.datums) {\n                    values.push(this.datums[key]);\n                }\n                return values;\n            },\n            reset: function reset() {\n                this.datums = {};\n                this.trie = newNode();\n            },\n            serialize: function serialize() {\n                return {\n                    datums: this.datums,\n                    trie: this.trie\n                };\n            }\n        });\n        return SearchIndex;\n        function normalizeTokens(tokens) {\n            tokens = _.filter(tokens, function(token) {\n                return !!token;\n            });\n            tokens = _.map(tokens, function(token) {\n                return token.toLowerCase();\n            });\n            return tokens;\n        }\n        function newNode() {\n            var node = {};\n            node[IDS] = [];\n            node[CHILDREN] = {};\n            return node;\n        }\n        function unique(array) {\n            var seen = {}, uniques = [];\n            for (var i = 0, len = array.length; i < len; i++) {\n                if (!seen[array[i]]) {\n                    seen[array[i]] = true;\n                    uniques.push(array[i]);\n                }\n            }\n            return uniques;\n        }\n        function getIntersection(arrayA, arrayB) {\n            var ai = 0, bi = 0, intersection = [];\n            arrayA = arrayA.sort();\n            arrayB = arrayB.sort();\n            var lenArrayA = arrayA.length, lenArrayB = arrayB.length;\n            while (ai < lenArrayA && bi < lenArrayB) {\n                if (arrayA[ai] < arrayB[bi]) {\n                    ai++;\n                } else if (arrayA[ai] > arrayB[bi]) {\n                    bi++;\n                } else {\n                    intersection.push(arrayA[ai]);\n                    ai++;\n                    bi++;\n                }\n            }\n            return intersection;\n        }\n    }();\n    var Prefetch = function() {\n        \"use strict\";\n        var keys;\n        keys = {\n            data: \"data\",\n            protocol: \"protocol\",\n            thumbprint: \"thumbprint\"\n        };\n        function Prefetch(o) {\n            this.url = o.url;\n            this.ttl = o.ttl;\n            this.cache = o.cache;\n            this.prepare = o.prepare;\n            this.transform = o.transform;\n            this.transport = o.transport;\n            this.thumbprint = o.thumbprint;\n            this.storage = new PersistentStorage(o.cacheKey);\n        }\n        _.mixin(Prefetch.prototype, {\n            _settings: function settings() {\n                return {\n                    url: this.url,\n                    type: \"GET\",\n                    dataType: \"json\"\n                };\n            },\n            store: function store(data) {\n                if (!this.cache) {\n                    return;\n                }\n                this.storage.set(keys.data, data, this.ttl);\n                this.storage.set(keys.protocol, location.protocol, this.ttl);\n                this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);\n            },\n            fromCache: function fromCache() {\n                var stored = {}, isExpired;\n                if (!this.cache) {\n                    return null;\n                }\n                stored.data = this.storage.get(keys.data);\n                stored.protocol = this.storage.get(keys.protocol);\n                stored.thumbprint = this.storage.get(keys.thumbprint);\n                isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;\n                return stored.data && !isExpired ? stored.data : null;\n            },\n            fromNetwork: function(cb) {\n                var that = this, settings;\n                if (!cb) {\n                    return;\n                }\n                settings = this.prepare(this._settings());\n                this.transport(settings).fail(onError).done(onResponse);\n                function onError() {\n                    cb(true);\n                }\n                function onResponse(resp) {\n                    cb(null, that.transform(resp));\n                }\n            },\n            clear: function clear() {\n                this.storage.clear();\n                return this;\n            }\n        });\n        return Prefetch;\n    }();\n    var Remote = function() {\n        \"use strict\";\n        function Remote(o) {\n            this.url = o.url;\n            this.prepare = o.prepare;\n            this.transform = o.transform;\n            this.transport = new Transport({\n                cache: o.cache,\n                limiter: o.limiter,\n                transport: o.transport\n            });\n        }\n        _.mixin(Remote.prototype, {\n            _settings: function settings() {\n                return {\n                    url: this.url,\n                    type: \"GET\",\n                    dataType: \"json\"\n                };\n            },\n            get: function get(query, cb) {\n                var that = this, settings;\n                if (!cb) {\n                    return;\n                }\n                query = query || \"\";\n                settings = this.prepare(query, this._settings());\n                return this.transport.get(settings, onResponse);\n                function onResponse(err, resp) {\n                    err ? cb([]) : cb(that.transform(resp));\n                }\n            },\n            cancelLastRequest: function cancelLastRequest() {\n                this.transport.cancel();\n            }\n        });\n        return Remote;\n    }();\n    var oParser = function() {\n        \"use strict\";\n        return function parse(o) {\n            var defaults, sorter;\n            defaults = {\n                initialize: true,\n                identify: _.stringify,\n                datumTokenizer: null,\n                queryTokenizer: null,\n                sufficient: 5,\n                sorter: null,\n                local: [],\n                prefetch: null,\n                remote: null\n            };\n            o = _.mixin(defaults, o || {});\n            !o.datumTokenizer && $.error(\"datumTokenizer is required\");\n            !o.queryTokenizer && $.error(\"queryTokenizer is required\");\n            sorter = o.sorter;\n            o.sorter = sorter ? function(x) {\n                return x.sort(sorter);\n            } : _.identity;\n            o.local = _.isFunction(o.local) ? o.local() : o.local;\n            o.prefetch = parsePrefetch(o.prefetch);\n            o.remote = parseRemote(o.remote);\n            return o;\n        };\n        function parsePrefetch(o) {\n            var defaults;\n            if (!o) {\n                return null;\n            }\n            defaults = {\n                url: null,\n                ttl: 24 * 60 * 60 * 1e3,\n                cache: true,\n                cacheKey: null,\n                thumbprint: \"\",\n                prepare: _.identity,\n                transform: _.identity,\n                transport: null\n            };\n            o = _.isString(o) ? {\n                url: o\n            } : o;\n            o = _.mixin(defaults, o);\n            !o.url && $.error(\"prefetch requires url to be set\");\n            o.transform = o.filter || o.transform;\n            o.cacheKey = o.cacheKey || o.url;\n            o.thumbprint = VERSION + o.thumbprint;\n            o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;\n            return o;\n        }\n        function parseRemote(o) {\n            var defaults;\n            if (!o) {\n                return;\n            }\n            defaults = {\n                url: null,\n                cache: true,\n                prepare: null,\n                replace: null,\n                wildcard: null,\n                limiter: null,\n                rateLimitBy: \"debounce\",\n                rateLimitWait: 300,\n                transform: _.identity,\n                transport: null\n            };\n            o = _.isString(o) ? {\n                url: o\n            } : o;\n            o = _.mixin(defaults, o);\n            !o.url && $.error(\"remote requires url to be set\");\n            o.transform = o.filter || o.transform;\n            o.prepare = toRemotePrepare(o);\n            o.limiter = toLimiter(o);\n            o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;\n            delete o.replace;\n            delete o.wildcard;\n            delete o.rateLimitBy;\n            delete o.rateLimitWait;\n            return o;\n        }\n        function toRemotePrepare(o) {\n            var prepare, replace, wildcard;\n            prepare = o.prepare;\n            replace = o.replace;\n            wildcard = o.wildcard;\n            if (prepare) {\n                return prepare;\n            }\n            if (replace) {\n                prepare = prepareByReplace;\n            } else if (o.wildcard) {\n                prepare = prepareByWildcard;\n            } else {\n                prepare = idenityPrepare;\n            }\n            return prepare;\n            function prepareByReplace(query, settings) {\n                settings.url = replace(settings.url, query);\n                return settings;\n            }\n            function prepareByWildcard(query, settings) {\n                settings.url = settings.url.replace(wildcard, encodeURIComponent(query));\n                return settings;\n            }\n            function idenityPrepare(query, settings) {\n                return settings;\n            }\n        }\n        function toLimiter(o) {\n            var limiter, method, wait;\n            limiter = o.limiter;\n            method = o.rateLimitBy;\n            wait = o.rateLimitWait;\n            if (!limiter) {\n                limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);\n            }\n            return limiter;\n            function debounce(wait) {\n                return function debounce(fn) {\n                    return _.debounce(fn, wait);\n                };\n            }\n            function throttle(wait) {\n                return function throttle(fn) {\n                    return _.throttle(fn, wait);\n                };\n            }\n        }\n        function callbackToDeferred(fn) {\n            return function wrapper(o) {\n                var deferred = $.Deferred();\n                fn(o, onSuccess, onError);\n                return deferred;\n                function onSuccess(resp) {\n                    _.defer(function() {\n                        deferred.resolve(resp);\n                    });\n                }\n                function onError(err) {\n                    _.defer(function() {\n                        deferred.reject(err);\n                    });\n                }\n            };\n        }\n    }();\n    var Bloodhound = function() {\n        \"use strict\";\n        var old;\n        old = window && window.Bloodhound;\n        function Bloodhound(o) {\n            o = oParser(o);\n            this.sorter = o.sorter;\n            this.identify = o.identify;\n            this.sufficient = o.sufficient;\n            this.local = o.local;\n            this.remote = o.remote ? new Remote(o.remote) : null;\n            this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;\n            this.index = new SearchIndex({\n                identify: this.identify,\n                datumTokenizer: o.datumTokenizer,\n                queryTokenizer: o.queryTokenizer\n            });\n            o.initialize !== false && this.initialize();\n        }\n        Bloodhound.noConflict = function noConflict() {\n            window && (window.Bloodhound = old);\n            return Bloodhound;\n        };\n        Bloodhound.tokenizers = tokenizers;\n        _.mixin(Bloodhound.prototype, {\n            __ttAdapter: function ttAdapter() {\n                var that = this;\n                return this.remote ? withAsync : withoutAsync;\n                function withAsync(query, sync, async) {\n                    return that.search(query, sync, async);\n                }\n                function withoutAsync(query, sync) {\n                    return that.search(query, sync);\n                }\n            },\n            _loadPrefetch: function loadPrefetch() {\n                var that = this, deferred, serialized;\n                deferred = $.Deferred();\n                if (!this.prefetch) {\n                    deferred.resolve();\n                } else if (serialized = this.prefetch.fromCache()) {\n                    this.index.bootstrap(serialized);\n                    deferred.resolve();\n                } else {\n                    this.prefetch.fromNetwork(done);\n                }\n                return deferred.promise();\n                function done(err, data) {\n                    if (err) {\n                        return deferred.reject();\n                    }\n                    that.add(data);\n                    that.prefetch.store(that.index.serialize());\n                    deferred.resolve();\n                }\n            },\n            _initialize: function initialize() {\n                var that = this, deferred;\n                this.clear();\n                (this.initPromise = this._loadPrefetch()).done(addLocalToIndex);\n                return this.initPromise;\n                function addLocalToIndex() {\n                    that.add(that.local);\n                }\n            },\n            initialize: function initialize(force) {\n                return !this.initPromise || force ? this._initialize() : this.initPromise;\n            },\n            add: function add(data) {\n                this.index.add(data);\n                return this;\n            },\n            get: function get(ids) {\n                ids = _.isArray(ids) ? ids : [].slice.call(arguments);\n                return this.index.get(ids);\n            },\n            search: function search(query, sync, async) {\n                var that = this, local;\n                local = this.sorter(this.index.search(query));\n                sync(this.remote ? local.slice() : local);\n                if (this.remote && local.length < this.sufficient) {\n                    this.remote.get(query, processRemote);\n                } else if (this.remote) {\n                    this.remote.cancelLastRequest();\n                }\n                return this;\n                function processRemote(remote) {\n                    var nonDuplicates = [];\n                    _.each(remote, function(r) {\n                        !_.some(local, function(l) {\n                            return that.identify(r) === that.identify(l);\n                        }) && nonDuplicates.push(r);\n                    });\n                    async && async(nonDuplicates);\n                }\n            },\n            all: function all() {\n                return this.index.all();\n            },\n            clear: function clear() {\n                this.index.reset();\n                return this;\n            },\n            clearPrefetchCache: function clearPrefetchCache() {\n                this.prefetch && this.prefetch.clear();\n                return this;\n            },\n            clearRemoteCache: function clearRemoteCache() {\n                Transport.resetCache();\n                return this;\n            },\n            ttAdapter: function ttAdapter() {\n                return this.__ttAdapter();\n            }\n        });\n        return Bloodhound;\n    }();\n    return Bloodhound;\n});\n\n(function(root, factory) {\n    if (typeof define === \"function\" && define.amd) {\n        define(\"typeahead.js\", [ \"jquery\" ], function(a0) {\n            return factory(a0);\n        });\n    } else if (typeof exports === \"object\") {\n        module.exports = factory(require(\"jquery\"));\n    } else {\n        factory(jQuery);\n    }\n})(this, function($) {\n    var _ = function() {\n        \"use strict\";\n        return {\n            isMsie: function() {\n                return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\\d+(.\\d+)?)/i)[2] : false;\n            },\n            isBlankString: function(str) {\n                return !str || /^\\s*$/.test(str);\n            },\n            escapeRegExChars: function(str) {\n                return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n            },\n            isString: function(obj) {\n                return typeof obj === \"string\";\n            },\n            isNumber: function(obj) {\n                return typeof obj === \"number\";\n            },\n            isArray: $.isArray,\n            isFunction: $.isFunction,\n            isObject: $.isPlainObject,\n            isUndefined: function(obj) {\n                return typeof obj === \"undefined\";\n            },\n            isElement: function(obj) {\n                return !!(obj && obj.nodeType === 1);\n            },\n            isJQuery: function(obj) {\n                return obj instanceof $;\n            },\n            toStr: function toStr(s) {\n                return _.isUndefined(s) || s === null ? \"\" : s + \"\";\n            },\n            bind: $.proxy,\n            each: function(collection, cb) {\n                $.each(collection, reverseArgs);\n                function reverseArgs(index, value) {\n                    return cb(value, index);\n                }\n            },\n            map: $.map,\n            filter: $.grep,\n            every: function(obj, test) {\n                var result = true;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (!(result = test.call(null, val, key, obj))) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            some: function(obj, test) {\n                var result = false;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (result = test.call(null, val, key, obj)) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            mixin: $.extend,\n            identity: function(x) {\n                return x;\n            },\n            clone: function(obj) {\n                return $.extend(true, {}, obj);\n            },\n            getIdGenerator: function() {\n                var counter = 0;\n                return function() {\n                    return counter++;\n                };\n            },\n            templatify: function templatify(obj) {\n                return $.isFunction(obj) ? obj : template;\n                function template() {\n                    return String(obj);\n                }\n            },\n            defer: function(fn) {\n                setTimeout(fn, 0);\n            },\n            debounce: function(func, wait, immediate) {\n                var timeout, result;\n                return function() {\n                    var context = this, args = arguments, later, callNow;\n                    later = function() {\n                        timeout = null;\n                        if (!immediate) {\n                            result = func.apply(context, args);\n                        }\n                    };\n                    callNow = immediate && !timeout;\n                    clearTimeout(timeout);\n                    timeout = setTimeout(later, wait);\n                    if (callNow) {\n                        result = func.apply(context, args);\n                    }\n                    return result;\n                };\n            },\n            throttle: function(func, wait) {\n                var context, args, timeout, result, previous, later;\n                previous = 0;\n                later = function() {\n                    previous = new Date();\n                    timeout = null;\n                    result = func.apply(context, args);\n                };\n                return function() {\n                    var now = new Date(), remaining = wait - (now - previous);\n                    context = this;\n                    args = arguments;\n                    if (remaining <= 0) {\n                        clearTimeout(timeout);\n                        timeout = null;\n                        previous = now;\n                        result = func.apply(context, args);\n                    } else if (!timeout) {\n                        timeout = setTimeout(later, remaining);\n                    }\n                    return result;\n                };\n            },\n            stringify: function(val) {\n                return _.isString(val) ? val : JSON.stringify(val);\n            },\n            noop: function() {}\n        };\n    }();\n    var WWW = function() {\n        \"use strict\";\n        var defaultClassNames = {\n            wrapper: \"twitter-typeahead\",\n            input: \"tt-input\",\n            hint: \"tt-hint\",\n            menu: \"tt-menu\",\n            dataset: \"tt-dataset\",\n            suggestion: \"tt-suggestion\",\n            selectable: \"tt-selectable\",\n            empty: \"tt-empty\",\n            open: \"tt-open\",\n            cursor: \"tt-cursor\",\n            highlight: \"tt-highlight\"\n        };\n        return build;\n        function build(o) {\n            var www, classes;\n            classes = _.mixin({}, defaultClassNames, o);\n            www = {\n                css: buildCss(),\n                classes: classes,\n                html: buildHtml(classes),\n                selectors: buildSelectors(classes)\n            };\n            return {\n                css: www.css,\n                html: www.html,\n                classes: www.classes,\n                selectors: www.selectors,\n                mixin: function(o) {\n                    _.mixin(o, www);\n                }\n            };\n        }\n        function buildHtml(c) {\n            return {\n                wrapper: '<span class=\"' + c.wrapper + '\"></span>',\n                menu: '<div class=\"' + c.menu + '\"></div>'\n            };\n        }\n        function buildSelectors(classes) {\n            var selectors = {};\n            _.each(classes, function(v, k) {\n                selectors[k] = \".\" + v;\n            });\n            return selectors;\n        }\n        function buildCss() {\n            var css = {\n                wrapper: {\n                    position: \"relative\",\n                    display: \"inline-block\"\n                },\n                hint: {\n                    position: \"absolute\",\n                    top: \"0\",\n                    left: \"0\",\n                    borderColor: \"transparent\",\n                    boxShadow: \"none\",\n                    opacity: \"1\"\n                },\n                input: {\n                    position: \"relative\",\n                    verticalAlign: \"top\",\n                    backgroundColor: \"transparent\"\n                },\n                inputWithNoHint: {\n                    position: \"relative\",\n                    verticalAlign: \"top\"\n                },\n                menu: {\n                    position: \"absolute\",\n                    top: \"100%\",\n                    left: \"0\",\n                    zIndex: \"100\",\n                    display: \"none\"\n                },\n                ltr: {\n                    left: \"0\",\n                    right: \"auto\"\n                },\n                rtl: {\n                    left: \"auto\",\n                    right: \" 0\"\n                }\n            };\n            if (_.isMsie()) {\n                _.mixin(css.input, {\n                    backgroundImage: \"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)\"\n                });\n            }\n            return css;\n        }\n    }();\n    var EventBus = function() {\n        \"use strict\";\n        var namespace, deprecationMap;\n        namespace = \"typeahead:\";\n        deprecationMap = {\n            render: \"rendered\",\n            cursorchange: \"cursorchanged\",\n            select: \"selected\",\n            autocomplete: \"autocompleted\"\n        };\n        function EventBus(o) {\n            if (!o || !o.el) {\n                $.error(\"EventBus initialized without el\");\n            }\n            this.$el = $(o.el);\n        }\n        _.mixin(EventBus.prototype, {\n            _trigger: function(type, args) {\n                var $e;\n                $e = $.Event(namespace + type);\n                (args = args || []).unshift($e);\n                this.$el.trigger.apply(this.$el, args);\n                return $e;\n            },\n            before: function(type) {\n                var args, $e;\n                args = [].slice.call(arguments, 1);\n                $e = this._trigger(\"before\" + type, args);\n                return $e.isDefaultPrevented();\n            },\n            trigger: function(type) {\n                var deprecatedType;\n                this._trigger(type, [].slice.call(arguments, 1));\n                if (deprecatedType = deprecationMap[type]) {\n                    this._trigger(deprecatedType, [].slice.call(arguments, 1));\n                }\n            }\n        });\n        return EventBus;\n    }();\n    var EventEmitter = function() {\n        \"use strict\";\n        var splitter = /\\s+/, nextTick = getNextTick();\n        return {\n            onSync: onSync,\n            onAsync: onAsync,\n            off: off,\n            trigger: trigger\n        };\n        function on(method, types, cb, context) {\n            var type;\n            if (!cb) {\n                return this;\n            }\n            types = types.split(splitter);\n            cb = context ? bindContext(cb, context) : cb;\n            this._callbacks = this._callbacks || {};\n            while (type = types.shift()) {\n                this._callbacks[type] = this._callbacks[type] || {\n                    sync: [],\n                    async: []\n                };\n                this._callbacks[type][method].push(cb);\n            }\n            return this;\n        }\n        function onAsync(types, cb, context) {\n            return on.call(this, \"async\", types, cb, context);\n        }\n        function onSync(types, cb, context) {\n            return on.call(this, \"sync\", types, cb, context);\n        }\n        function off(types) {\n            var type;\n            if (!this._callbacks) {\n                return this;\n            }\n            types = types.split(splitter);\n            while (type = types.shift()) {\n                delete this._callbacks[type];\n            }\n            return this;\n        }\n        function trigger(types) {\n            var type, callbacks, args, syncFlush, asyncFlush;\n            if (!this._callbacks) {\n                return this;\n            }\n            types = types.split(splitter);\n            args = [].slice.call(arguments, 1);\n            while ((type = types.shift()) && (callbacks = this._callbacks[type])) {\n                syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));\n                asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));\n                syncFlush() && nextTick(asyncFlush);\n            }\n            return this;\n        }\n        function getFlush(callbacks, context, args) {\n            return flush;\n            function flush() {\n                var cancelled;\n                for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {\n                    cancelled = callbacks[i].apply(context, args) === false;\n                }\n                return !cancelled;\n            }\n        }\n        function getNextTick() {\n            var nextTickFn;\n            if (window.setImmediate) {\n                nextTickFn = function nextTickSetImmediate(fn) {\n                    setImmediate(function() {\n                        fn();\n                    });\n                };\n            } else {\n                nextTickFn = function nextTickSetTimeout(fn) {\n                    setTimeout(function() {\n                        fn();\n                    }, 0);\n                };\n            }\n            return nextTickFn;\n        }\n        function bindContext(fn, context) {\n            return fn.bind ? fn.bind(context) : function() {\n                fn.apply(context, [].slice.call(arguments, 0));\n            };\n        }\n    }();\n    var highlight = function(doc) {\n        \"use strict\";\n        var defaults = {\n            node: null,\n            pattern: null,\n            tagName: \"strong\",\n            className: null,\n            wordsOnly: false,\n            caseSensitive: false\n        };\n        return function hightlight(o) {\n            var regex;\n            o = _.mixin({}, defaults, o);\n            if (!o.node || !o.pattern) {\n                return;\n            }\n            o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];\n            regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);\n            traverse(o.node, hightlightTextNode);\n            function hightlightTextNode(textNode) {\n                var match, patternNode, wrapperNode;\n                if (match = regex.exec(textNode.data)) {\n                    wrapperNode = doc.createElement(o.tagName);\n                    o.className && (wrapperNode.className = o.className);\n                    patternNode = textNode.splitText(match.index);\n                    patternNode.splitText(match[0].length);\n                    wrapperNode.appendChild(patternNode.cloneNode(true));\n                    textNode.parentNode.replaceChild(wrapperNode, patternNode);\n                }\n                return !!match;\n            }\n            function traverse(el, hightlightTextNode) {\n                var childNode, TEXT_NODE_TYPE = 3;\n                for (var i = 0; i < el.childNodes.length; i++) {\n                    childNode = el.childNodes[i];\n                    if (childNode.nodeType === TEXT_NODE_TYPE) {\n                        i += hightlightTextNode(childNode) ? 1 : 0;\n                    } else {\n                        traverse(childNode, hightlightTextNode);\n                    }\n                }\n            }\n        };\n        function getRegex(patterns, caseSensitive, wordsOnly) {\n            var escapedPatterns = [], regexStr;\n            for (var i = 0, len = patterns.length; i < len; i++) {\n                escapedPatterns.push(_.escapeRegExChars(patterns[i]));\n            }\n            regexStr = wordsOnly ? \"\\\\b(\" + escapedPatterns.join(\"|\") + \")\\\\b\" : \"(\" + escapedPatterns.join(\"|\") + \")\";\n            return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, \"i\");\n        }\n    }(window.document);\n    var Input = function() {\n        \"use strict\";\n        var specialKeyCodeMap;\n        specialKeyCodeMap = {\n            9: \"tab\",\n            27: \"esc\",\n            37: \"left\",\n            39: \"right\",\n            13: \"enter\",\n            38: \"up\",\n            40: \"down\"\n        };\n        function Input(o, www) {\n            o = o || {};\n            if (!o.input) {\n                $.error(\"input is missing\");\n            }\n            www.mixin(this);\n            this.$hint = $(o.hint);\n            this.$input = $(o.input);\n            this.query = this.$input.val();\n            this.queryWhenFocused = this.hasFocus() ? this.query : null;\n            this.$overflowHelper = buildOverflowHelper(this.$input);\n            this._checkLanguageDirection();\n            if (this.$hint.length === 0) {\n                this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;\n            }\n        }\n        Input.normalizeQuery = function(str) {\n            return _.toStr(str).replace(/^\\s*/g, \"\").replace(/\\s{2,}/g, \" \");\n        };\n        _.mixin(Input.prototype, EventEmitter, {\n            _onBlur: function onBlur() {\n                this.resetInputValue();\n                this.trigger(\"blurred\");\n            },\n            _onFocus: function onFocus() {\n                this.queryWhenFocused = this.query;\n                this.trigger(\"focused\");\n            },\n            _onKeydown: function onKeydown($e) {\n                var keyName = specialKeyCodeMap[$e.which || $e.keyCode];\n                this._managePreventDefault(keyName, $e);\n                if (keyName && this._shouldTrigger(keyName, $e)) {\n                    this.trigger(keyName + \"Keyed\", $e);\n                }\n            },\n            _onInput: function onInput() {\n                this._setQuery(this.getInputValue());\n                this.clearHintIfInvalid();\n                this._checkLanguageDirection();\n            },\n            _managePreventDefault: function managePreventDefault(keyName, $e) {\n                var preventDefault;\n                switch (keyName) {\n                  case \"up\":\n                  case \"down\":\n                    preventDefault = !withModifier($e);\n                    break;\n\n                  default:\n                    preventDefault = false;\n                }\n                preventDefault && $e.preventDefault();\n            },\n            _shouldTrigger: function shouldTrigger(keyName, $e) {\n                var trigger;\n                switch (keyName) {\n                  case \"tab\":\n                    trigger = !withModifier($e);\n                    break;\n\n                  default:\n                    trigger = true;\n                }\n                return trigger;\n            },\n            _checkLanguageDirection: function checkLanguageDirection() {\n                var dir = (this.$input.css(\"direction\") || \"ltr\").toLowerCase();\n                if (this.dir !== dir) {\n                    this.dir = dir;\n                    this.$hint.attr(\"dir\", dir);\n                    this.trigger(\"langDirChanged\", dir);\n                }\n            },\n            _setQuery: function setQuery(val, silent) {\n                var areEquivalent, hasDifferentWhitespace;\n                areEquivalent = areQueriesEquivalent(val, this.query);\n                hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;\n                this.query = val;\n                if (!silent && !areEquivalent) {\n                    this.trigger(\"queryChanged\", this.query);\n                } else if (!silent && hasDifferentWhitespace) {\n                    this.trigger(\"whitespaceChanged\", this.query);\n                }\n            },\n            bind: function() {\n                var that = this, onBlur, onFocus, onKeydown, onInput;\n                onBlur = _.bind(this._onBlur, this);\n                onFocus = _.bind(this._onFocus, this);\n                onKeydown = _.bind(this._onKeydown, this);\n                onInput = _.bind(this._onInput, this);\n                this.$input.on(\"blur.tt\", onBlur).on(\"focus.tt\", onFocus).on(\"keydown.tt\", onKeydown);\n                if (!_.isMsie() || _.isMsie() > 9) {\n                    this.$input.on(\"input.tt\", onInput);\n                } else {\n                    this.$input.on(\"keydown.tt keypress.tt cut.tt paste.tt\", function($e) {\n                        if (specialKeyCodeMap[$e.which || $e.keyCode]) {\n                            return;\n                        }\n                        _.defer(_.bind(that._onInput, that, $e));\n                    });\n                }\n                return this;\n            },\n            focus: function focus() {\n                this.$input.focus();\n            },\n            blur: function blur() {\n                this.$input.blur();\n            },\n            getLangDir: function getLangDir() {\n                return this.dir;\n            },\n            getQuery: function getQuery() {\n                return this.query || \"\";\n            },\n            setQuery: function setQuery(val, silent) {\n                this.setInputValue(val);\n                this._setQuery(val, silent);\n            },\n            hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {\n                return this.query !== this.queryWhenFocused;\n            },\n            getInputValue: function getInputValue() {\n                return this.$input.val();\n            },\n            setInputValue: function setInputValue(value) {\n                this.$input.val(value);\n                this.clearHintIfInvalid();\n                this._checkLanguageDirection();\n            },\n            resetInputValue: function resetInputValue() {\n                this.setInputValue(this.query);\n            },\n            getHint: function getHint() {\n                return this.$hint.val();\n            },\n            setHint: function setHint(value) {\n                this.$hint.val(value);\n            },\n            clearHint: function clearHint() {\n                this.setHint(\"\");\n            },\n            clearHintIfInvalid: function clearHintIfInvalid() {\n                var val, hint, valIsPrefixOfHint, isValid;\n                val = this.getInputValue();\n                hint = this.getHint();\n                valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;\n                isValid = val !== \"\" && valIsPrefixOfHint && !this.hasOverflow();\n                !isValid && this.clearHint();\n            },\n            hasFocus: function hasFocus() {\n                return this.$input.is(\":focus\");\n            },\n            hasOverflow: function hasOverflow() {\n                var constraint = this.$input.width() - 2;\n                this.$overflowHelper.text(this.getInputValue());\n                return this.$overflowHelper.width() >= constraint;\n            },\n            isCursorAtEnd: function() {\n                var valueLength, selectionStart, range;\n                valueLength = this.$input.val().length;\n                selectionStart = this.$input[0].selectionStart;\n                if (_.isNumber(selectionStart)) {\n                    return selectionStart === valueLength;\n                } else if (document.selection) {\n                    range = document.selection.createRange();\n                    range.moveStart(\"character\", -valueLength);\n                    return valueLength === range.text.length;\n                }\n                return true;\n            },\n            destroy: function destroy() {\n                this.$hint.off(\".tt\");\n                this.$input.off(\".tt\");\n                this.$overflowHelper.remove();\n                this.$hint = this.$input = this.$overflowHelper = $(\"<div>\");\n            }\n        });\n        return Input;\n        function buildOverflowHelper($input) {\n            return $('<pre aria-hidden=\"true\"></pre>').css({\n                position: \"absolute\",\n                visibility: \"hidden\",\n                whiteSpace: \"pre\",\n                fontFamily: $input.css(\"font-family\"),\n                fontSize: $input.css(\"font-size\"),\n                fontStyle: $input.css(\"font-style\"),\n                fontVariant: $input.css(\"font-variant\"),\n                fontWeight: $input.css(\"font-weight\"),\n                wordSpacing: $input.css(\"word-spacing\"),\n                letterSpacing: $input.css(\"letter-spacing\"),\n                textIndent: $input.css(\"text-indent\"),\n                textRendering: $input.css(\"text-rendering\"),\n                textTransform: $input.css(\"text-transform\")\n            }).insertAfter($input);\n        }\n        function areQueriesEquivalent(a, b) {\n            return Input.normalizeQuery(a) === Input.normalizeQuery(b);\n        }\n        function withModifier($e) {\n            return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;\n        }\n    }();\n    var Dataset = function() {\n        \"use strict\";\n        var keys, nameGenerator;\n        keys = {\n            val: \"tt-selectable-display\",\n            obj: \"tt-selectable-object\"\n        };\n        nameGenerator = _.getIdGenerator();\n        function Dataset(o, www) {\n            o = o || {};\n            o.templates = o.templates || {};\n            o.templates.notFound = o.templates.notFound || o.templates.empty;\n            if (!o.source) {\n                $.error(\"missing source\");\n            }\n            if (!o.node) {\n                $.error(\"missing node\");\n            }\n            if (o.name && !isValidName(o.name)) {\n                $.error(\"invalid dataset name: \" + o.name);\n            }\n            www.mixin(this);\n            this.highlight = !!o.highlight;\n            this.name = o.name || nameGenerator();\n            this.limit = o.limit || 5;\n            this.displayFn = getDisplayFn(o.display || o.displayKey);\n            this.templates = getTemplates(o.templates, this.displayFn);\n            this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;\n            this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;\n            this._resetLastSuggestion();\n            this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + \"-\" + this.name);\n        }\n        Dataset.extractData = function extractData(el) {\n            var $el = $(el);\n            if ($el.data(keys.obj)) {\n                return {\n                    val: $el.data(keys.val) || \"\",\n                    obj: $el.data(keys.obj) || null\n                };\n            }\n            return null;\n        };\n        _.mixin(Dataset.prototype, EventEmitter, {\n            _overwrite: function overwrite(query, suggestions) {\n                suggestions = suggestions || [];\n                if (suggestions.length) {\n                    this._renderSuggestions(query, suggestions);\n                } else if (this.async && this.templates.pending) {\n                    this._renderPending(query);\n                } else if (!this.async && this.templates.notFound) {\n                    this._renderNotFound(query);\n                } else {\n                    this._empty();\n                }\n                this.trigger(\"rendered\", this.name, suggestions, false);\n            },\n            _append: function append(query, suggestions) {\n                suggestions = suggestions || [];\n                if (suggestions.length && this.$lastSuggestion.length) {\n                    this._appendSuggestions(query, suggestions);\n                } else if (suggestions.length) {\n                    this._renderSuggestions(query, suggestions);\n                } else if (!this.$lastSuggestion.length && this.templates.notFound) {\n                    this._renderNotFound(query);\n                }\n                this.trigger(\"rendered\", this.name, suggestions, true);\n            },\n            _renderSuggestions: function renderSuggestions(query, suggestions) {\n                var $fragment;\n                $fragment = this._getSuggestionsFragment(query, suggestions);\n                this.$lastSuggestion = $fragment.children().last();\n                this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions));\n            },\n            _appendSuggestions: function appendSuggestions(query, suggestions) {\n                var $fragment, $lastSuggestion;\n                $fragment = this._getSuggestionsFragment(query, suggestions);\n                $lastSuggestion = $fragment.children().last();\n                this.$lastSuggestion.after($fragment);\n                this.$lastSuggestion = $lastSuggestion;\n            },\n            _renderPending: function renderPending(query) {\n                var template = this.templates.pending;\n                this._resetLastSuggestion();\n                template && this.$el.html(template({\n                    query: query,\n                    dataset: this.name\n                }));\n            },\n            _renderNotFound: function renderNotFound(query) {\n                var template = this.templates.notFound;\n                this._resetLastSuggestion();\n                template && this.$el.html(template({\n                    query: query,\n                    dataset: this.name\n                }));\n            },\n            _empty: function empty() {\n                this.$el.empty();\n                this._resetLastSuggestion();\n            },\n            _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {\n                var that = this, fragment;\n                fragment = document.createDocumentFragment();\n                _.each(suggestions, function getSuggestionNode(suggestion) {\n                    var $el, context;\n                    context = that._injectQuery(query, suggestion);\n                    $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + \" \" + that.classes.selectable);\n                    fragment.appendChild($el[0]);\n                });\n                this.highlight && highlight({\n                    className: this.classes.highlight,\n                    node: fragment,\n                    pattern: query\n                });\n                return $(fragment);\n            },\n            _getFooter: function getFooter(query, suggestions) {\n                return this.templates.footer ? this.templates.footer({\n                    query: query,\n                    suggestions: suggestions,\n                    dataset: this.name\n                }) : null;\n            },\n            _getHeader: function getHeader(query, suggestions) {\n                return this.templates.header ? this.templates.header({\n                    query: query,\n                    suggestions: suggestions,\n                    dataset: this.name\n                }) : null;\n            },\n            _resetLastSuggestion: function resetLastSuggestion() {\n                this.$lastSuggestion = $();\n            },\n            _injectQuery: function injectQuery(query, obj) {\n                return _.isObject(obj) ? _.mixin({\n                    _query: query\n                }, obj) : obj;\n            },\n            update: function update(query) {\n                var that = this, canceled = false, syncCalled = false, rendered = 0;\n                this.cancel();\n                this.cancel = function cancel() {\n                    canceled = true;\n                    that.cancel = $.noop;\n                    that.async && that.trigger(\"asyncCanceled\", query);\n                };\n                this.source(query, sync, async);\n                !syncCalled && sync([]);\n                function sync(suggestions) {\n                    if (syncCalled) {\n                        return;\n                    }\n                    syncCalled = true;\n                    suggestions = (suggestions || []).slice(0, that.limit);\n                    rendered = suggestions.length;\n                    that._overwrite(query, suggestions);\n                    if (rendered < that.limit && that.async) {\n                        that.trigger(\"asyncRequested\", query);\n                    }\n                }\n                function async(suggestions) {\n                    suggestions = suggestions || [];\n                    if (!canceled && rendered < that.limit) {\n                        that.cancel = $.noop;\n                        rendered += suggestions.length;\n                        that._append(query, suggestions.slice(0, that.limit - rendered));\n                        that.async && that.trigger(\"asyncReceived\", query);\n                    }\n                }\n            },\n            cancel: $.noop,\n            clear: function clear() {\n                this._empty();\n                this.cancel();\n                this.trigger(\"cleared\");\n            },\n            isEmpty: function isEmpty() {\n                return this.$el.is(\":empty\");\n            },\n            destroy: function destroy() {\n                this.$el = $(\"<div>\");\n            }\n        });\n        return Dataset;\n        function getDisplayFn(display) {\n            display = display || _.stringify;\n            return _.isFunction(display) ? display : displayFn;\n            function displayFn(obj) {\n                return obj[display];\n            }\n        }\n        function getTemplates(templates, displayFn) {\n            return {\n                notFound: templates.notFound && _.templatify(templates.notFound),\n                pending: templates.pending && _.templatify(templates.pending),\n                header: templates.header && _.templatify(templates.header),\n                footer: templates.footer && _.templatify(templates.footer),\n                suggestion: templates.suggestion || suggestionTemplate\n            };\n            function suggestionTemplate(context) {\n                return $(\"<div>\").text(displayFn(context));\n            }\n        }\n        function isValidName(str) {\n            return /^[_a-zA-Z0-9-]+$/.test(str);\n        }\n    }();\n    var Menu = function() {\n        \"use strict\";\n        function Menu(o, www) {\n            var that = this;\n            o = o || {};\n            if (!o.node) {\n                $.error(\"node is required\");\n            }\n            www.mixin(this);\n            this.$node = $(o.node);\n            this.query = null;\n            this.datasets = _.map(o.datasets, initializeDataset);\n            function initializeDataset(oDataset) {\n                var node = that.$node.find(oDataset.node).first();\n                oDataset.node = node.length ? node : $(\"<div>\").appendTo(that.$node);\n                return new Dataset(oDataset, www);\n            }\n        }\n        _.mixin(Menu.prototype, EventEmitter, {\n            _onSelectableClick: function onSelectableClick($e) {\n                this.trigger(\"selectableClicked\", $($e.currentTarget));\n            },\n            _onRendered: function onRendered(type, dataset, suggestions, async) {\n                this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());\n                this.trigger(\"datasetRendered\", dataset, suggestions, async);\n            },\n            _onCleared: function onCleared() {\n                this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());\n                this.trigger(\"datasetCleared\");\n            },\n            _propagate: function propagate() {\n                this.trigger.apply(this, arguments);\n            },\n            _allDatasetsEmpty: function allDatasetsEmpty() {\n                return _.every(this.datasets, isDatasetEmpty);\n                function isDatasetEmpty(dataset) {\n                    return dataset.isEmpty();\n                }\n            },\n            _getSelectables: function getSelectables() {\n                return this.$node.find(this.selectors.selectable);\n            },\n            _removeCursor: function _removeCursor() {\n                var $selectable = this.getActiveSelectable();\n                $selectable && $selectable.removeClass(this.classes.cursor);\n            },\n            _ensureVisible: function ensureVisible($el) {\n                var elTop, elBottom, nodeScrollTop, nodeHeight;\n                elTop = $el.position().top;\n                elBottom = elTop + $el.outerHeight(true);\n                nodeScrollTop = this.$node.scrollTop();\n                nodeHeight = this.$node.height() + parseInt(this.$node.css(\"paddingTop\"), 10) + parseInt(this.$node.css(\"paddingBottom\"), 10);\n                if (elTop < 0) {\n                    this.$node.scrollTop(nodeScrollTop + elTop);\n                } else if (nodeHeight < elBottom) {\n                    this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));\n                }\n            },\n            bind: function() {\n                var that = this, onSelectableClick;\n                onSelectableClick = _.bind(this._onSelectableClick, this);\n                this.$node.on(\"click.tt\", this.selectors.selectable, onSelectableClick);\n                _.each(this.datasets, function(dataset) {\n                    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);\n                });\n                return this;\n            },\n            isOpen: function isOpen() {\n                return this.$node.hasClass(this.classes.open);\n            },\n            open: function open() {\n                this.$node.addClass(this.classes.open);\n            },\n            close: function close() {\n                this.$node.removeClass(this.classes.open);\n                this._removeCursor();\n            },\n            setLanguageDirection: function setLanguageDirection(dir) {\n                this.$node.attr(\"dir\", dir);\n            },\n            selectableRelativeToCursor: function selectableRelativeToCursor(delta) {\n                var $selectables, $oldCursor, oldIndex, newIndex;\n                $oldCursor = this.getActiveSelectable();\n                $selectables = this._getSelectables();\n                oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;\n                newIndex = oldIndex + delta;\n                newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;\n                newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;\n                return newIndex === -1 ? null : $selectables.eq(newIndex);\n            },\n            setCursor: function setCursor($selectable) {\n                this._removeCursor();\n                if ($selectable = $selectable && $selectable.first()) {\n                    $selectable.addClass(this.classes.cursor);\n                    this._ensureVisible($selectable);\n                }\n            },\n            getSelectableData: function getSelectableData($el) {\n                return $el && $el.length ? Dataset.extractData($el) : null;\n            },\n            getActiveSelectable: function getActiveSelectable() {\n                var $selectable = this._getSelectables().filter(this.selectors.cursor).first();\n                return $selectable.length ? $selectable : null;\n            },\n            getTopSelectable: function getTopSelectable() {\n                var $selectable = this._getSelectables().first();\n                return $selectable.length ? $selectable : null;\n            },\n            update: function update(query) {\n                var isValidUpdate = query !== this.query;\n                if (isValidUpdate) {\n                    this.query = query;\n                    _.each(this.datasets, updateDataset);\n                }\n                return isValidUpdate;\n                function updateDataset(dataset) {\n                    dataset.update(query);\n                }\n            },\n            empty: function empty() {\n                _.each(this.datasets, clearDataset);\n                this.query = null;\n                this.$node.addClass(this.classes.empty);\n                function clearDataset(dataset) {\n                    dataset.clear();\n                }\n            },\n            destroy: function destroy() {\n                this.$node.off(\".tt\");\n                this.$node = $(\"<div>\");\n                _.each(this.datasets, destroyDataset);\n                function destroyDataset(dataset) {\n                    dataset.destroy();\n                }\n            }\n        });\n        return Menu;\n    }();\n    var DefaultMenu = function() {\n        \"use strict\";\n        var s = Menu.prototype;\n        function DefaultMenu() {\n            Menu.apply(this, [].slice.call(arguments, 0));\n        }\n        _.mixin(DefaultMenu.prototype, Menu.prototype, {\n            open: function open() {\n                !this._allDatasetsEmpty() && this._show();\n                return s.open.apply(this, [].slice.call(arguments, 0));\n            },\n            close: function close() {\n                this._hide();\n                return s.close.apply(this, [].slice.call(arguments, 0));\n            },\n            _onRendered: function onRendered() {\n                if (this._allDatasetsEmpty()) {\n                    this._hide();\n                } else {\n                    this.isOpen() && this._show();\n                }\n                return s._onRendered.apply(this, [].slice.call(arguments, 0));\n            },\n            _onCleared: function onCleared() {\n                if (this._allDatasetsEmpty()) {\n                    this._hide();\n                } else {\n                    this.isOpen() && this._show();\n                }\n                return s._onCleared.apply(this, [].slice.call(arguments, 0));\n            },\n            setLanguageDirection: function setLanguageDirection(dir) {\n                this.$node.css(dir === \"ltr\" ? this.css.ltr : this.css.rtl);\n                return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));\n            },\n            _hide: function hide() {\n                this.$node.hide();\n            },\n            _show: function show() {\n                this.$node.css(\"display\", \"block\");\n            }\n        });\n        return DefaultMenu;\n    }();\n    var Typeahead = function() {\n        \"use strict\";\n        function Typeahead(o, www) {\n            var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged;\n            o = o || {};\n            if (!o.input) {\n                $.error(\"missing input\");\n            }\n            if (!o.menu) {\n                $.error(\"missing menu\");\n            }\n            if (!o.eventBus) {\n                $.error(\"missing event bus\");\n            }\n            www.mixin(this);\n            this.eventBus = o.eventBus;\n            this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;\n            this.input = o.input;\n            this.menu = o.menu;\n            this.enabled = true;\n            this.active = false;\n            this.input.hasFocus() && this.activate();\n            this.dir = this.input.getLangDir();\n            this._hacks();\n            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);\n            onFocused = c(this, \"activate\", \"open\", \"_onFocused\");\n            onBlurred = c(this, \"deactivate\", \"_onBlurred\");\n            onEnterKeyed = c(this, \"isActive\", \"isOpen\", \"_onEnterKeyed\");\n            onTabKeyed = c(this, \"isActive\", \"isOpen\", \"_onTabKeyed\");\n            onEscKeyed = c(this, \"isActive\", \"_onEscKeyed\");\n            onUpKeyed = c(this, \"isActive\", \"open\", \"_onUpKeyed\");\n            onDownKeyed = c(this, \"isActive\", \"open\", \"_onDownKeyed\");\n            onLeftKeyed = c(this, \"isActive\", \"isOpen\", \"_onLeftKeyed\");\n            onRightKeyed = c(this, \"isActive\", \"isOpen\", \"_onRightKeyed\");\n            onQueryChanged = c(this, \"_openIfActive\", \"_onQueryChanged\");\n            onWhitespaceChanged = c(this, \"_openIfActive\", \"_onWhitespaceChanged\");\n            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);\n        }\n        _.mixin(Typeahead.prototype, {\n            _hacks: function hacks() {\n                var $input, $menu;\n                $input = this.input.$input || $(\"<div>\");\n                $menu = this.menu.$node || $(\"<div>\");\n                $input.on(\"blur.tt\", function($e) {\n                    var active, isActive, hasActive;\n                    active = document.activeElement;\n                    isActive = $menu.is(active);\n                    hasActive = $menu.has(active).length > 0;\n                    if (_.isMsie() && (isActive || hasActive)) {\n                        $e.preventDefault();\n                        $e.stopImmediatePropagation();\n                        _.defer(function() {\n                            $input.focus();\n                        });\n                    }\n                });\n                $menu.on(\"mousedown.tt\", function($e) {\n                    $e.preventDefault();\n                });\n            },\n            _onSelectableClicked: function onSelectableClicked(type, $el) {\n                this.select($el);\n            },\n            _onDatasetCleared: function onDatasetCleared() {\n                this._updateHint();\n            },\n            _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {\n                this._updateHint();\n                this.eventBus.trigger(\"render\", suggestions, async, dataset);\n            },\n            _onAsyncRequested: function onAsyncRequested(type, dataset, query) {\n                this.eventBus.trigger(\"asyncrequest\", query, dataset);\n            },\n            _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {\n                this.eventBus.trigger(\"asynccancel\", query, dataset);\n            },\n            _onAsyncReceived: function onAsyncReceived(type, dataset, query) {\n                this.eventBus.trigger(\"asyncreceive\", query, dataset);\n            },\n            _onFocused: function onFocused() {\n                this._minLengthMet() && this.menu.update(this.input.getQuery());\n            },\n            _onBlurred: function onBlurred() {\n                if (this.input.hasQueryChangedSinceLastFocus()) {\n                    this.eventBus.trigger(\"change\", this.input.getQuery());\n                }\n            },\n            _onEnterKeyed: function onEnterKeyed(type, $e) {\n                var $selectable;\n                if ($selectable = this.menu.getActiveSelectable()) {\n                    this.select($selectable) && $e.preventDefault();\n                }\n            },\n            _onTabKeyed: function onTabKeyed(type, $e) {\n                var $selectable;\n                if ($selectable = this.menu.getActiveSelectable()) {\n                    this.select($selectable) && $e.preventDefault();\n                } else if ($selectable = this.menu.getTopSelectable()) {\n                    this.autocomplete($selectable) && $e.preventDefault();\n                }\n            },\n            _onEscKeyed: function onEscKeyed() {\n                this.close();\n            },\n            _onUpKeyed: function onUpKeyed() {\n                this.moveCursor(-1);\n            },\n            _onDownKeyed: function onDownKeyed() {\n                this.moveCursor(+1);\n            },\n            _onLeftKeyed: function onLeftKeyed() {\n                if (this.dir === \"rtl\" && this.input.isCursorAtEnd()) {\n                    this.autocomplete(this.menu.getTopSelectable());\n                }\n            },\n            _onRightKeyed: function onRightKeyed() {\n                if (this.dir === \"ltr\" && this.input.isCursorAtEnd()) {\n                    this.autocomplete(this.menu.getTopSelectable());\n                }\n            },\n            _onQueryChanged: function onQueryChanged(e, query) {\n                this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();\n            },\n            _onWhitespaceChanged: function onWhitespaceChanged() {\n                this._updateHint();\n            },\n            _onLangDirChanged: function onLangDirChanged(e, dir) {\n                if (this.dir !== dir) {\n                    this.dir = dir;\n                    this.menu.setLanguageDirection(dir);\n                }\n            },\n            _openIfActive: function openIfActive() {\n                this.isActive() && this.open();\n            },\n            _minLengthMet: function minLengthMet(query) {\n                query = _.isString(query) ? query : this.input.getQuery() || \"\";\n                return query.length >= this.minLength;\n            },\n            _updateHint: function updateHint() {\n                var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;\n                $selectable = this.menu.getTopSelectable();\n                data = this.menu.getSelectableData($selectable);\n                val = this.input.getInputValue();\n                if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {\n                    query = Input.normalizeQuery(val);\n                    escapedQuery = _.escapeRegExChars(query);\n                    frontMatchRegEx = new RegExp(\"^(?:\" + escapedQuery + \")(.+$)\", \"i\");\n                    match = frontMatchRegEx.exec(data.val);\n                    match && this.input.setHint(val + match[1]);\n                } else {\n                    this.input.clearHint();\n                }\n            },\n            isEnabled: function isEnabled() {\n                return this.enabled;\n            },\n            enable: function enable() {\n                this.enabled = true;\n            },\n            disable: function disable() {\n                this.enabled = false;\n            },\n            isActive: function isActive() {\n                return this.active;\n            },\n            activate: function activate() {\n                if (this.isActive()) {\n                    return true;\n                } else if (!this.isEnabled() || this.eventBus.before(\"active\")) {\n                    return false;\n                } else {\n                    this.active = true;\n                    this.eventBus.trigger(\"active\");\n                    return true;\n                }\n            },\n            deactivate: function deactivate() {\n                if (!this.isActive()) {\n                    return true;\n                } else if (this.eventBus.before(\"idle\")) {\n                    return false;\n                } else {\n                    this.active = false;\n                    this.close();\n                    this.eventBus.trigger(\"idle\");\n                    return true;\n                }\n            },\n            isOpen: function isOpen() {\n                return this.menu.isOpen();\n            },\n            open: function open() {\n                if (!this.isOpen() && !this.eventBus.before(\"open\")) {\n                    this.menu.open();\n                    this._updateHint();\n                    this.eventBus.trigger(\"open\");\n                }\n                return this.isOpen();\n            },\n            close: function close() {\n                if (this.isOpen() && !this.eventBus.before(\"close\")) {\n                    this.menu.close();\n                    this.input.clearHint();\n                    this.input.resetInputValue();\n                    this.eventBus.trigger(\"close\");\n                }\n                return !this.isOpen();\n            },\n            setVal: function setVal(val) {\n                this.input.setQuery(_.toStr(val));\n            },\n            getVal: function getVal() {\n                return this.input.getQuery();\n            },\n            select: function select($selectable) {\n                var data = this.menu.getSelectableData($selectable);\n                if (data && !this.eventBus.before(\"select\", data.obj)) {\n                    this.input.setQuery(data.val, true);\n                    this.eventBus.trigger(\"select\", data.obj);\n                    this.close();\n                    return true;\n                }\n                return false;\n            },\n            autocomplete: function autocomplete($selectable) {\n                var query, data, isValid;\n                query = this.input.getQuery();\n                data = this.menu.getSelectableData($selectable);\n                isValid = data && query !== data.val;\n                if (isValid && !this.eventBus.before(\"autocomplete\", data.obj)) {\n                    this.input.setQuery(data.val);\n                    this.eventBus.trigger(\"autocomplete\", data.obj);\n                    return true;\n                }\n                return false;\n            },\n            moveCursor: function moveCursor(delta) {\n                var query, $candidate, data, payload, cancelMove;\n                query = this.input.getQuery();\n                $candidate = this.menu.selectableRelativeToCursor(delta);\n                data = this.menu.getSelectableData($candidate);\n                payload = data ? data.obj : null;\n                cancelMove = this._minLengthMet() && this.menu.update(query);\n                if (!cancelMove && !this.eventBus.before(\"cursorchange\", payload)) {\n                    this.menu.setCursor($candidate);\n                    if (data) {\n                        this.input.setInputValue(data.val);\n                    } else {\n                        this.input.resetInputValue();\n                        this._updateHint();\n                    }\n                    this.eventBus.trigger(\"cursorchange\", payload);\n                    return true;\n                }\n                return false;\n            },\n            destroy: function destroy() {\n                this.input.destroy();\n                this.menu.destroy();\n            }\n        });\n        return Typeahead;\n        function c(ctx) {\n            var methods = [].slice.call(arguments, 1);\n            return function() {\n                var args = [].slice.call(arguments);\n                _.each(methods, function(method) {\n                    return ctx[method].apply(ctx, args);\n                });\n            };\n        }\n    }();\n    (function() {\n        \"use strict\";\n        var old, keys, methods;\n        old = $.fn.typeahead;\n        keys = {\n            www: \"tt-www\",\n            attrs: \"tt-attrs\",\n            typeahead: \"tt-typeahead\"\n        };\n        methods = {\n            initialize: function initialize(o, datasets) {\n                var www;\n                datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);\n                o = o || {};\n                www = WWW(o.classNames);\n                return this.each(attach);\n                function attach() {\n                    var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;\n                    _.each(datasets, function(d) {\n                        d.highlight = !!o.highlight;\n                    });\n                    $input = $(this);\n                    $wrapper = $(www.html.wrapper);\n                    $hint = $elOrNull(o.hint);\n                    $menu = $elOrNull(o.menu);\n                    defaultHint = o.hint !== false && !$hint;\n                    defaultMenu = o.menu !== false && !$menu;\n                    defaultHint && ($hint = buildHintFromInput($input, www));\n                    defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));\n                    $hint && $hint.val(\"\");\n                    $input = prepInput($input, www);\n                    if (defaultHint || defaultMenu) {\n                        $wrapper.css(www.css.wrapper);\n                        $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);\n                        $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null);\n                    }\n                    MenuConstructor = defaultMenu ? DefaultMenu : Menu;\n                    eventBus = new EventBus({\n                        el: $input\n                    });\n                    input = new Input({\n                        hint: $hint,\n                        input: $input\n                    }, www);\n                    menu = new MenuConstructor({\n                        node: $menu,\n                        datasets: datasets\n                    }, www);\n                    typeahead = new Typeahead({\n                        input: input,\n                        menu: menu,\n                        eventBus: eventBus,\n                        minLength: o.minLength\n                    }, www);\n                    $input.data(keys.www, www);\n                    $input.data(keys.typeahead, typeahead);\n                }\n            },\n            isEnabled: function isEnabled() {\n                var enabled;\n                ttEach(this.first(), function(t) {\n                    enabled = t.isEnabled();\n                });\n                return enabled;\n            },\n            enable: function enable() {\n                ttEach(this, function(t) {\n                    t.enable();\n                });\n                return this;\n            },\n            disable: function disable() {\n                ttEach(this, function(t) {\n                    t.disable();\n                });\n                return this;\n            },\n            isActive: function isActive() {\n                var active;\n                ttEach(this.first(), function(t) {\n                    active = t.isActive();\n                });\n                return active;\n            },\n            activate: function activate() {\n                ttEach(this, function(t) {\n                    t.activate();\n                });\n                return this;\n            },\n            deactivate: function deactivate() {\n                ttEach(this, function(t) {\n                    t.deactivate();\n                });\n                return this;\n            },\n            isOpen: function isOpen() {\n                var open;\n                ttEach(this.first(), function(t) {\n                    open = t.isOpen();\n                });\n                return open;\n            },\n            open: function open() {\n                ttEach(this, function(t) {\n                    t.open();\n                });\n                return this;\n            },\n            close: function close() {\n                ttEach(this, function(t) {\n                    t.close();\n                });\n                return this;\n            },\n            select: function select(el) {\n                var success = false, $el = $(el);\n                ttEach(this.first(), function(t) {\n                    success = t.select($el);\n                });\n                return success;\n            },\n            autocomplete: function autocomplete(el) {\n                var success = false, $el = $(el);\n                ttEach(this.first(), function(t) {\n                    success = t.autocomplete($el);\n                });\n                return success;\n            },\n            moveCursor: function moveCursoe(delta) {\n                var success = false;\n                ttEach(this.first(), function(t) {\n                    success = t.moveCursor(delta);\n                });\n                return success;\n            },\n            val: function val(newVal) {\n                var query;\n                if (!arguments.length) {\n                    ttEach(this.first(), function(t) {\n                        query = t.getVal();\n                    });\n                    return query;\n                } else {\n                    ttEach(this, function(t) {\n                        t.setVal(newVal);\n                    });\n                    return this;\n                }\n            },\n            destroy: function destroy() {\n                ttEach(this, function(typeahead, $input) {\n                    revert($input);\n                    typeahead.destroy();\n                });\n                return this;\n            }\n        };\n        $.fn.typeahead = function(method) {\n            if (methods[method]) {\n                return methods[method].apply(this, [].slice.call(arguments, 1));\n            } else {\n                return methods.initialize.apply(this, arguments);\n            }\n        };\n        $.fn.typeahead.noConflict = function noConflict() {\n            $.fn.typeahead = old;\n            return this;\n        };\n        function ttEach($els, fn) {\n            $els.each(function() {\n                var $input = $(this), typeahead;\n                (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);\n            });\n        }\n        function buildHintFromInput($input, www) {\n            return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop(\"readonly\", true).removeAttr(\"id name placeholder required\").attr({\n                autocomplete: \"off\",\n                spellcheck: \"false\",\n                tabindex: -1\n            });\n        }\n        function prepInput($input, www) {\n            $input.data(keys.attrs, {\n                dir: $input.attr(\"dir\"),\n                autocomplete: $input.attr(\"autocomplete\"),\n                spellcheck: $input.attr(\"spellcheck\"),\n                style: $input.attr(\"style\")\n            });\n            $input.addClass(www.classes.input).attr({\n                autocomplete: \"off\",\n                spellcheck: false\n            });\n            try {\n                !$input.attr(\"dir\") && $input.attr(\"dir\", \"auto\");\n            } catch (e) {}\n            return $input;\n        }\n        function getBackgroundStyles($el) {\n            return {\n                backgroundAttachment: $el.css(\"background-attachment\"),\n                backgroundClip: $el.css(\"background-clip\"),\n                backgroundColor: $el.css(\"background-color\"),\n                backgroundImage: $el.css(\"background-image\"),\n                backgroundOrigin: $el.css(\"background-origin\"),\n                backgroundPosition: $el.css(\"background-position\"),\n                backgroundRepeat: $el.css(\"background-repeat\"),\n                backgroundSize: $el.css(\"background-size\")\n            };\n        }\n        function revert($input) {\n            var www, $wrapper;\n            www = $input.data(keys.www);\n            $wrapper = $input.parent().filter(www.selectors.wrapper);\n            _.each($input.data(keys.attrs), function(val, key) {\n                _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);\n            });\n            $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input);\n            if ($wrapper.length) {\n                $input.detach().insertAfter($wrapper);\n                $wrapper.remove();\n            }\n        }\n        function $elOrNull(obj) {\n            var isValid, $el;\n            isValid = _.isJQuery(obj) || _.isElement(obj);\n            $el = isValid ? $(obj).first() : [];\n            return $el.length ? $el : null;\n        }\n    })();\n});"
  },
  {
    "path": "dist/typeahead.jquery.js",
    "content": "/*!\n * typeahead.js 0.11.1\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT\n */\n\n(function(root, factory) {\n    if (typeof define === \"function\" && define.amd) {\n        define(\"typeahead.js\", [ \"jquery\" ], function(a0) {\n            return factory(a0);\n        });\n    } else if (typeof exports === \"object\") {\n        module.exports = factory(require(\"jquery\"));\n    } else {\n        factory(jQuery);\n    }\n})(this, function($) {\n    var _ = function() {\n        \"use strict\";\n        return {\n            isMsie: function() {\n                return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\\d+(.\\d+)?)/i)[2] : false;\n            },\n            isBlankString: function(str) {\n                return !str || /^\\s*$/.test(str);\n            },\n            escapeRegExChars: function(str) {\n                return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n            },\n            isString: function(obj) {\n                return typeof obj === \"string\";\n            },\n            isNumber: function(obj) {\n                return typeof obj === \"number\";\n            },\n            isArray: $.isArray,\n            isFunction: $.isFunction,\n            isObject: $.isPlainObject,\n            isUndefined: function(obj) {\n                return typeof obj === \"undefined\";\n            },\n            isElement: function(obj) {\n                return !!(obj && obj.nodeType === 1);\n            },\n            isJQuery: function(obj) {\n                return obj instanceof $;\n            },\n            toStr: function toStr(s) {\n                return _.isUndefined(s) || s === null ? \"\" : s + \"\";\n            },\n            bind: $.proxy,\n            each: function(collection, cb) {\n                $.each(collection, reverseArgs);\n                function reverseArgs(index, value) {\n                    return cb(value, index);\n                }\n            },\n            map: $.map,\n            filter: $.grep,\n            every: function(obj, test) {\n                var result = true;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (!(result = test.call(null, val, key, obj))) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            some: function(obj, test) {\n                var result = false;\n                if (!obj) {\n                    return result;\n                }\n                $.each(obj, function(key, val) {\n                    if (result = test.call(null, val, key, obj)) {\n                        return false;\n                    }\n                });\n                return !!result;\n            },\n            mixin: $.extend,\n            identity: function(x) {\n                return x;\n            },\n            clone: function(obj) {\n                return $.extend(true, {}, obj);\n            },\n            getIdGenerator: function() {\n                var counter = 0;\n                return function() {\n                    return counter++;\n                };\n            },\n            templatify: function templatify(obj) {\n                return $.isFunction(obj) ? obj : template;\n                function template() {\n                    return String(obj);\n                }\n            },\n            defer: function(fn) {\n                setTimeout(fn, 0);\n            },\n            debounce: function(func, wait, immediate) {\n                var timeout, result;\n                return function() {\n                    var context = this, args = arguments, later, callNow;\n                    later = function() {\n                        timeout = null;\n                        if (!immediate) {\n                            result = func.apply(context, args);\n                        }\n                    };\n                    callNow = immediate && !timeout;\n                    clearTimeout(timeout);\n                    timeout = setTimeout(later, wait);\n                    if (callNow) {\n                        result = func.apply(context, args);\n                    }\n                    return result;\n                };\n            },\n            throttle: function(func, wait) {\n                var context, args, timeout, result, previous, later;\n                previous = 0;\n                later = function() {\n                    previous = new Date();\n                    timeout = null;\n                    result = func.apply(context, args);\n                };\n                return function() {\n                    var now = new Date(), remaining = wait - (now - previous);\n                    context = this;\n                    args = arguments;\n                    if (remaining <= 0) {\n                        clearTimeout(timeout);\n                        timeout = null;\n                        previous = now;\n                        result = func.apply(context, args);\n                    } else if (!timeout) {\n                        timeout = setTimeout(later, remaining);\n                    }\n                    return result;\n                };\n            },\n            stringify: function(val) {\n                return _.isString(val) ? val : JSON.stringify(val);\n            },\n            noop: function() {}\n        };\n    }();\n    var WWW = function() {\n        \"use strict\";\n        var defaultClassNames = {\n            wrapper: \"twitter-typeahead\",\n            input: \"tt-input\",\n            hint: \"tt-hint\",\n            menu: \"tt-menu\",\n            dataset: \"tt-dataset\",\n            suggestion: \"tt-suggestion\",\n            selectable: \"tt-selectable\",\n            empty: \"tt-empty\",\n            open: \"tt-open\",\n            cursor: \"tt-cursor\",\n            highlight: \"tt-highlight\"\n        };\n        return build;\n        function build(o) {\n            var www, classes;\n            classes = _.mixin({}, defaultClassNames, o);\n            www = {\n                css: buildCss(),\n                classes: classes,\n                html: buildHtml(classes),\n                selectors: buildSelectors(classes)\n            };\n            return {\n                css: www.css,\n                html: www.html,\n                classes: www.classes,\n                selectors: www.selectors,\n                mixin: function(o) {\n                    _.mixin(o, www);\n                }\n            };\n        }\n        function buildHtml(c) {\n            return {\n                wrapper: '<span class=\"' + c.wrapper + '\"></span>',\n                menu: '<div class=\"' + c.menu + '\"></div>'\n            };\n        }\n        function buildSelectors(classes) {\n            var selectors = {};\n            _.each(classes, function(v, k) {\n                selectors[k] = \".\" + v;\n            });\n            return selectors;\n        }\n        function buildCss() {\n            var css = {\n                wrapper: {\n                    position: \"relative\",\n                    display: \"inline-block\"\n                },\n                hint: {\n                    position: \"absolute\",\n                    top: \"0\",\n                    left: \"0\",\n                    borderColor: \"transparent\",\n                    boxShadow: \"none\",\n                    opacity: \"1\"\n                },\n                input: {\n                    position: \"relative\",\n                    verticalAlign: \"top\",\n                    backgroundColor: \"transparent\"\n                },\n                inputWithNoHint: {\n                    position: \"relative\",\n                    verticalAlign: \"top\"\n                },\n                menu: {\n                    position: \"absolute\",\n                    top: \"100%\",\n                    left: \"0\",\n                    zIndex: \"100\",\n                    display: \"none\"\n                },\n                ltr: {\n                    left: \"0\",\n                    right: \"auto\"\n                },\n                rtl: {\n                    left: \"auto\",\n                    right: \" 0\"\n                }\n            };\n            if (_.isMsie()) {\n                _.mixin(css.input, {\n                    backgroundImage: \"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)\"\n                });\n            }\n            return css;\n        }\n    }();\n    var EventBus = function() {\n        \"use strict\";\n        var namespace, deprecationMap;\n        namespace = \"typeahead:\";\n        deprecationMap = {\n            render: \"rendered\",\n            cursorchange: \"cursorchanged\",\n            select: \"selected\",\n            autocomplete: \"autocompleted\"\n        };\n        function EventBus(o) {\n            if (!o || !o.el) {\n                $.error(\"EventBus initialized without el\");\n            }\n            this.$el = $(o.el);\n        }\n        _.mixin(EventBus.prototype, {\n            _trigger: function(type, args) {\n                var $e;\n                $e = $.Event(namespace + type);\n                (args = args || []).unshift($e);\n                this.$el.trigger.apply(this.$el, args);\n                return $e;\n            },\n            before: function(type) {\n                var args, $e;\n                args = [].slice.call(arguments, 1);\n                $e = this._trigger(\"before\" + type, args);\n                return $e.isDefaultPrevented();\n            },\n            trigger: function(type) {\n                var deprecatedType;\n                this._trigger(type, [].slice.call(arguments, 1));\n                if (deprecatedType = deprecationMap[type]) {\n                    this._trigger(deprecatedType, [].slice.call(arguments, 1));\n                }\n            }\n        });\n        return EventBus;\n    }();\n    var EventEmitter = function() {\n        \"use strict\";\n        var splitter = /\\s+/, nextTick = getNextTick();\n        return {\n            onSync: onSync,\n            onAsync: onAsync,\n            off: off,\n            trigger: trigger\n        };\n        function on(method, types, cb, context) {\n            var type;\n            if (!cb) {\n                return this;\n            }\n            types = types.split(splitter);\n            cb = context ? bindContext(cb, context) : cb;\n            this._callbacks = this._callbacks || {};\n            while (type = types.shift()) {\n                this._callbacks[type] = this._callbacks[type] || {\n                    sync: [],\n                    async: []\n                };\n                this._callbacks[type][method].push(cb);\n            }\n            return this;\n        }\n        function onAsync(types, cb, context) {\n            return on.call(this, \"async\", types, cb, context);\n        }\n        function onSync(types, cb, context) {\n            return on.call(this, \"sync\", types, cb, context);\n        }\n        function off(types) {\n            var type;\n            if (!this._callbacks) {\n                return this;\n            }\n            types = types.split(splitter);\n            while (type = types.shift()) {\n                delete this._callbacks[type];\n            }\n            return this;\n        }\n        function trigger(types) {\n            var type, callbacks, args, syncFlush, asyncFlush;\n            if (!this._callbacks) {\n                return this;\n            }\n            types = types.split(splitter);\n            args = [].slice.call(arguments, 1);\n            while ((type = types.shift()) && (callbacks = this._callbacks[type])) {\n                syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));\n                asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));\n                syncFlush() && nextTick(asyncFlush);\n            }\n            return this;\n        }\n        function getFlush(callbacks, context, args) {\n            return flush;\n            function flush() {\n                var cancelled;\n                for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {\n                    cancelled = callbacks[i].apply(context, args) === false;\n                }\n                return !cancelled;\n            }\n        }\n        function getNextTick() {\n            var nextTickFn;\n            if (window.setImmediate) {\n                nextTickFn = function nextTickSetImmediate(fn) {\n                    setImmediate(function() {\n                        fn();\n                    });\n                };\n            } else {\n                nextTickFn = function nextTickSetTimeout(fn) {\n                    setTimeout(function() {\n                        fn();\n                    }, 0);\n                };\n            }\n            return nextTickFn;\n        }\n        function bindContext(fn, context) {\n            return fn.bind ? fn.bind(context) : function() {\n                fn.apply(context, [].slice.call(arguments, 0));\n            };\n        }\n    }();\n    var highlight = function(doc) {\n        \"use strict\";\n        var defaults = {\n            node: null,\n            pattern: null,\n            tagName: \"strong\",\n            className: null,\n            wordsOnly: false,\n            caseSensitive: false\n        };\n        return function hightlight(o) {\n            var regex;\n            o = _.mixin({}, defaults, o);\n            if (!o.node || !o.pattern) {\n                return;\n            }\n            o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];\n            regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);\n            traverse(o.node, hightlightTextNode);\n            function hightlightTextNode(textNode) {\n                var match, patternNode, wrapperNode;\n                if (match = regex.exec(textNode.data)) {\n                    wrapperNode = doc.createElement(o.tagName);\n                    o.className && (wrapperNode.className = o.className);\n                    patternNode = textNode.splitText(match.index);\n                    patternNode.splitText(match[0].length);\n                    wrapperNode.appendChild(patternNode.cloneNode(true));\n                    textNode.parentNode.replaceChild(wrapperNode, patternNode);\n                }\n                return !!match;\n            }\n            function traverse(el, hightlightTextNode) {\n                var childNode, TEXT_NODE_TYPE = 3;\n                for (var i = 0; i < el.childNodes.length; i++) {\n                    childNode = el.childNodes[i];\n                    if (childNode.nodeType === TEXT_NODE_TYPE) {\n                        i += hightlightTextNode(childNode) ? 1 : 0;\n                    } else {\n                        traverse(childNode, hightlightTextNode);\n                    }\n                }\n            }\n        };\n        function getRegex(patterns, caseSensitive, wordsOnly) {\n            var escapedPatterns = [], regexStr;\n            for (var i = 0, len = patterns.length; i < len; i++) {\n                escapedPatterns.push(_.escapeRegExChars(patterns[i]));\n            }\n            regexStr = wordsOnly ? \"\\\\b(\" + escapedPatterns.join(\"|\") + \")\\\\b\" : \"(\" + escapedPatterns.join(\"|\") + \")\";\n            return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, \"i\");\n        }\n    }(window.document);\n    var Input = function() {\n        \"use strict\";\n        var specialKeyCodeMap;\n        specialKeyCodeMap = {\n            9: \"tab\",\n            27: \"esc\",\n            37: \"left\",\n            39: \"right\",\n            13: \"enter\",\n            38: \"up\",\n            40: \"down\"\n        };\n        function Input(o, www) {\n            o = o || {};\n            if (!o.input) {\n                $.error(\"input is missing\");\n            }\n            www.mixin(this);\n            this.$hint = $(o.hint);\n            this.$input = $(o.input);\n            this.query = this.$input.val();\n            this.queryWhenFocused = this.hasFocus() ? this.query : null;\n            this.$overflowHelper = buildOverflowHelper(this.$input);\n            this._checkLanguageDirection();\n            if (this.$hint.length === 0) {\n                this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;\n            }\n        }\n        Input.normalizeQuery = function(str) {\n            return _.toStr(str).replace(/^\\s*/g, \"\").replace(/\\s{2,}/g, \" \");\n        };\n        _.mixin(Input.prototype, EventEmitter, {\n            _onBlur: function onBlur() {\n                this.resetInputValue();\n                this.trigger(\"blurred\");\n            },\n            _onFocus: function onFocus() {\n                this.queryWhenFocused = this.query;\n                this.trigger(\"focused\");\n            },\n            _onKeydown: function onKeydown($e) {\n                var keyName = specialKeyCodeMap[$e.which || $e.keyCode];\n                this._managePreventDefault(keyName, $e);\n                if (keyName && this._shouldTrigger(keyName, $e)) {\n                    this.trigger(keyName + \"Keyed\", $e);\n                }\n            },\n            _onInput: function onInput() {\n                this._setQuery(this.getInputValue());\n                this.clearHintIfInvalid();\n                this._checkLanguageDirection();\n            },\n            _managePreventDefault: function managePreventDefault(keyName, $e) {\n                var preventDefault;\n                switch (keyName) {\n                  case \"up\":\n                  case \"down\":\n                    preventDefault = !withModifier($e);\n                    break;\n\n                  default:\n                    preventDefault = false;\n                }\n                preventDefault && $e.preventDefault();\n            },\n            _shouldTrigger: function shouldTrigger(keyName, $e) {\n                var trigger;\n                switch (keyName) {\n                  case \"tab\":\n                    trigger = !withModifier($e);\n                    break;\n\n                  default:\n                    trigger = true;\n                }\n                return trigger;\n            },\n            _checkLanguageDirection: function checkLanguageDirection() {\n                var dir = (this.$input.css(\"direction\") || \"ltr\").toLowerCase();\n                if (this.dir !== dir) {\n                    this.dir = dir;\n                    this.$hint.attr(\"dir\", dir);\n                    this.trigger(\"langDirChanged\", dir);\n                }\n            },\n            _setQuery: function setQuery(val, silent) {\n                var areEquivalent, hasDifferentWhitespace;\n                areEquivalent = areQueriesEquivalent(val, this.query);\n                hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;\n                this.query = val;\n                if (!silent && !areEquivalent) {\n                    this.trigger(\"queryChanged\", this.query);\n                } else if (!silent && hasDifferentWhitespace) {\n                    this.trigger(\"whitespaceChanged\", this.query);\n                }\n            },\n            bind: function() {\n                var that = this, onBlur, onFocus, onKeydown, onInput;\n                onBlur = _.bind(this._onBlur, this);\n                onFocus = _.bind(this._onFocus, this);\n                onKeydown = _.bind(this._onKeydown, this);\n                onInput = _.bind(this._onInput, this);\n                this.$input.on(\"blur.tt\", onBlur).on(\"focus.tt\", onFocus).on(\"keydown.tt\", onKeydown);\n                if (!_.isMsie() || _.isMsie() > 9) {\n                    this.$input.on(\"input.tt\", onInput);\n                } else {\n                    this.$input.on(\"keydown.tt keypress.tt cut.tt paste.tt\", function($e) {\n                        if (specialKeyCodeMap[$e.which || $e.keyCode]) {\n                            return;\n                        }\n                        _.defer(_.bind(that._onInput, that, $e));\n                    });\n                }\n                return this;\n            },\n            focus: function focus() {\n                this.$input.focus();\n            },\n            blur: function blur() {\n                this.$input.blur();\n            },\n            getLangDir: function getLangDir() {\n                return this.dir;\n            },\n            getQuery: function getQuery() {\n                return this.query || \"\";\n            },\n            setQuery: function setQuery(val, silent) {\n                this.setInputValue(val);\n                this._setQuery(val, silent);\n            },\n            hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {\n                return this.query !== this.queryWhenFocused;\n            },\n            getInputValue: function getInputValue() {\n                return this.$input.val();\n            },\n            setInputValue: function setInputValue(value) {\n                this.$input.val(value);\n                this.clearHintIfInvalid();\n                this._checkLanguageDirection();\n            },\n            resetInputValue: function resetInputValue() {\n                this.setInputValue(this.query);\n            },\n            getHint: function getHint() {\n                return this.$hint.val();\n            },\n            setHint: function setHint(value) {\n                this.$hint.val(value);\n            },\n            clearHint: function clearHint() {\n                this.setHint(\"\");\n            },\n            clearHintIfInvalid: function clearHintIfInvalid() {\n                var val, hint, valIsPrefixOfHint, isValid;\n                val = this.getInputValue();\n                hint = this.getHint();\n                valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;\n                isValid = val !== \"\" && valIsPrefixOfHint && !this.hasOverflow();\n                !isValid && this.clearHint();\n            },\n            hasFocus: function hasFocus() {\n                return this.$input.is(\":focus\");\n            },\n            hasOverflow: function hasOverflow() {\n                var constraint = this.$input.width() - 2;\n                this.$overflowHelper.text(this.getInputValue());\n                return this.$overflowHelper.width() >= constraint;\n            },\n            isCursorAtEnd: function() {\n                var valueLength, selectionStart, range;\n                valueLength = this.$input.val().length;\n                selectionStart = this.$input[0].selectionStart;\n                if (_.isNumber(selectionStart)) {\n                    return selectionStart === valueLength;\n                } else if (document.selection) {\n                    range = document.selection.createRange();\n                    range.moveStart(\"character\", -valueLength);\n                    return valueLength === range.text.length;\n                }\n                return true;\n            },\n            destroy: function destroy() {\n                this.$hint.off(\".tt\");\n                this.$input.off(\".tt\");\n                this.$overflowHelper.remove();\n                this.$hint = this.$input = this.$overflowHelper = $(\"<div>\");\n            }\n        });\n        return Input;\n        function buildOverflowHelper($input) {\n            return $('<pre aria-hidden=\"true\"></pre>').css({\n                position: \"absolute\",\n                visibility: \"hidden\",\n                whiteSpace: \"pre\",\n                fontFamily: $input.css(\"font-family\"),\n                fontSize: $input.css(\"font-size\"),\n                fontStyle: $input.css(\"font-style\"),\n                fontVariant: $input.css(\"font-variant\"),\n                fontWeight: $input.css(\"font-weight\"),\n                wordSpacing: $input.css(\"word-spacing\"),\n                letterSpacing: $input.css(\"letter-spacing\"),\n                textIndent: $input.css(\"text-indent\"),\n                textRendering: $input.css(\"text-rendering\"),\n                textTransform: $input.css(\"text-transform\")\n            }).insertAfter($input);\n        }\n        function areQueriesEquivalent(a, b) {\n            return Input.normalizeQuery(a) === Input.normalizeQuery(b);\n        }\n        function withModifier($e) {\n            return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;\n        }\n    }();\n    var Dataset = function() {\n        \"use strict\";\n        var keys, nameGenerator;\n        keys = {\n            val: \"tt-selectable-display\",\n            obj: \"tt-selectable-object\"\n        };\n        nameGenerator = _.getIdGenerator();\n        function Dataset(o, www) {\n            o = o || {};\n            o.templates = o.templates || {};\n            o.templates.notFound = o.templates.notFound || o.templates.empty;\n            if (!o.source) {\n                $.error(\"missing source\");\n            }\n            if (!o.node) {\n                $.error(\"missing node\");\n            }\n            if (o.name && !isValidName(o.name)) {\n                $.error(\"invalid dataset name: \" + o.name);\n            }\n            www.mixin(this);\n            this.highlight = !!o.highlight;\n            this.name = o.name || nameGenerator();\n            this.limit = o.limit || 5;\n            this.displayFn = getDisplayFn(o.display || o.displayKey);\n            this.templates = getTemplates(o.templates, this.displayFn);\n            this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;\n            this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;\n            this._resetLastSuggestion();\n            this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + \"-\" + this.name);\n        }\n        Dataset.extractData = function extractData(el) {\n            var $el = $(el);\n            if ($el.data(keys.obj)) {\n                return {\n                    val: $el.data(keys.val) || \"\",\n                    obj: $el.data(keys.obj) || null\n                };\n            }\n            return null;\n        };\n        _.mixin(Dataset.prototype, EventEmitter, {\n            _overwrite: function overwrite(query, suggestions) {\n                suggestions = suggestions || [];\n                if (suggestions.length) {\n                    this._renderSuggestions(query, suggestions);\n                } else if (this.async && this.templates.pending) {\n                    this._renderPending(query);\n                } else if (!this.async && this.templates.notFound) {\n                    this._renderNotFound(query);\n                } else {\n                    this._empty();\n                }\n                this.trigger(\"rendered\", this.name, suggestions, false);\n            },\n            _append: function append(query, suggestions) {\n                suggestions = suggestions || [];\n                if (suggestions.length && this.$lastSuggestion.length) {\n                    this._appendSuggestions(query, suggestions);\n                } else if (suggestions.length) {\n                    this._renderSuggestions(query, suggestions);\n                } else if (!this.$lastSuggestion.length && this.templates.notFound) {\n                    this._renderNotFound(query);\n                }\n                this.trigger(\"rendered\", this.name, suggestions, true);\n            },\n            _renderSuggestions: function renderSuggestions(query, suggestions) {\n                var $fragment;\n                $fragment = this._getSuggestionsFragment(query, suggestions);\n                this.$lastSuggestion = $fragment.children().last();\n                this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions));\n            },\n            _appendSuggestions: function appendSuggestions(query, suggestions) {\n                var $fragment, $lastSuggestion;\n                $fragment = this._getSuggestionsFragment(query, suggestions);\n                $lastSuggestion = $fragment.children().last();\n                this.$lastSuggestion.after($fragment);\n                this.$lastSuggestion = $lastSuggestion;\n            },\n            _renderPending: function renderPending(query) {\n                var template = this.templates.pending;\n                this._resetLastSuggestion();\n                template && this.$el.html(template({\n                    query: query,\n                    dataset: this.name\n                }));\n            },\n            _renderNotFound: function renderNotFound(query) {\n                var template = this.templates.notFound;\n                this._resetLastSuggestion();\n                template && this.$el.html(template({\n                    query: query,\n                    dataset: this.name\n                }));\n            },\n            _empty: function empty() {\n                this.$el.empty();\n                this._resetLastSuggestion();\n            },\n            _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {\n                var that = this, fragment;\n                fragment = document.createDocumentFragment();\n                _.each(suggestions, function getSuggestionNode(suggestion) {\n                    var $el, context;\n                    context = that._injectQuery(query, suggestion);\n                    $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + \" \" + that.classes.selectable);\n                    fragment.appendChild($el[0]);\n                });\n                this.highlight && highlight({\n                    className: this.classes.highlight,\n                    node: fragment,\n                    pattern: query\n                });\n                return $(fragment);\n            },\n            _getFooter: function getFooter(query, suggestions) {\n                return this.templates.footer ? this.templates.footer({\n                    query: query,\n                    suggestions: suggestions,\n                    dataset: this.name\n                }) : null;\n            },\n            _getHeader: function getHeader(query, suggestions) {\n                return this.templates.header ? this.templates.header({\n                    query: query,\n                    suggestions: suggestions,\n                    dataset: this.name\n                }) : null;\n            },\n            _resetLastSuggestion: function resetLastSuggestion() {\n                this.$lastSuggestion = $();\n            },\n            _injectQuery: function injectQuery(query, obj) {\n                return _.isObject(obj) ? _.mixin({\n                    _query: query\n                }, obj) : obj;\n            },\n            update: function update(query) {\n                var that = this, canceled = false, syncCalled = false, rendered = 0;\n                this.cancel();\n                this.cancel = function cancel() {\n                    canceled = true;\n                    that.cancel = $.noop;\n                    that.async && that.trigger(\"asyncCanceled\", query);\n                };\n                this.source(query, sync, async);\n                !syncCalled && sync([]);\n                function sync(suggestions) {\n                    if (syncCalled) {\n                        return;\n                    }\n                    syncCalled = true;\n                    suggestions = (suggestions || []).slice(0, that.limit);\n                    rendered = suggestions.length;\n                    that._overwrite(query, suggestions);\n                    if (rendered < that.limit && that.async) {\n                        that.trigger(\"asyncRequested\", query);\n                    }\n                }\n                function async(suggestions) {\n                    suggestions = suggestions || [];\n                    if (!canceled && rendered < that.limit) {\n                        that.cancel = $.noop;\n                        rendered += suggestions.length;\n                        that._append(query, suggestions.slice(0, that.limit - rendered));\n                        that.async && that.trigger(\"asyncReceived\", query);\n                    }\n                }\n            },\n            cancel: $.noop,\n            clear: function clear() {\n                this._empty();\n                this.cancel();\n                this.trigger(\"cleared\");\n            },\n            isEmpty: function isEmpty() {\n                return this.$el.is(\":empty\");\n            },\n            destroy: function destroy() {\n                this.$el = $(\"<div>\");\n            }\n        });\n        return Dataset;\n        function getDisplayFn(display) {\n            display = display || _.stringify;\n            return _.isFunction(display) ? display : displayFn;\n            function displayFn(obj) {\n                return obj[display];\n            }\n        }\n        function getTemplates(templates, displayFn) {\n            return {\n                notFound: templates.notFound && _.templatify(templates.notFound),\n                pending: templates.pending && _.templatify(templates.pending),\n                header: templates.header && _.templatify(templates.header),\n                footer: templates.footer && _.templatify(templates.footer),\n                suggestion: templates.suggestion || suggestionTemplate\n            };\n            function suggestionTemplate(context) {\n                return $(\"<div>\").text(displayFn(context));\n            }\n        }\n        function isValidName(str) {\n            return /^[_a-zA-Z0-9-]+$/.test(str);\n        }\n    }();\n    var Menu = function() {\n        \"use strict\";\n        function Menu(o, www) {\n            var that = this;\n            o = o || {};\n            if (!o.node) {\n                $.error(\"node is required\");\n            }\n            www.mixin(this);\n            this.$node = $(o.node);\n            this.query = null;\n            this.datasets = _.map(o.datasets, initializeDataset);\n            function initializeDataset(oDataset) {\n                var node = that.$node.find(oDataset.node).first();\n                oDataset.node = node.length ? node : $(\"<div>\").appendTo(that.$node);\n                return new Dataset(oDataset, www);\n            }\n        }\n        _.mixin(Menu.prototype, EventEmitter, {\n            _onSelectableClick: function onSelectableClick($e) {\n                this.trigger(\"selectableClicked\", $($e.currentTarget));\n            },\n            _onRendered: function onRendered(type, dataset, suggestions, async) {\n                this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());\n                this.trigger(\"datasetRendered\", dataset, suggestions, async);\n            },\n            _onCleared: function onCleared() {\n                this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());\n                this.trigger(\"datasetCleared\");\n            },\n            _propagate: function propagate() {\n                this.trigger.apply(this, arguments);\n            },\n            _allDatasetsEmpty: function allDatasetsEmpty() {\n                return _.every(this.datasets, isDatasetEmpty);\n                function isDatasetEmpty(dataset) {\n                    return dataset.isEmpty();\n                }\n            },\n            _getSelectables: function getSelectables() {\n                return this.$node.find(this.selectors.selectable);\n            },\n            _removeCursor: function _removeCursor() {\n                var $selectable = this.getActiveSelectable();\n                $selectable && $selectable.removeClass(this.classes.cursor);\n            },\n            _ensureVisible: function ensureVisible($el) {\n                var elTop, elBottom, nodeScrollTop, nodeHeight;\n                elTop = $el.position().top;\n                elBottom = elTop + $el.outerHeight(true);\n                nodeScrollTop = this.$node.scrollTop();\n                nodeHeight = this.$node.height() + parseInt(this.$node.css(\"paddingTop\"), 10) + parseInt(this.$node.css(\"paddingBottom\"), 10);\n                if (elTop < 0) {\n                    this.$node.scrollTop(nodeScrollTop + elTop);\n                } else if (nodeHeight < elBottom) {\n                    this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));\n                }\n            },\n            bind: function() {\n                var that = this, onSelectableClick;\n                onSelectableClick = _.bind(this._onSelectableClick, this);\n                this.$node.on(\"click.tt\", this.selectors.selectable, onSelectableClick);\n                _.each(this.datasets, function(dataset) {\n                    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);\n                });\n                return this;\n            },\n            isOpen: function isOpen() {\n                return this.$node.hasClass(this.classes.open);\n            },\n            open: function open() {\n                this.$node.addClass(this.classes.open);\n            },\n            close: function close() {\n                this.$node.removeClass(this.classes.open);\n                this._removeCursor();\n            },\n            setLanguageDirection: function setLanguageDirection(dir) {\n                this.$node.attr(\"dir\", dir);\n            },\n            selectableRelativeToCursor: function selectableRelativeToCursor(delta) {\n                var $selectables, $oldCursor, oldIndex, newIndex;\n                $oldCursor = this.getActiveSelectable();\n                $selectables = this._getSelectables();\n                oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;\n                newIndex = oldIndex + delta;\n                newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;\n                newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;\n                return newIndex === -1 ? null : $selectables.eq(newIndex);\n            },\n            setCursor: function setCursor($selectable) {\n                this._removeCursor();\n                if ($selectable = $selectable && $selectable.first()) {\n                    $selectable.addClass(this.classes.cursor);\n                    this._ensureVisible($selectable);\n                }\n            },\n            getSelectableData: function getSelectableData($el) {\n                return $el && $el.length ? Dataset.extractData($el) : null;\n            },\n            getActiveSelectable: function getActiveSelectable() {\n                var $selectable = this._getSelectables().filter(this.selectors.cursor).first();\n                return $selectable.length ? $selectable : null;\n            },\n            getTopSelectable: function getTopSelectable() {\n                var $selectable = this._getSelectables().first();\n                return $selectable.length ? $selectable : null;\n            },\n            update: function update(query) {\n                var isValidUpdate = query !== this.query;\n                if (isValidUpdate) {\n                    this.query = query;\n                    _.each(this.datasets, updateDataset);\n                }\n                return isValidUpdate;\n                function updateDataset(dataset) {\n                    dataset.update(query);\n                }\n            },\n            empty: function empty() {\n                _.each(this.datasets, clearDataset);\n                this.query = null;\n                this.$node.addClass(this.classes.empty);\n                function clearDataset(dataset) {\n                    dataset.clear();\n                }\n            },\n            destroy: function destroy() {\n                this.$node.off(\".tt\");\n                this.$node = $(\"<div>\");\n                _.each(this.datasets, destroyDataset);\n                function destroyDataset(dataset) {\n                    dataset.destroy();\n                }\n            }\n        });\n        return Menu;\n    }();\n    var DefaultMenu = function() {\n        \"use strict\";\n        var s = Menu.prototype;\n        function DefaultMenu() {\n            Menu.apply(this, [].slice.call(arguments, 0));\n        }\n        _.mixin(DefaultMenu.prototype, Menu.prototype, {\n            open: function open() {\n                !this._allDatasetsEmpty() && this._show();\n                return s.open.apply(this, [].slice.call(arguments, 0));\n            },\n            close: function close() {\n                this._hide();\n                return s.close.apply(this, [].slice.call(arguments, 0));\n            },\n            _onRendered: function onRendered() {\n                if (this._allDatasetsEmpty()) {\n                    this._hide();\n                } else {\n                    this.isOpen() && this._show();\n                }\n                return s._onRendered.apply(this, [].slice.call(arguments, 0));\n            },\n            _onCleared: function onCleared() {\n                if (this._allDatasetsEmpty()) {\n                    this._hide();\n                } else {\n                    this.isOpen() && this._show();\n                }\n                return s._onCleared.apply(this, [].slice.call(arguments, 0));\n            },\n            setLanguageDirection: function setLanguageDirection(dir) {\n                this.$node.css(dir === \"ltr\" ? this.css.ltr : this.css.rtl);\n                return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));\n            },\n            _hide: function hide() {\n                this.$node.hide();\n            },\n            _show: function show() {\n                this.$node.css(\"display\", \"block\");\n            }\n        });\n        return DefaultMenu;\n    }();\n    var Typeahead = function() {\n        \"use strict\";\n        function Typeahead(o, www) {\n            var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged;\n            o = o || {};\n            if (!o.input) {\n                $.error(\"missing input\");\n            }\n            if (!o.menu) {\n                $.error(\"missing menu\");\n            }\n            if (!o.eventBus) {\n                $.error(\"missing event bus\");\n            }\n            www.mixin(this);\n            this.eventBus = o.eventBus;\n            this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;\n            this.input = o.input;\n            this.menu = o.menu;\n            this.enabled = true;\n            this.active = false;\n            this.input.hasFocus() && this.activate();\n            this.dir = this.input.getLangDir();\n            this._hacks();\n            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);\n            onFocused = c(this, \"activate\", \"open\", \"_onFocused\");\n            onBlurred = c(this, \"deactivate\", \"_onBlurred\");\n            onEnterKeyed = c(this, \"isActive\", \"isOpen\", \"_onEnterKeyed\");\n            onTabKeyed = c(this, \"isActive\", \"isOpen\", \"_onTabKeyed\");\n            onEscKeyed = c(this, \"isActive\", \"_onEscKeyed\");\n            onUpKeyed = c(this, \"isActive\", \"open\", \"_onUpKeyed\");\n            onDownKeyed = c(this, \"isActive\", \"open\", \"_onDownKeyed\");\n            onLeftKeyed = c(this, \"isActive\", \"isOpen\", \"_onLeftKeyed\");\n            onRightKeyed = c(this, \"isActive\", \"isOpen\", \"_onRightKeyed\");\n            onQueryChanged = c(this, \"_openIfActive\", \"_onQueryChanged\");\n            onWhitespaceChanged = c(this, \"_openIfActive\", \"_onWhitespaceChanged\");\n            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);\n        }\n        _.mixin(Typeahead.prototype, {\n            _hacks: function hacks() {\n                var $input, $menu;\n                $input = this.input.$input || $(\"<div>\");\n                $menu = this.menu.$node || $(\"<div>\");\n                $input.on(\"blur.tt\", function($e) {\n                    var active, isActive, hasActive;\n                    active = document.activeElement;\n                    isActive = $menu.is(active);\n                    hasActive = $menu.has(active).length > 0;\n                    if (_.isMsie() && (isActive || hasActive)) {\n                        $e.preventDefault();\n                        $e.stopImmediatePropagation();\n                        _.defer(function() {\n                            $input.focus();\n                        });\n                    }\n                });\n                $menu.on(\"mousedown.tt\", function($e) {\n                    $e.preventDefault();\n                });\n            },\n            _onSelectableClicked: function onSelectableClicked(type, $el) {\n                this.select($el);\n            },\n            _onDatasetCleared: function onDatasetCleared() {\n                this._updateHint();\n            },\n            _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {\n                this._updateHint();\n                this.eventBus.trigger(\"render\", suggestions, async, dataset);\n            },\n            _onAsyncRequested: function onAsyncRequested(type, dataset, query) {\n                this.eventBus.trigger(\"asyncrequest\", query, dataset);\n            },\n            _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {\n                this.eventBus.trigger(\"asynccancel\", query, dataset);\n            },\n            _onAsyncReceived: function onAsyncReceived(type, dataset, query) {\n                this.eventBus.trigger(\"asyncreceive\", query, dataset);\n            },\n            _onFocused: function onFocused() {\n                this._minLengthMet() && this.menu.update(this.input.getQuery());\n            },\n            _onBlurred: function onBlurred() {\n                if (this.input.hasQueryChangedSinceLastFocus()) {\n                    this.eventBus.trigger(\"change\", this.input.getQuery());\n                }\n            },\n            _onEnterKeyed: function onEnterKeyed(type, $e) {\n                var $selectable;\n                if ($selectable = this.menu.getActiveSelectable()) {\n                    this.select($selectable) && $e.preventDefault();\n                }\n            },\n            _onTabKeyed: function onTabKeyed(type, $e) {\n                var $selectable;\n                if ($selectable = this.menu.getActiveSelectable()) {\n                    this.select($selectable) && $e.preventDefault();\n                } else if ($selectable = this.menu.getTopSelectable()) {\n                    this.autocomplete($selectable) && $e.preventDefault();\n                }\n            },\n            _onEscKeyed: function onEscKeyed() {\n                this.close();\n            },\n            _onUpKeyed: function onUpKeyed() {\n                this.moveCursor(-1);\n            },\n            _onDownKeyed: function onDownKeyed() {\n                this.moveCursor(+1);\n            },\n            _onLeftKeyed: function onLeftKeyed() {\n                if (this.dir === \"rtl\" && this.input.isCursorAtEnd()) {\n                    this.autocomplete(this.menu.getTopSelectable());\n                }\n            },\n            _onRightKeyed: function onRightKeyed() {\n                if (this.dir === \"ltr\" && this.input.isCursorAtEnd()) {\n                    this.autocomplete(this.menu.getTopSelectable());\n                }\n            },\n            _onQueryChanged: function onQueryChanged(e, query) {\n                this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();\n            },\n            _onWhitespaceChanged: function onWhitespaceChanged() {\n                this._updateHint();\n            },\n            _onLangDirChanged: function onLangDirChanged(e, dir) {\n                if (this.dir !== dir) {\n                    this.dir = dir;\n                    this.menu.setLanguageDirection(dir);\n                }\n            },\n            _openIfActive: function openIfActive() {\n                this.isActive() && this.open();\n            },\n            _minLengthMet: function minLengthMet(query) {\n                query = _.isString(query) ? query : this.input.getQuery() || \"\";\n                return query.length >= this.minLength;\n            },\n            _updateHint: function updateHint() {\n                var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;\n                $selectable = this.menu.getTopSelectable();\n                data = this.menu.getSelectableData($selectable);\n                val = this.input.getInputValue();\n                if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {\n                    query = Input.normalizeQuery(val);\n                    escapedQuery = _.escapeRegExChars(query);\n                    frontMatchRegEx = new RegExp(\"^(?:\" + escapedQuery + \")(.+$)\", \"i\");\n                    match = frontMatchRegEx.exec(data.val);\n                    match && this.input.setHint(val + match[1]);\n                } else {\n                    this.input.clearHint();\n                }\n            },\n            isEnabled: function isEnabled() {\n                return this.enabled;\n            },\n            enable: function enable() {\n                this.enabled = true;\n            },\n            disable: function disable() {\n                this.enabled = false;\n            },\n            isActive: function isActive() {\n                return this.active;\n            },\n            activate: function activate() {\n                if (this.isActive()) {\n                    return true;\n                } else if (!this.isEnabled() || this.eventBus.before(\"active\")) {\n                    return false;\n                } else {\n                    this.active = true;\n                    this.eventBus.trigger(\"active\");\n                    return true;\n                }\n            },\n            deactivate: function deactivate() {\n                if (!this.isActive()) {\n                    return true;\n                } else if (this.eventBus.before(\"idle\")) {\n                    return false;\n                } else {\n                    this.active = false;\n                    this.close();\n                    this.eventBus.trigger(\"idle\");\n                    return true;\n                }\n            },\n            isOpen: function isOpen() {\n                return this.menu.isOpen();\n            },\n            open: function open() {\n                if (!this.isOpen() && !this.eventBus.before(\"open\")) {\n                    this.menu.open();\n                    this._updateHint();\n                    this.eventBus.trigger(\"open\");\n                }\n                return this.isOpen();\n            },\n            close: function close() {\n                if (this.isOpen() && !this.eventBus.before(\"close\")) {\n                    this.menu.close();\n                    this.input.clearHint();\n                    this.input.resetInputValue();\n                    this.eventBus.trigger(\"close\");\n                }\n                return !this.isOpen();\n            },\n            setVal: function setVal(val) {\n                this.input.setQuery(_.toStr(val));\n            },\n            getVal: function getVal() {\n                return this.input.getQuery();\n            },\n            select: function select($selectable) {\n                var data = this.menu.getSelectableData($selectable);\n                if (data && !this.eventBus.before(\"select\", data.obj)) {\n                    this.input.setQuery(data.val, true);\n                    this.eventBus.trigger(\"select\", data.obj);\n                    this.close();\n                    return true;\n                }\n                return false;\n            },\n            autocomplete: function autocomplete($selectable) {\n                var query, data, isValid;\n                query = this.input.getQuery();\n                data = this.menu.getSelectableData($selectable);\n                isValid = data && query !== data.val;\n                if (isValid && !this.eventBus.before(\"autocomplete\", data.obj)) {\n                    this.input.setQuery(data.val);\n                    this.eventBus.trigger(\"autocomplete\", data.obj);\n                    return true;\n                }\n                return false;\n            },\n            moveCursor: function moveCursor(delta) {\n                var query, $candidate, data, payload, cancelMove;\n                query = this.input.getQuery();\n                $candidate = this.menu.selectableRelativeToCursor(delta);\n                data = this.menu.getSelectableData($candidate);\n                payload = data ? data.obj : null;\n                cancelMove = this._minLengthMet() && this.menu.update(query);\n                if (!cancelMove && !this.eventBus.before(\"cursorchange\", payload)) {\n                    this.menu.setCursor($candidate);\n                    if (data) {\n                        this.input.setInputValue(data.val);\n                    } else {\n                        this.input.resetInputValue();\n                        this._updateHint();\n                    }\n                    this.eventBus.trigger(\"cursorchange\", payload);\n                    return true;\n                }\n                return false;\n            },\n            destroy: function destroy() {\n                this.input.destroy();\n                this.menu.destroy();\n            }\n        });\n        return Typeahead;\n        function c(ctx) {\n            var methods = [].slice.call(arguments, 1);\n            return function() {\n                var args = [].slice.call(arguments);\n                _.each(methods, function(method) {\n                    return ctx[method].apply(ctx, args);\n                });\n            };\n        }\n    }();\n    (function() {\n        \"use strict\";\n        var old, keys, methods;\n        old = $.fn.typeahead;\n        keys = {\n            www: \"tt-www\",\n            attrs: \"tt-attrs\",\n            typeahead: \"tt-typeahead\"\n        };\n        methods = {\n            initialize: function initialize(o, datasets) {\n                var www;\n                datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);\n                o = o || {};\n                www = WWW(o.classNames);\n                return this.each(attach);\n                function attach() {\n                    var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;\n                    _.each(datasets, function(d) {\n                        d.highlight = !!o.highlight;\n                    });\n                    $input = $(this);\n                    $wrapper = $(www.html.wrapper);\n                    $hint = $elOrNull(o.hint);\n                    $menu = $elOrNull(o.menu);\n                    defaultHint = o.hint !== false && !$hint;\n                    defaultMenu = o.menu !== false && !$menu;\n                    defaultHint && ($hint = buildHintFromInput($input, www));\n                    defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));\n                    $hint && $hint.val(\"\");\n                    $input = prepInput($input, www);\n                    if (defaultHint || defaultMenu) {\n                        $wrapper.css(www.css.wrapper);\n                        $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);\n                        $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null);\n                    }\n                    MenuConstructor = defaultMenu ? DefaultMenu : Menu;\n                    eventBus = new EventBus({\n                        el: $input\n                    });\n                    input = new Input({\n                        hint: $hint,\n                        input: $input\n                    }, www);\n                    menu = new MenuConstructor({\n                        node: $menu,\n                        datasets: datasets\n                    }, www);\n                    typeahead = new Typeahead({\n                        input: input,\n                        menu: menu,\n                        eventBus: eventBus,\n                        minLength: o.minLength\n                    }, www);\n                    $input.data(keys.www, www);\n                    $input.data(keys.typeahead, typeahead);\n                }\n            },\n            isEnabled: function isEnabled() {\n                var enabled;\n                ttEach(this.first(), function(t) {\n                    enabled = t.isEnabled();\n                });\n                return enabled;\n            },\n            enable: function enable() {\n                ttEach(this, function(t) {\n                    t.enable();\n                });\n                return this;\n            },\n            disable: function disable() {\n                ttEach(this, function(t) {\n                    t.disable();\n                });\n                return this;\n            },\n            isActive: function isActive() {\n                var active;\n                ttEach(this.first(), function(t) {\n                    active = t.isActive();\n                });\n                return active;\n            },\n            activate: function activate() {\n                ttEach(this, function(t) {\n                    t.activate();\n                });\n                return this;\n            },\n            deactivate: function deactivate() {\n                ttEach(this, function(t) {\n                    t.deactivate();\n                });\n                return this;\n            },\n            isOpen: function isOpen() {\n                var open;\n                ttEach(this.first(), function(t) {\n                    open = t.isOpen();\n                });\n                return open;\n            },\n            open: function open() {\n                ttEach(this, function(t) {\n                    t.open();\n                });\n                return this;\n            },\n            close: function close() {\n                ttEach(this, function(t) {\n                    t.close();\n                });\n                return this;\n            },\n            select: function select(el) {\n                var success = false, $el = $(el);\n                ttEach(this.first(), function(t) {\n                    success = t.select($el);\n                });\n                return success;\n            },\n            autocomplete: function autocomplete(el) {\n                var success = false, $el = $(el);\n                ttEach(this.first(), function(t) {\n                    success = t.autocomplete($el);\n                });\n                return success;\n            },\n            moveCursor: function moveCursoe(delta) {\n                var success = false;\n                ttEach(this.first(), function(t) {\n                    success = t.moveCursor(delta);\n                });\n                return success;\n            },\n            val: function val(newVal) {\n                var query;\n                if (!arguments.length) {\n                    ttEach(this.first(), function(t) {\n                        query = t.getVal();\n                    });\n                    return query;\n                } else {\n                    ttEach(this, function(t) {\n                        t.setVal(newVal);\n                    });\n                    return this;\n                }\n            },\n            destroy: function destroy() {\n                ttEach(this, function(typeahead, $input) {\n                    revert($input);\n                    typeahead.destroy();\n                });\n                return this;\n            }\n        };\n        $.fn.typeahead = function(method) {\n            if (methods[method]) {\n                return methods[method].apply(this, [].slice.call(arguments, 1));\n            } else {\n                return methods.initialize.apply(this, arguments);\n            }\n        };\n        $.fn.typeahead.noConflict = function noConflict() {\n            $.fn.typeahead = old;\n            return this;\n        };\n        function ttEach($els, fn) {\n            $els.each(function() {\n                var $input = $(this), typeahead;\n                (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);\n            });\n        }\n        function buildHintFromInput($input, www) {\n            return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop(\"readonly\", true).removeAttr(\"id name placeholder required\").attr({\n                autocomplete: \"off\",\n                spellcheck: \"false\",\n                tabindex: -1\n            });\n        }\n        function prepInput($input, www) {\n            $input.data(keys.attrs, {\n                dir: $input.attr(\"dir\"),\n                autocomplete: $input.attr(\"autocomplete\"),\n                spellcheck: $input.attr(\"spellcheck\"),\n                style: $input.attr(\"style\")\n            });\n            $input.addClass(www.classes.input).attr({\n                autocomplete: \"off\",\n                spellcheck: false\n            });\n            try {\n                !$input.attr(\"dir\") && $input.attr(\"dir\", \"auto\");\n            } catch (e) {}\n            return $input;\n        }\n        function getBackgroundStyles($el) {\n            return {\n                backgroundAttachment: $el.css(\"background-attachment\"),\n                backgroundClip: $el.css(\"background-clip\"),\n                backgroundColor: $el.css(\"background-color\"),\n                backgroundImage: $el.css(\"background-image\"),\n                backgroundOrigin: $el.css(\"background-origin\"),\n                backgroundPosition: $el.css(\"background-position\"),\n                backgroundRepeat: $el.css(\"background-repeat\"),\n                backgroundSize: $el.css(\"background-size\")\n            };\n        }\n        function revert($input) {\n            var www, $wrapper;\n            www = $input.data(keys.www);\n            $wrapper = $input.parent().filter(www.selectors.wrapper);\n            _.each($input.data(keys.attrs), function(val, key) {\n                _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);\n            });\n            $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input);\n            if ($wrapper.length) {\n                $input.detach().insertAfter($wrapper);\n                $wrapper.remove();\n            }\n        }\n        function $elOrNull(obj) {\n            var isValid, $el;\n            isValid = _.isJQuery(obj) || _.isElement(obj);\n            $el = isValid ? $(obj).first() : [];\n            return $el.length ? $el : null;\n        }\n    })();\n});"
  },
  {
    "path": "doc/bloodhound.md",
    "content": "Bloodhound\n==========\n\nBloodhound is the typeahead.js suggestion engine. Bloodhound is robust, \nflexible, and offers advanced functionalities such as prefetching, intelligent\ncaching, fast lookups, and backfilling with remote data.\n\nTable of Contents\n-----------------\n\n* [Features](#features)\n* [Usage](#usage)\n  * [API](#api)\n  * [Options](#options)\n  * [Prefetch](#prefetch)\n  * [Remote](#remote)\n\nFeatures\n--------\n\n* Works with hardcoded data\n* Prefetches data on initialization to reduce suggestion latency\n* Uses local storage intelligently to cut down on network requests\n* Backfills suggestions from a remote source\n* Rate-limits and caches network requests to remote sources to lighten the load\n\nUsage\n-----\n\n### API\n\n* [`new Bloodhound(options)`](#new-bloodhoundoptions)\n* [`Bloodhound.noConflict()`](#bloodhoundnoconflict)\n* [`Bloodhound#initialize(reinitialize)`](#bloodhoundinitializereinitialize)\n* [`Bloodhound#add(data)`](#bloodhoundadddata)\n* [`Bloodhound#get(ids)`](#bloodhoundgetids)\n* [`Bloodhound#search(query, sync, async)`](#bloodhoundsearchquery-sync-async)\n* [`Bloodhound#clear()`](#bloodhoundclear)\n\n#### new Bloodhound(options)\n\nThe constructor function. It takes an [options hash](#options) as its only \nargument.\n\n```javascript\nvar engine = new Bloodhound({\n  local: ['dog', 'pig', 'moose'],\n  queryTokenizer: Bloodhound.tokenizers.whitespace,\n  datumTokenizer: Bloodhound.tokenizers.whitespace\n});\n```\n\n#### Bloodhound.noConflict()\n\nReturns a reference to `Bloodhound` and reverts `window.Bloodhound` to its \nprevious value. Can be used to avoid naming collisions. \n\n```javascript\nvar Dachshund = Bloodhound.noConflict();\n```\n\n#### Bloodhound#initialize(reinitialize) \n\nKicks off the initialization of the suggestion engine. Initialization entails\nadding the data provided by `local` and `prefetch` to the internal search \nindex as well as setting up transport mechanism used by `remote`. Before \n`#initialize` is called, the `#get` and `#search` methods will effectively be\nno-ops.\n\nNote, unless the `initialize` option is `false`, this method is implicitly\ncalled by the constructor.\n\n```javascript\nvar engine = new Bloodhound({\n  initialize: false,\n  local: ['dog', 'pig', 'moose'],\n  queryTokenizer: Bloodhound.tokenizers.whitespace,\n  datumTokenizer: Bloodhound.tokenizers.whitespace\n});\n\nvar promise = engine.initialize();\n\npromise\n.done(function() { console.log('ready to go!'); })\n.fail(function() { console.log('err, something went wrong :('); });\n```\n\nAfter initialization, how subsequent invocations of `#initialize` behave \ndepends on the `reinitialize` argument. If `reinitialize` is falsy, the\nmethod will not execute the initialization logic and will just return the same \njQuery promise returned by the initial invocation. If `reinitialize` is truthy,\nthe method will behave as if it were being called for the first time.\n\n```javascript\nvar promise1 = engine.initialize();\nvar promise2 = engine.initialize();\nvar promise3 = engine.initialize(true);\n\nassert(promise1 === promise2);\nassert(promise3 !== promise1 && promise3 !== promise2);\n```\n\n<!-- section links -->\n\n[jQuery promise]: http://api.jquery.com/Types/#Promise\n\n#### Bloodhound#add(data)\n\nTakes one argument, `data`, which is expected to be an array. The data passed\nin will get added to the internal search index.\n\n```javascript\nengine.add([{ val: 'one' }, { val: 'two' }]);\n```\n\n#### Bloodhound#get(ids)\n\nReturns the data in the local search index corresponding to `ids`.\n\n```javascript\n  var engine = new Bloodhound({\n    local: [{ id: 1, name: 'dog' }, { id: 2, name: 'pig' }],\n    identify: function(obj) { return obj.id; },\n    queryTokenizer: Bloodhound.tokenizers.whitespace,\n    datumTokenizer: Bloodhound.tokenizers.whitespace\n  });\n\n  engine.get([1, 3]); // [{ id: 1, name: 'dog' }, null]\n```\n\n#### Bloodhound#search(query, sync, async)\n\nReturns the data that matches `query`. Matches found in the local search index\nwill be passed to the `sync` callback. If the data passed to `sync` doesn't \ncontain at least `sufficient` number of datums, `remote` data will be requested \nand then passed to the `async` callback.\n\n```javascript\nbloodhound.get(myQuery, sync, async);\n\nfunction sync(datums) {\n  console.log('datums from `local`, `prefetch`, and `#add`');\n  console.log(datums);\n}\n\nfunction async(datums) {\n  console.log('datums from `remote`');\n  console.log(datums);\n}\n```\n\n#### Bloodhound#clear()\n\nClears the internal search index that's powered by `local`, `prefetch`, and \n`#add`.\n\n```javascript\nengine.clear();\n```\n\n### Options\n\nWhen instantiating a Bloodhound suggestion engine, there are a number of \noptions you can configure.\n\n* `datumTokenizer` – A function with the signature `(datum)` that transforms a\n  datum into an array of string tokens. **Required**.\n\n* `queryTokenizer` – A function with the signature `(query)` that transforms a\n  query into an array of string tokens. **Required**.\n\n* `initialize` – If set to `false`, the Bloodhound instance will not be \n  implicitly initialized by the constructor function. Defaults to `true`.\n\n* `identify` – Given a datum, this function is expected to return a unique id\n  for it. Defaults to `JSON.stringify`. Note that it is **highly recommended**\n  to override this option.\n\n* `sufficient` – If the number of datums provided from the internal search \n  index is less than `sufficient`, `remote` will be used to backfill search\n  requests triggered by calling `#search`. Defaults to `5`.\n\n* `sorter` – A [compare function] used to sort data returned from the internal\n  search index.\n\n* `local` – An array of data or a function that returns an array of data. The \n  data will be added to the internal search index when `#initialize` is called.\n\n* `prefetch` – Can be a URL to a JSON file containing an array of data or, if \n  more configurability is needed, a [prefetch options hash](#prefetch).\n\n* `remote` – Can be a URL to fetch data from when the data provided by \n  the internal search index is insufficient or, if more configurability is \n  needed, a [remote options hash](#remote).\n\n<!-- section links -->\n\n[compare function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort\n\n### Prefetch\n\nPrefetched data is fetched and processed on initialization. If the browser \nsupports local storage, the processed data will be cached there to \nprevent additional network requests on subsequent page loads.\n\n**WARNING:** While it's possible to get away with it for smaller data sets, \nprefetched data isn't meant to contain entire sets of data. Rather, it should \nact as a first-level cache. Ignoring this warning means you'll run the risk of \nhitting [local storage limits].\n\nWhen configuring `prefetch`, the following options are available.\n\n* `url` – The URL prefetch data should be loaded from. **Required.**\n\n* `cache` – If `false`, will not attempt to read or write to local storage and\n  will always load prefetch data from `url` on initialization.  Defaults to \n  `true`.\n\n* `ttl` – The time (in milliseconds) the prefetched data should be cached in \n  local storage. Defaults to `86400000` (1 day).\n\n* `cacheKey` – The key that data will be stored in local storage under. \n  Defaults to value of `url`.\n\n* `thumbprint` – A string used for thumbprinting prefetched data. If this\n  doesn't match what's stored in local storage, the data will be refetched.\n\n* `prepare` – A function that provides a hook to allow you to prepare the \n  settings object passed to `transport` when a request is about to be made. \n  The function signature should be `prepare(settings)` where `settings` is the \n  default settings object created internally by the Bloodhound instance. The \n  `prepare` function should return a settings object. Defaults to the \n  [identity function].\n\n* `transform` – A function with the signature `transform(response)` that allows\n  you to transform the prefetch response before the Bloodhound instance operates \n  on it. Defaults to the [identity function].\n\n<!-- section links -->\n\n[local storage limits]: http://stackoverflow.com/a/2989317\n[identity function]: http://en.wikipedia.org/wiki/Identity_function\n\n### Remote\n\nBloodhound only goes to the network when the internal search engine cannot \nprovide a sufficient number of results. In order to prevent an obscene number \nof requests being made to the remote endpoint, requests are rate-limited.\n\nWhen configuring `remote`, the following options are available.\n\n* `url` – The URL remote data should be loaded from. **Required.**\n\n* `prepare` – A function that provides a hook to allow you to prepare the \n  settings object passed to `transport` when a request is about to be made. \n  The function signature should be `prepare(query, settings)`, where `query` is\n  the query `#search` was called with and `settings` is the default settings\n  object created internally by the Bloodhound instance. The `prepare` function\n  should return a settings object. Defaults to the [identity function].\n\n* `wildcard` – A convenience option for `prepare`. If set, `prepare` will be a\n  function that replaces the value of this option in `url` with the URI encoded\n  query.\n\n* `rateLimitBy` – The method used to rate-limit network requests. Can be either \n  `debounce` or `throttle`. Defaults to `debounce`.\n\n* `rateLimitWait` – The time interval in milliseconds that will be used by \n  `rateLimitBy`. Defaults to `300`.\n\n* `transform` – A function with the signature `transform(response)` that allows\n  you to transform the remote response before the Bloodhound instance operates \n  on it. Defaults to the [identity function].\n\n<!-- section links -->\n\n[identity function]: http://en.wikipedia.org/wiki/Identity_function\n"
  },
  {
    "path": "doc/jquery_typeahead.md",
    "content": "jQuery#typeahead\n----------------\n\nThe UI component of typeahead.js is available as a jQuery plugin. It's \nresponsible for rendering suggestions and handling DOM interactions.\n\nTable of Contents\n-----------------\n\n* [Features](#features)\n* [Usage](#usage)\n  * [API](#api)\n  * [Options](#options)\n  * [Datasets](#datasets)\n  * [Custom Events](#custom-events)\n  * [Class Names](#class-names)\n\nFeatures\n--------\n\n* Displays suggestions to end-users as they type\n* Shows top suggestion as a hint (i.e. background text)\n* Supports custom templates to allow for UI flexibility\n* Works well with RTL languages and input method editors\n* Highlights query matches within the suggestion\n* Triggers custom events to encourage extensibility\n\nUsage\n-----\n\n### API\n\n* [`jQuery#typeahead(options, [*datasets])`](#jquerytypeaheadoptions-datasets)\n* [`jQuery#typeahead('val')`](#jquerytypeaheadval)\n* [`jQuery#typeahead('val', val)`](#jquerytypeaheadval-val)\n* [`jQuery#typeahead('destroy')`](#jquerytypeaheaddestroy)\n* [`jQuery.fn.typeahead.noConflict()`](#jqueryfntypeaheadnoconflict)\n\n#### jQuery#typeahead(options, [\\*datasets])\n\nFor a given `input[type=\"text\"]`, enables typeahead functionality. `options` \nis an options hash that's used for configuration. Refer to [Options](#options) \nfor more info regarding the available configs. Subsequent arguments \n(`*datasets`), are individual option hashes for datasets. For more details \nregarding datasets, refer to [Datasets](#datasets).\n\n```javascript\n$('.typeahead').typeahead({\n  minLength: 3,\n  highlight: true\n},\n{\n  name: 'my-dataset',\n  source: mySource\n});\n```\n\n#### jQuery#typeahead('val')\n\nReturns the current value of the typeahead. The value is the text the user has \nentered into the `input` element.\n\n```javascript\nvar myVal = $('.typeahead').typeahead('val');\n```\n\n#### jQuery#typeahead('val', val)\n\nSets the value of the typeahead. This should be used in place of `jQuery#val`.\n\n```javascript\n$('.typeahead').typeahead('val', myVal);\n```\n\n#### jQuery#typeahead('open')\n\nOpens the suggestion menu.\n\n```javascript\n$('.typeahead').typeahead('open');\n```\n\n#### jQuery#typeahead('close')\n\nCloses the suggestion menu.\n\n```javascript\n$('.typeahead').typeahead('close');\n```\n\n#### jQuery#typeahead('destroy')\n\nRemoves typeahead functionality and reverts the `input` element back to its \noriginal state.\n\n```javascript\n$('.typeahead').typeahead('destroy');\n```\n\n#### jQuery.fn.typeahead.noConflict()\n\nReturns a reference to the typeahead plugin and reverts `jQuery.fn.typeahead` \nto its previous value. Can be used to avoid naming collisions. \n\n```javascript\nvar typeahead = jQuery.fn.typeahead.noConflict();\njQuery.fn._typeahead = typeahead;\n```\n\n### Options\n\nWhen initializing a typeahead, there are a number of options you can configure.\n\n* `highlight` – If `true`, when suggestions are rendered, pattern matches\n  for the current query in text nodes will be wrapped in a `strong` element with\n  its class set to `{{classNames.highlight}}`. Defaults to `false`.\n\n* `hint` – If `false`, the typeahead will not show a hint. Defaults to `true`.\n\n* `minLength` – The minimum character length needed before suggestions start \n  getting rendered. Defaults to `1`.\n\n* `classNames` – For overriding the default class names used. See \n  [Class Names](#class-names) for more details.\n\n### Datasets\n\nA typeahead is composed of one or more datasets. When an end-user modifies the\nvalue of a typeahead, each dataset will attempt to render suggestions for the\nnew value. \n\nFor most use cases, one dataset should suffice. It's only in the scenario where\nyou want rendered suggestions to be grouped based on some sort of categorical \nrelationship that you'd need to use multiple datasets. For example, on \ntwitter.com, the search typeahead groups results into recent searches, trends, \nand accounts – that would be a great use case for using multiple datasets.\n\nDatasets can be configured using the following options.\n\n* `source` – The backing data source for suggestions. Expected to be a function \n  with the signature `(query, syncResults, asyncResults)`. `syncResults` should\n  be called with suggestions computed synchronously and `asyncResults` should be \n  called with suggestions computed asynchronously (e.g. suggestions that come \n  for an AJAX request). `source` can also be a Bloodhound instance. \n  **Required**.\n\n* `async` – Lets the dataset know if async suggestions should be expected. If\n  not set, this information is inferred from the signature of `source` i.e.\n  if the `source` function expects 3 arguments, `async` will be set to `true`.\n\n* `name` – The name of the dataset. This will be appended to \n  `{{classNames.dataset}}-` to form the class name of the containing DOM \n  element. Must only consist of underscores, dashes, letters (`a-z`), and \n  numbers. Defaults to a random number.\n\n* `limit` – The max number of suggestions to be displayed. Defaults to `5`.\n\n* `display` – For a given suggestion, determines the string representation \n  of it. This will be used when setting the value of the input control after a \n  suggestion is selected. Can be either a key string or a function that \n  transforms a suggestion object into a string. Defaults to stringifying the \n  suggestion.\n\n* `templates` – A hash of templates to be used when rendering the dataset. Note\n  a precompiled template is a function that takes a JavaScript object as its\n  first argument and returns a HTML string.\n\n  * `notFound` – Rendered when `0` suggestions are available for the given \n    query. Can be either a HTML string or a precompiled template. If it's a \n    precompiled template, the passed in context will contain `query`.\n\n  * `pending` - Rendered when `0` synchronous suggestions are available but\n    asynchronous suggestions are expected. Can be either a HTML string or a \n    precompiled template. If it's a precompiled template, the passed in context \n    will contain `query`.\n\n  * `header`– Rendered at the top of the dataset when suggestions are present. \n    Can be either a HTML string or a precompiled template. If it's a precompiled \n    template, the passed in context will contain `query` and `suggestions`.\n\n  * `footer`– Rendered at the bottom of the dataset when suggestions are \n    present. Can be either a HTML string or a precompiled template. If it's a \n    precompiled template, the passed in context will contain `query` and\n    `suggestions`.\n\n  * `suggestion` – Used to render a single suggestion. If set, this has to be a \n    precompiled template. The associated suggestion object will serve as the \n    context. Defaults to the value of `display` wrapped in a `div` tag i.e. \n    `<div>{{value}}</div>`.\n\n### Custom Events\n\nThe following events get triggered on the input element during the life-cycle of\na typeahead.\n\n* `typeahead:active` – Fired when the typeahead moves to active state.\n\n* `typeahead:idle` – Fired when the typeahead moves to idle state.\n\n* `typeahead:open` – Fired when the results container is opened.\n\n* `typeahead:close` – Fired when the results container is closed.\n\n* `typeahead:change` – Normalized version of the native [`change` event]. \n  Fired when input loses focus and the value has changed since it originally \n  received focus.\n\n* `typeahead:render` – Fired when suggestions are rendered for a dataset. The\n  event handler will be invoked with 4 arguments: the jQuery event object, the\n  suggestions that were rendered, a flag indicating whether the suggestions\n  were fetched asynchronously, and the name of the dataset the rendering \n  occurred in.\n\n* `typeahead:select` – Fired when a suggestion is selected. The event handler \n  will be invoked with 2 arguments: the jQuery event object and the suggestion\n  object that was selected.\n\n* `typeahead:autocomplete` – Fired when a autocompletion occurs. The \n  event handler will be invoked with 2 arguments: the jQuery event object and \n  the suggestion object that was used for autocompletion.\n\n* `typeahead:cursorchange` – Fired when the results container cursor moves. The \n  event handler will be invoked with 2 arguments: the jQuery event object and \n  the suggestion object that was moved to.\n\n* `typeahead:asyncrequest` – Fired when an async request for suggestions is \n  sent. The event handler will be invoked with 3 arguments: the jQuery event \n  object, the current query, and the name of the dataset the async request \n  belongs to.\n\n* `typeahead:asynccancel` – Fired when an async request is cancelled. The event \n  handler will be invoked with 3 arguments: the jQuery event object, the current \n  query, and the name of the dataset the async request belonged to.\n\n* `typeahead:asyncreceive` – Fired when an async request completes. The event \n  handler will be invoked with 3 arguments: the jQuery event object, the current \n  query, and the name of the dataset the async request belongs to.\n\nExample usage:\n\n```\n$('.typeahead').bind('typeahead:select', function(ev, suggestion) {\n  console.log('Selection: ' + suggestion);\n});\n```\n\n**NOTE**: Every event does not supply the same arguments. See the event\ndescriptions above for details on each event's argument list.\n\n<!-- section links -->\n\n[`change` event]: https://developer.mozilla.org/en-US/docs/Web/Events/change\n\n### Class Names\n\n* `input` - Added to input that's initialized into a typeahead. Defaults to \n  `tt-input`.\n\n* `hint` - Added to hint input. Defaults to `tt-hint`.\n\n* `menu` - Added to menu element. Defaults to `tt-menu`.\n\n* `dataset` - Added to dataset elements. to Defaults to `tt-dataset`.\n\n* `suggestion` - Added to suggestion elements. Defaults to `tt-suggestion`.\n\n* `empty` - Added to menu element when it contains no content. Defaults to \n  `tt-empty`.\n\n* `open` - Added to menu element when it is opened. Defaults to `tt-open`.\n\n* `cursor` - Added to suggestion element when menu cursor moves to said \n  suggestion. Defaults to `tt-cursor`.\n\n* `highlight` - Added to the element that wraps highlighted text. Defaults to \n  `tt-highlight`.\n\nTo override any of these defaults, you can use the `classNames` option:\n\n```javascript\n$('.typeahead').typeahead({\n  classNames: {\n    input: 'Typeahead-input',\n    hint: 'Typeahead-hint',\n    selectable: 'Typeahead-selectable'\n  }\n});\n```\n"
  },
  {
    "path": "doc/migration/0.10.0.md",
    "content": "Migrating to typeahead.js v0.10.0\n=================================\n\nPreamble\n--------\n\nv0.10.0 of typeahead.js ended up being almost a complete rewrite. Many things \nstayed the same, but there were a handful of changes you need to be aware of \nif you plan on upgrading from an older version. This document aims to call out \nthose changes and explain what you need to do in order to have an painless \nupgrade.\n\nNotable Changes\n----------------\n\n### First Argument to the jQuery Plugin\n\nIn v0.10.0, the first argument to `jQuery#typeahead` is an options hash that\ncan be used to configure the behavior of the typeahead. This is in contrast\nto previous versions where `jQuery#typeahead` expected just a series of datasets\nto be passed to it:\n\n```javascript\n// pre-v0.10.0\n$('.typeahead').typeahead(myDataset);\n\n// v0.10.0\n$('.typeahead').typeahead({\n  highlight: true,\n  hint: false\n}, myDataset);\n```\n\nIf you're fine with the default configuration, you can just pass `null` as the \nfirst argument:\n\n```javascript\n$('.typeahead').typeahead(null, myDataset);\n```\n\n### Bloodhound Suggestion Engine\n\nThe most notable change in v0.10.0 is that typeahead.js has been decomposed into\na suggestion engine and a UI view. As part of this change, the way you configure\ndatasets has changed. Previously, a dataset config would have looked like:\n\n```javascript\n{\n  valueKey: 'num',\n  local: [{ num: 'one' }, { num: 'two' }, { num: 'three' }],\n  prefetch: '/prefetch',\n  remote: '/remote?q=%QUERY'\n}\n```\n\nIn v0.10.0, an equivalent dataset config would look like:\n\n```javascript\n{\n displayKey: 'num',\n source: mySource\n}\n```\n\nAs you can see, `local`, `prefetch`, and `remote` are no longer defined at the \ndataset level. Instead, all you set in a dataset config is `source`. `source` is\nexpected to be a function with the signature `function(query, callback)`. When a\ntypeahead's query changes, suggestions will be requested from  `source`. It's\nexpected `source` will compute the suggestion set and invoke `callback` with an array\nof suggestion objects. The typeahead will then go on to render those suggestions.\n\nIf you're wondering if you can still configure `local`, `prefetch`, and \n`remote`, don't worry, that's where the Bloodhound suggestion engine comes in.\nHere's how you would define `mySource` which was referenced in the previous \ncode snippet:\n\n```\nvar mySource = new Bloodhound({\n  datumTokenizer: function(d) { \n    return Bloodhound.tokenizers.whitespace(d.num); \n  },\n  queryTokenizer: Bloodhound.tokenizers.whitespace,\n  local: [{ num: 'one' }, { num: 'two' }, { num: 'three' }],\n  prefetch: '/prefetch',\n  remote: '/remote?q=%QUERY'\n});\n\n// this kicks off the loading and processing of local and prefetch data\n// the suggestion engine will be useless until it is initialized\nmySource.initialize();\n```\n\nIn the above snippet, a Bloodhound suggestion engine is initialized and that's \nwhat will be used as the source of your dataset. There's still one last thing\nthat needs to be done before you can use a Bloodhound suggestion engine as the \nsource of a dataset. Because datasets expect `source` to be function, the \nBloodhound instance needs to be wrapped in an adapter so it can meet that \nexpectation.\n\n```\nmySource = mySource.ttAdapter();\n```\n\nPut it all together:\n\n```javascript\nvar mySource = new Bloodhound({\n  datumTokenizer: function(d) { \n    return Bloodhound.tokenizers.whitespace(d.num); \n  },\n  queryTokenizer: Bloodhound.tokenizers.whitespace,\n  local: [{ num: 'one' }, { num: 'two' }, { num: 'three' }],\n  prefetch: '/prefetch',\n  remote: '/remote?q=%QUERY'\n});\n\nmySource.initialize();\n\n$('.typeahead').typeahead(null, {\n  displayKey: 'num',\n  source: mySource.ttAdapter()\n});\n```\n\n### Tokenization Methods Must Be Provided\n\nThe Bloodhound suggestion engine is token-based, so how datums and queries are\ntokenized plays a vital role in the quality of search results. Pre-v0.10.0,\nit was not possible to configure the tokenization method. Starting in v0.10.0,\nyou **must** specify how you want datums and queries tokenized. \n\nThe most common tokenization methods split a given string on whitespace or \nnon-word characters. Bloodhound provides implementations for those methods\nout of the box:\n\n```javascript\n// returns ['one', 'two', 'twenty-five']\nBloodhound.tokenizers.whitespace('  one two  twenty-five');\n\n// returns ['one', 'two', 'twenty', 'five']\nBloodhound.tokenizers.nonword('  one two  twenty-five');\n```\n\nFor query tokenization, you'll probably want to use one of the above methods.\nFor datum tokenization, this is where you may want to do something a tad bit\nmore advanced.\n\nFor datums, sometimes you want tokens to be dervied from more than one property. \nFor example, if you were building a search engine for GitHub repositories, it'd \nprobably be wise to have tokens derived from the repo's name, owner, and \nprimary language:\n\n```javascript\nvar repos = [\n  { name: 'example', owner: 'John Doe', language: 'JavaScript' },\n  { name: 'another example', owner: 'Joe Doe', language: 'Scala' }\n];\n\nfunction customTokenizer(datum) {\n  var nameTokens = Bloodhound.tokenizers.whitespace(datum.name);\n  var ownerTokens = Bloodhound.tokenizers.whitespace(datum.owner);\n  var languageTokens = Bloodhound.tokenizers.whitespace(datum.language);\n\n  return nameTokens.concat(ownerTokens).concat(languageTokens);\n}\n```\n\nThere may also be the scenario where you want datum tokenization to be performed\non the backend. The best way to do that is to just add a property to your datums \nthat contains those tokens. You can then provide a tokenizer that just returns \nthe already existing tokens:\n\n```javascript\nvar sports = [\n  { value: 'football', tokens: ['football', 'pigskin'] },\n  { value: 'basketball', tokens: ['basketball', 'bball'] }\n];\n\nfunction customTokenizer(datum) { return datum.tokens; }\n```\n\nThere are plenty of other ways you could go about tokenizing datums, it really\njust depends on what you are trying to accomplish.\n\n### String Datums Are No Longer Supported\n\nDropping support for string datums was a difficult choice, but in the end it\nmade sense for a number of reasons. If you still want to hydrate the suggestion \nengine with string datums, you'll need to use the `filter` function:\n\n```javascript\nvar engine = new Bloodhound({\n  prefetch: {\n    url: '/data',\n    filter: function(data) {\n      // assume data is an array of strings e.g. ['one', 'two', 'three']\n      return $.map(data, function(str) { return { value: str }; });\n    },\n    datumTokenizer: function(d) { \n      return Bloodhound.tokenizers.whitespace(d.value); \n    },\n    queryTokenizer: Bloodhound.tokenizers.whitespace\n  }\n});\n```\n\n### Precompiled Templates Are Now Required\n\nIn previous versions of typeahead.js, you could specify a string template along\nwith the templating engine that should be used to compile/render it. In \nv0.10.0, you can no longer specify templating engines; instead you must provide\nprecompiled templates. Precompiled templates are functions that take one \nargument: the context the template should be rendered with. \n\nMost of the popular templating engines allow for the creation of precompiled \ntemplates. For example, you can generate one using Handlebars by doing the\nfollowing:\n\n```javascript\nvar precompiledTemplate = Handlebars.compile('<p>{{value}}</p>');\n```\n\n[Handlebars]: http://handlebarsjs.com/\n\n### CSS Class Changes\n\n`tt-is-under-cursor` is now `tt-cursor` - Applied to a hovered-on suggestion (either via cursor or arrow key).\n\n`tt-query` is now `tt-input` - Applied to the typeahead input field.\n\nSomething Missing?\n------------------\n\nIf something is missing from this migration guide, pull requests are accepted :)\n"
  },
  {
    "path": "karma.conf.js",
    "content": "module.exports = function(config) {\n  config.set({\n    basePath: '',\n\n    preprocessors: {\n      'src/**/*.js': 'coverage'\n    },\n\n    reporters: ['progress', 'coverage'],\n\n    browsers: ['Chrome'],\n\n    frameworks: ['jasmine'],\n\n    coverageReporter: {\n      type: 'html',\n      dir: 'test/coverage/'\n    },\n\n    files: [\n      'bower_components/jquery/jquery.js',\n      'src/common/utils.js',\n      'src/bloodhound/version.js',\n      'src/bloodhound/tokenizers.js',\n      'src/bloodhound/lru_cache.js',\n      'src/bloodhound/persistent_storage.js',\n      'src/bloodhound/transport.js',\n      'src/bloodhound/remote.js',\n      'src/bloodhound/prefetch.js',\n      'src/bloodhound/search_index.js',\n      'src/bloodhound/options_parser.js',\n      'src/bloodhound/bloodhound.js',\n      'src/typeahead/www.js',\n      'src/typeahead/event_bus.js',\n      'src/typeahead/event_emitter.js',\n      'src/typeahead/highlight.js',\n      'src/typeahead/input.js',\n      'src/typeahead/dataset.js',\n      'src/typeahead/menu.js',\n      'src/typeahead/default_menu.js',\n      'src/typeahead/typeahead.js',\n      'src/typeahead/plugin.js',\n      'test/fixtures/**/*',\n      'bower_components/jasmine-jquery/lib/jasmine-jquery.js',\n      'bower_components/jasmine-ajax/lib/mock-ajax.js',\n      'test/helpers/**/*',\n      'test/**/*_spec.js'\n    ]\n  });\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"typeahead.js\",\n  \"description\": \"fast and fully-featured autocomplete library\",\n  \"keywords\": [\n    \"typeahead\",\n    \"autocomplete\"\n  ],\n  \"homepage\": \"http://twitter.github.com/typeahead.js\",\n  \"bugs\": \"https://github.com/twitter/typeahead.js/issues\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/twitter/typeahead.js.git\"\n  },\n  \"author\": {\n    \"name\": \"Twitter, Inc.\",\n    \"url\": \"https://twitter.com/twitteross\"\n  },\n  \"contributors\": [\n    {\n      \"name\": \"Jake Harding\",\n      \"url\": \"https://twitter.com/JakeHarding\"\n    },\n    {\n      \"name\": \"Tim Trueman\",\n      \"url\": \"https://twitter.com/timtrueman\"\n    },\n    {\n      \"name\": \"Veljko Skarich\",\n      \"url\": \"https://twitter.com/vskarich\"\n    }\n  ],\n  \"dependencies\": {\n    \"jquery\": \">=1.7\"\n  },\n  \"devDependencies\": {\n    \"chai\": \"^1.9.1\",\n    \"colors\": \"^0.6.2\",\n    \"grunt\": \"~0.4\",\n    \"grunt-concurrent\": \"^0.5.0\",\n    \"grunt-contrib-clean\": \"~0.4.0\",\n    \"grunt-contrib-concat\": \"~0.1\",\n    \"grunt-contrib-connect\": \"~0.1\",\n    \"grunt-contrib-jshint\": \"~0.8\",\n    \"grunt-contrib-uglify\": \"~0.2.6\",\n    \"grunt-contrib-watch\": \"~0.2\",\n    \"grunt-exec\": \"~0.4.5\",\n    \"grunt-sed\": \"~0.1\",\n    \"grunt-step\": \"~0.2.0\",\n    \"grunt-umd\": \"^2.3.3\",\n    \"karma\": \"^0.12.22\",\n    \"karma-chrome-launcher\": \"^0.1.4\",\n    \"karma-coverage\": \"^0.2.6\",\n    \"karma-firefox-launcher\": \"^0.1.3\",\n    \"karma-jasmine\": \"^0.1.5\",\n    \"karma-opera-launcher\": \"^0.1.0\",\n    \"karma-phantomjs-launcher\": \"^0.1.4\",\n    \"karma-safari-launcher\": \"^0.1.1\",\n    \"mocha\": \"^1.20.1\",\n    \"semver\": \"~1.1.3\",\n    \"underscore\": \"^1.6.0\",\n    \"yiewd\": \"^0.5.0\"\n  },\n  \"scripts\": {\n    \"test\": \"./node_modules/karma/bin/karma start --single-run --browsers PhantomJS\"\n  },\n  \"version\": \"0.11.1\",\n  \"main\": \"dist/typeahead.bundle.js\"\n}"
  },
  {
    "path": "src/bloodhound/bloodhound.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Bloodhound = (function() {\n  'use strict';\n\n  var old;\n\n  old = window && window.Bloodhound;\n\n  // constructor\n  // -----------\n\n  function Bloodhound(o) {\n    o = oParser(o);\n\n    this.sorter = o.sorter;\n    this.identify = o.identify;\n    this.sufficient = o.sufficient;\n\n    this.local = o.local;\n    this.remote = o.remote ? new Remote(o.remote) : null;\n    this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;\n\n    // the backing data structure used for fast pattern matching\n    this.index = new SearchIndex({\n      identify: this.identify,\n      datumTokenizer: o.datumTokenizer,\n      queryTokenizer: o.queryTokenizer\n    });\n\n    // hold off on intialization if the intialize option was explicitly false\n    o.initialize !== false && this.initialize();\n  }\n\n  // static methods\n  // --------------\n\n  Bloodhound.noConflict = function noConflict() {\n    window && (window.Bloodhound = old);\n    return Bloodhound;\n  };\n\n  Bloodhound.tokenizers = tokenizers;\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Bloodhound.prototype, {\n\n    // ### super secret stuff used for integration with jquery plugin\n\n    __ttAdapter: function ttAdapter() {\n      var that = this;\n\n      return this.remote ? withAsync : withoutAsync;\n\n      function withAsync(query, sync, async) {\n        return that.search(query, sync, async);\n      }\n\n      function withoutAsync(query, sync) {\n        return that.search(query, sync);\n      }\n    },\n\n    // ### private\n\n    _loadPrefetch: function loadPrefetch() {\n      var that = this, deferred, serialized;\n\n      deferred = $.Deferred();\n\n      if (!this.prefetch) {\n        deferred.resolve();\n      }\n\n      else if (serialized = this.prefetch.fromCache()) {\n        this.index.bootstrap(serialized);\n        deferred.resolve();\n      }\n\n      else {\n        this.prefetch.fromNetwork(done);\n      }\n\n      return deferred.promise();\n\n      function done(err, data) {\n        if (err) { return deferred.reject(); }\n\n        that.add(data);\n        that.prefetch.store(that.index.serialize());\n        deferred.resolve();\n      }\n    },\n\n    _initialize: function initialize() {\n      var that = this, deferred;\n\n      // in case this is a reinitialization, clear previous data\n      this.clear();\n\n      (this.initPromise = this._loadPrefetch())\n      .done(addLocalToIndex); // local must be added to index after prefetch\n\n      return this.initPromise;\n\n      function addLocalToIndex() { that.add(that.local); }\n    },\n\n    // ### public\n\n    initialize: function initialize(force) {\n      return !this.initPromise || force ? this._initialize() : this.initPromise;\n    },\n\n    // TODO: before initialize what happens?\n    add: function add(data) {\n      this.index.add(data);\n      return this;\n    },\n\n    get: function get(ids) {\n      ids = _.isArray(ids) ? ids : [].slice.call(arguments);\n      return this.index.get(ids);\n    },\n\n    search: function search(query, sync, async) {\n      var that = this, local;\n\n      local = this.sorter(this.index.search(query));\n\n      // return a copy to guarantee no changes within this scope\n      // as this array will get used when processing the remote results\n      sync(this.remote ? local.slice() : local);\n\n      if (this.remote && local.length < this.sufficient) {\n        this.remote.get(query, processRemote);\n      }\n\n      else if (this.remote) {\n        // #149: prevents outdated rate-limited requests from being sent\n        this.remote.cancelLastRequest();\n      }\n\n      return this;\n\n      function processRemote(remote) {\n        var nonDuplicates = [];\n\n        // exclude duplicates\n        _.each(remote, function(r) {\n           !_.some(local, function(l) {\n            return that.identify(r) === that.identify(l);\n          }) && nonDuplicates.push(r);\n        });\n\n        async && async(nonDuplicates);\n      }\n    },\n\n    all: function all() {\n      return this.index.all();\n    },\n\n    clear: function clear() {\n      this.index.reset();\n      return this;\n    },\n\n    clearPrefetchCache: function clearPrefetchCache() {\n      this.prefetch && this.prefetch.clear();\n      return this;\n    },\n\n    clearRemoteCache: function clearRemoteCache() {\n      Transport.resetCache();\n      return this;\n    },\n\n    // DEPRECATED: will be removed in v1\n    ttAdapter: function ttAdapter() {\n      return this.__ttAdapter();\n    }\n  });\n\n  return Bloodhound;\n})();\n"
  },
  {
    "path": "src/bloodhound/lru_cache.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\n// inspired by https://github.com/jharding/lru-cache\n\nvar LruCache = (function() {\n  'use strict';\n\n  function LruCache(maxSize) {\n    this.maxSize = _.isNumber(maxSize) ? maxSize : 100;\n    this.reset();\n\n    // if max size is less than 0, provide a noop cache\n    if (this.maxSize <= 0) {\n      this.set = this.get = $.noop;\n    }\n  }\n\n  _.mixin(LruCache.prototype, {\n    set: function set(key, val) {\n      var tailItem = this.list.tail, node;\n\n      // at capacity\n      if (this.size >= this.maxSize) {\n        this.list.remove(tailItem);\n        delete this.hash[tailItem.key];\n\n        this.size--;\n      }\n\n      // writing over existing key\n      if (node = this.hash[key]) {\n        node.val = val;\n        this.list.moveToFront(node);\n      }\n\n      // new key\n      else {\n        node = new Node(key, val);\n\n        this.list.add(node);\n        this.hash[key] = node;\n\n        this.size++;\n      }\n    },\n\n    get: function get(key) {\n      var node = this.hash[key];\n\n      if (node) {\n        this.list.moveToFront(node);\n        return node.val;\n      }\n    },\n\n    reset: function reset() {\n      this.size = 0;\n      this.hash = {};\n      this.list = new List();\n    }\n  });\n\n  function List() {\n    this.head = this.tail = null;\n  }\n\n  _.mixin(List.prototype, {\n    add: function add(node) {\n      if (this.head) {\n        node.next = this.head;\n        this.head.prev = node;\n      }\n\n      this.head = node;\n      this.tail = this.tail || node;\n    },\n\n    remove: function remove(node) {\n      node.prev ? node.prev.next = node.next : this.head = node.next;\n      node.next ? node.next.prev = node.prev : this.tail = node.prev;\n    },\n\n    moveToFront: function(node) {\n      this.remove(node);\n      this.add(node);\n    }\n  });\n\n  function Node(key, val) {\n    this.key = key;\n    this.val = val;\n    this.prev = this.next = null;\n  }\n\n  return LruCache;\n\n})();\n"
  },
  {
    "path": "src/bloodhound/options_parser.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar oParser = (function() {\n  'use strict';\n\n  return function parse(o) {\n    var defaults, sorter;\n\n    defaults = {\n      initialize: true,\n      identify: _.stringify,\n      datumTokenizer: null,\n      queryTokenizer: null,\n      sufficient: 5,\n      sorter: null,\n      local: [],\n      prefetch: null,\n      remote: null\n    };\n\n    o = _.mixin(defaults, o || {});\n\n    // throw error if required options are not set\n    !o.datumTokenizer && $.error('datumTokenizer is required');\n    !o.queryTokenizer && $.error('queryTokenizer is required');\n\n    sorter = o.sorter;\n    o.sorter = sorter ? function(x) { return x.sort(sorter); } : _.identity;\n\n    o.local = _.isFunction(o.local) ? o.local() : o.local;\n    o.prefetch = parsePrefetch(o.prefetch);\n    o.remote = parseRemote(o.remote);\n\n    return o;\n  };\n\n  function parsePrefetch(o) {\n    var defaults;\n\n    if (!o) { return null; }\n\n    defaults = {\n      url: null,\n      ttl: 24 * 60 * 60 * 1000, // 1 day\n      cache: true,\n      cacheKey: null,\n      thumbprint: '',\n      prepare: _.identity,\n      transform: _.identity,\n      transport: null\n    };\n\n    // support basic (url) and advanced configuration\n    o = _.isString(o) ? { url: o } : o;\n    o = _.mixin(defaults, o);\n\n    // throw error if required options are not set\n    !o.url && $.error('prefetch requires url to be set');\n\n    // DEPRECATED: filter will be dropped in v1\n    o.transform = o.filter || o.transform;\n\n    o.cacheKey = o.cacheKey || o.url;\n    o.thumbprint = VERSION + o.thumbprint;\n    o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;\n\n    return o;\n  }\n\n  function parseRemote(o) {\n    var defaults;\n\n    if (!o) { return; }\n\n    defaults = {\n      url: null,\n      cache: true, // leave undocumented\n      prepare: null,\n      replace: null,\n      wildcard: null,\n      limiter: null,\n      rateLimitBy: 'debounce',\n      rateLimitWait: 300,\n      transform: _.identity,\n      transport: null\n    };\n\n    // support basic (url) and advanced configuration\n    o = _.isString(o) ? { url: o } : o;\n    o = _.mixin(defaults, o);\n\n    // throw error if required options are not set\n    !o.url && $.error('remote requires url to be set');\n\n    // DEPRECATED: filter will be dropped in v1\n    o.transform = o.filter || o.transform;\n\n    o.prepare = toRemotePrepare(o);\n    o.limiter = toLimiter(o);\n    o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;\n\n    delete o.replace;\n    delete o.wildcard;\n    delete o.rateLimitBy;\n    delete o.rateLimitWait;\n\n    return o;\n  }\n\n  function toRemotePrepare(o) {\n    var prepare, replace, wildcard;\n\n    prepare = o.prepare;\n    replace = o.replace;\n    wildcard = o.wildcard;\n\n    if (prepare) { return prepare; }\n\n    if (replace) {\n      prepare = prepareByReplace;\n    }\n\n    else if (o.wildcard) {\n      prepare = prepareByWildcard;\n    }\n\n    else {\n      prepare = idenityPrepare;\n    }\n\n    return prepare;\n\n    function prepareByReplace(query, settings) {\n      settings.url = replace(settings.url, query);\n      return settings;\n    }\n\n    function prepareByWildcard(query, settings) {\n      settings.url = settings.url.replace(wildcard, encodeURIComponent(query));\n      return settings;\n    }\n\n    function idenityPrepare(query, settings) {\n      return settings;\n    }\n  }\n\n  function toLimiter(o) {\n    var limiter, method, wait;\n\n    limiter = o.limiter;\n    method = o.rateLimitBy;\n    wait = o.rateLimitWait;\n\n    if (!limiter) {\n      limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);\n    }\n\n    return limiter;\n\n    function debounce(wait) {\n      return function debounce(fn) { return _.debounce(fn, wait); };\n    }\n\n    function throttle(wait) {\n      return function throttle(fn) { return _.throttle(fn, wait); };\n    }\n  }\n\n  function callbackToDeferred(fn) {\n    return function wrapper(o) {\n      var deferred = $.Deferred();\n\n      fn(o, onSuccess, onError);\n\n      return deferred;\n\n      function onSuccess(resp) {\n        // defer in case fn is synchronous, otherwise done\n        // and always handlers will be attached after the resolution\n        _.defer(function() { deferred.resolve(resp); });\n      }\n\n      function onError(err) {\n        // defer in case fn is synchronous, otherwise done\n        // and always handlers will be attached after the resolution\n        _.defer(function() { deferred.reject(err); });\n      }\n    };\n  }\n})();\n"
  },
  {
    "path": "src/bloodhound/persistent_storage.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar PersistentStorage = (function() {\n  'use strict';\n\n  var LOCAL_STORAGE;\n\n  try {\n    LOCAL_STORAGE = window.localStorage;\n\n    // while in private browsing mode, some browsers make\n    // localStorage available, but throw an error when used\n    LOCAL_STORAGE.setItem('~~~', '!');\n    LOCAL_STORAGE.removeItem('~~~');\n  } catch (err) {\n    LOCAL_STORAGE = null;\n  }\n\n  // constructor\n  // -----------\n\n  function PersistentStorage(namespace, override) {\n    this.prefix = ['__', namespace, '__'].join('');\n    this.ttlKey = '__ttl__';\n    this.keyMatcher = new RegExp('^' + _.escapeRegExChars(this.prefix));\n\n    // for testing purpose\n    this.ls = override || LOCAL_STORAGE;\n\n    // if local storage isn't available, everything becomes a noop\n    !this.ls && this._noop();\n  }\n\n  // instance methods\n  // ----------------\n\n  _.mixin(PersistentStorage.prototype, {\n    // ### private\n\n    _prefix: function(key) {\n      return this.prefix + key;\n    },\n\n    _ttlKey: function(key) {\n      return this._prefix(key) + this.ttlKey;\n    },\n\n    _noop: function() {\n      this.get =\n      this.set =\n      this.remove =\n      this.clear =\n      this.isExpired = _.noop;\n    },\n\n    _safeSet: function(key, val) {\n      try {\n        this.ls.setItem(key, val);\n      } catch (err) {\n        // hit the localstorage limit so clean up and better luck next time\n        if (err.name === 'QuotaExceededError') {\n          this.clear();\n          this._noop();\n        }\n      }\n    },\n\n    // ### public\n\n    get: function(key) {\n      if (this.isExpired(key)) {\n        this.remove(key);\n      }\n\n      return decode(this.ls.getItem(this._prefix(key)));\n    },\n\n    set: function(key, val, ttl) {\n      if (_.isNumber(ttl)) {\n        this._safeSet(this._ttlKey(key), encode(now() + ttl));\n      }\n\n      else {\n        this.ls.removeItem(this._ttlKey(key));\n      }\n\n      return this._safeSet(this._prefix(key), encode(val));\n    },\n\n    remove: function(key) {\n      this.ls.removeItem(this._ttlKey(key));\n      this.ls.removeItem(this._prefix(key));\n\n      return this;\n    },\n\n    clear: function() {\n      var i, keys = gatherMatchingKeys(this.keyMatcher);\n\n      for (i = keys.length; i--;) {\n        this.remove(keys[i]);\n      }\n\n      return this;\n    },\n\n    isExpired: function(key) {\n      var ttl = decode(this.ls.getItem(this._ttlKey(key)));\n\n      return _.isNumber(ttl) && now() > ttl ? true : false;\n    }\n  });\n\n  return PersistentStorage;\n\n  // helper functions\n  // ----------------\n\n  function now() {\n    return new Date().getTime();\n  }\n\n  function encode(val) {\n    // convert undefined to null to avoid issues with JSON.parse\n    return JSON.stringify(_.isUndefined(val) ? null : val);\n  }\n\n  function decode(val) {\n    return $.parseJSON(val);\n  }\n\n  function gatherMatchingKeys(keyMatcher) {\n    var i, key, keys = [], len = LOCAL_STORAGE.length;\n\n    for (i = 0; i < len; i++) {\n      if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {\n        keys.push(key.replace(keyMatcher, ''));\n      }\n    }\n\n    return keys;\n  }\n})();\n"
  },
  {
    "path": "src/bloodhound/prefetch.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Prefetch = (function() {\n  'use strict';\n\n  var keys;\n\n  keys = { data: 'data', protocol: 'protocol', thumbprint: 'thumbprint' };\n\n  // constructor\n  // -----------\n\n  // defaults for options are handled in options_parser\n  function Prefetch(o) {\n    this.url = o.url;\n    this.ttl = o.ttl;\n    this.cache = o.cache;\n    this.prepare = o.prepare;\n    this.transform = o.transform;\n    this.transport = o.transport;\n    this.thumbprint = o.thumbprint;\n\n    this.storage = new PersistentStorage(o.cacheKey);\n  }\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Prefetch.prototype, {\n\n    // ### private\n\n    _settings: function settings() {\n      return { url: this.url, type: 'GET', dataType: 'json' };\n    },\n\n    // ### public\n\n    store: function store(data) {\n      if (!this.cache) { return; }\n\n      this.storage.set(keys.data, data, this.ttl);\n      this.storage.set(keys.protocol, location.protocol, this.ttl);\n      this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);\n    },\n\n    fromCache: function fromCache() {\n      var stored = {}, isExpired;\n\n      if (!this.cache) { return null; }\n\n      stored.data = this.storage.get(keys.data);\n      stored.protocol = this.storage.get(keys.protocol);\n      stored.thumbprint = this.storage.get(keys.thumbprint);\n\n      // the stored data is considered expired if the thumbprints\n      // don't match or if the protocol it was originally stored under\n      // has changed\n      isExpired =\n        stored.thumbprint !== this.thumbprint ||\n        stored.protocol !== location.protocol;\n\n      // TODO: if expired, remove from local storage\n\n      return stored.data && !isExpired ? stored.data : null;\n    },\n\n    fromNetwork: function(cb) {\n      var that = this, settings;\n\n      if (!cb) { return; }\n\n      settings = this.prepare(this._settings());\n      this.transport(settings).fail(onError).done(onResponse);\n\n      function onError() { cb(true); }\n      function onResponse(resp) { cb(null, that.transform(resp)); }\n    },\n\n    clear: function clear() {\n      this.storage.clear();\n      return this;\n    }\n  });\n\n  return Prefetch;\n})();\n"
  },
  {
    "path": "src/bloodhound/remote.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Remote = (function() {\n  'use strict';\n\n  // constructor\n  // -----------\n\n  function Remote(o) {\n    this.url = o.url;\n    this.prepare = o.prepare;\n    this.transform = o.transform;\n\n    this.transport = new Transport({\n      cache: o.cache,\n      limiter: o.limiter,\n      transport: o.transport\n    });\n  }\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Remote.prototype, {\n    // ### private\n\n    _settings: function settings() {\n      return { url: this.url, type: 'GET', dataType: 'json' };\n    },\n\n    // ### public\n\n    get: function get(query, cb) {\n      var that = this, settings;\n\n      if (!cb) { return; }\n\n      query = query || '';\n      settings = this.prepare(query, this._settings());\n\n      return this.transport.get(settings, onResponse);\n\n      function onResponse(err, resp) {\n        err ? cb([]) : cb(that.transform(resp));\n      }\n    },\n\n    cancelLastRequest: function cancelLastRequest() {\n      this.transport.cancel();\n    }\n  });\n\n  return Remote;\n})();\n"
  },
  {
    "path": "src/bloodhound/search_index.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar SearchIndex = window.SearchIndex = (function() {\n  'use strict';\n\n  var CHILDREN = 'c', IDS = 'i';\n\n  // constructor\n  // -----------\n\n  function SearchIndex(o) {\n    o = o || {};\n\n    if (!o.datumTokenizer || !o.queryTokenizer) {\n      $.error('datumTokenizer and queryTokenizer are both required');\n    }\n\n    this.identify = o.identify || _.stringify;\n    this.datumTokenizer = o.datumTokenizer;\n    this.queryTokenizer = o.queryTokenizer;\n\n    this.reset();\n  }\n\n  // instance methods\n  // ----------------\n\n  _.mixin(SearchIndex.prototype, {\n\n    // ### public\n\n    bootstrap: function bootstrap(o) {\n      this.datums = o.datums;\n      this.trie = o.trie;\n    },\n\n    add: function(data) {\n      var that = this;\n\n      data = _.isArray(data) ? data : [data];\n\n      _.each(data, function(datum) {\n        var id, tokens;\n\n        that.datums[id = that.identify(datum)] = datum;\n        tokens = normalizeTokens(that.datumTokenizer(datum));\n\n        _.each(tokens, function(token) {\n          var node, chars, ch;\n\n          node = that.trie;\n          chars = token.split('');\n\n          while (ch = chars.shift()) {\n            node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());\n            node[IDS].push(id);\n          }\n        });\n      });\n    },\n\n    get: function get(ids) {\n      var that = this;\n\n      return _.map(ids, function(id) { return that.datums[id]; });\n    },\n\n    search: function search(query) {\n      var that = this, tokens, matches;\n\n      tokens = normalizeTokens(this.queryTokenizer(query));\n\n      _.each(tokens, function(token) {\n        var node, chars, ch, ids;\n\n        // previous tokens didn't share any matches\n        if (matches && matches.length === 0) {\n          return false;\n        }\n\n        node = that.trie;\n        chars = token.split('');\n\n        while (node && (ch = chars.shift())) {\n          node = node[CHILDREN][ch];\n        }\n\n        if (node && chars.length === 0) {\n          ids = node[IDS].slice(0);\n          matches = matches ? getIntersection(matches, ids) : ids;\n        }\n\n        // break early if we find out there are no possible matches\n        else {\n          matches = [];\n          return false;\n        }\n      });\n\n      return matches ?\n        _.map(unique(matches), function(id) { return that.datums[id]; }) : [];\n    },\n\n    all: function all() {\n      var values = [];\n\n      for (var key in this.datums) {\n        values.push(this.datums[key]);\n      }\n\n      return values;\n    },\n\n    reset: function reset() {\n      this.datums = {};\n      this.trie = newNode();\n    },\n\n    serialize: function serialize() {\n      return { datums: this.datums, trie: this.trie };\n    }\n  });\n\n  return SearchIndex;\n\n  // helper functions\n  // ----------------\n\n  function normalizeTokens(tokens) {\n   // filter out falsy tokens\n    tokens = _.filter(tokens, function(token) { return !!token; });\n\n    // normalize tokens\n    tokens = _.map(tokens, function(token) { return token.toLowerCase(); });\n\n    return tokens;\n  }\n\n  function newNode() {\n    var node = {};\n\n    node[IDS] = [];\n    node[CHILDREN] = {};\n\n    return node;\n  }\n\n  function unique(array) {\n    var seen = {}, uniques = [];\n\n    for (var i = 0, len = array.length; i < len; i++) {\n      if (!seen[array[i]]) {\n        seen[array[i]] = true;\n        uniques.push(array[i]);\n      }\n    }\n\n    return uniques;\n  }\n\n  function getIntersection(arrayA, arrayB) {\n    var ai = 0, bi = 0, intersection = [];\n\n    arrayA = arrayA.sort();\n    arrayB = arrayB.sort();\n\n    var lenArrayA = arrayA.length, lenArrayB = arrayB.length;\n\n    while (ai < lenArrayA && bi < lenArrayB) {\n      if (arrayA[ai] < arrayB[bi]) {\n        ai++;\n      }\n\n      else if (arrayA[ai] > arrayB[bi]) {\n        bi++;\n      }\n\n      else {\n        intersection.push(arrayA[ai]);\n        ai++;\n        bi++;\n      }\n    }\n\n    return intersection;\n  }\n})();\n"
  },
  {
    "path": "src/bloodhound/tokenizers.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar tokenizers = (function() {\n  'use strict';\n\n  return {\n    nonword: nonword,\n    whitespace: whitespace,\n    obj: {\n      nonword: getObjTokenizer(nonword),\n      whitespace: getObjTokenizer(whitespace)\n    }\n  };\n\n  function whitespace(str) {\n    str = _.toStr(str);\n    return str ? str.split(/\\s+/) : [];\n  }\n\n  function nonword(str) {\n    str = _.toStr(str);\n    return str ? str.split(/\\W+/) : [];\n  }\n\n  function getObjTokenizer(tokenizer) {\n    return function setKey(keys) {\n      keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);\n\n      return function tokenize(o) {\n        var tokens = [];\n\n        _.each(keys, function(k) {\n          tokens = tokens.concat(tokenizer(_.toStr(o[k])));\n        });\n\n        return tokens;\n      };\n    };\n  }\n})();\n"
  },
  {
    "path": "src/bloodhound/transport.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Transport = (function() {\n  'use strict';\n\n  var pendingRequestsCount = 0,\n      pendingRequests = {},\n      maxPendingRequests = 6,\n      sharedCache = new LruCache(10);\n\n  // constructor\n  // -----------\n\n  function Transport(o) {\n    o = o || {};\n\n    this.cancelled = false;\n    this.lastReq = null;\n\n    this._send = o.transport;\n    this._get = o.limiter ? o.limiter(this._get) : this._get;\n\n    this._cache = o.cache === false ? new LruCache(0) : sharedCache;\n  }\n\n  // static methods\n  // --------------\n\n  Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {\n    maxPendingRequests = num;\n  };\n\n  Transport.resetCache = function resetCache() {\n    sharedCache.reset();\n  };\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Transport.prototype, {\n\n    // ### private\n\n    _fingerprint: function fingerprint(o) {\n      o = o || {};\n      return o.url + o.type + $.param(o.data || {});\n    },\n\n    _get: function(o, cb) {\n      var that = this, fingerprint, jqXhr;\n\n      fingerprint = this._fingerprint(o);\n\n      // #149: don't make a network request if there has been a cancellation\n      // or if the url doesn't match the last url Transport#get was invoked with\n      if (this.cancelled || fingerprint !== this.lastReq) { return; }\n\n      // a request is already in progress, piggyback off of it\n      if (jqXhr = pendingRequests[fingerprint]) {\n        jqXhr.done(done).fail(fail);\n      }\n\n      // under the pending request threshold, so fire off a request\n      else if (pendingRequestsCount < maxPendingRequests) {\n        pendingRequestsCount++;\n        pendingRequests[fingerprint] =\n          this._send(o).done(done).fail(fail).always(always);\n      }\n\n      // at the pending request threshold, so hang out in the on deck circle\n      else {\n        this.onDeckRequestArgs = [].slice.call(arguments, 0);\n      }\n\n      function done(resp) {\n        cb(null, resp);\n        that._cache.set(fingerprint, resp);\n      }\n\n      function fail() {\n        cb(true);\n      }\n\n      function always() {\n        pendingRequestsCount--;\n        delete pendingRequests[fingerprint];\n\n        // ensures request is always made for the last query\n        if (that.onDeckRequestArgs) {\n          that._get.apply(that, that.onDeckRequestArgs);\n          that.onDeckRequestArgs = null;\n        }\n      }\n    },\n\n    // ### public\n\n    get: function(o, cb) {\n      var resp, fingerprint;\n\n      cb = cb || $.noop;\n      o = _.isString(o) ? { url: o } : (o || {});\n\n      fingerprint = this._fingerprint(o);\n\n      this.cancelled = false;\n      this.lastReq = fingerprint;\n\n      // in-memory cache hit\n      if (resp = this._cache.get(fingerprint)) {\n        cb(null, resp);\n      }\n\n      // go to network\n      else {\n        this._get(o, cb);\n      }\n    },\n\n    cancel: function() {\n      this.cancelled = true;\n    }\n  });\n\n  return Transport;\n})();\n"
  },
  {
    "path": "src/bloodhound/version.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar VERSION = '%VERSION%';\n"
  },
  {
    "path": "src/common/utils.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar _ = (function() {\n  'use strict';\n\n  return {\n    isMsie: function() {\n      // from https://github.com/ded/bowser/blob/master/bowser.js\n      return (/(msie|trident)/i).test(navigator.userAgent) ?\n        navigator.userAgent.match(/(msie |rv:)(\\d+(.\\d+)?)/i)[2] : false;\n    },\n\n    isBlankString: function(str) { return !str || /^\\s*$/.test(str); },\n\n    // http://stackoverflow.com/a/6969486\n    escapeRegExChars: function(str) {\n      return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, '\\\\$&');\n    },\n\n    isString: function(obj) { return typeof obj === 'string'; },\n\n    isNumber: function(obj) { return typeof obj === 'number'; },\n\n    isArray: $.isArray,\n\n    isFunction: $.isFunction,\n\n    isObject: $.isPlainObject,\n\n    isUndefined: function(obj) { return typeof obj === 'undefined'; },\n\n    isElement: function(obj) { return !!(obj && obj.nodeType === 1); },\n\n    isJQuery: function(obj) { return obj instanceof $; },\n\n    toStr: function toStr(s) {\n      return (_.isUndefined(s) || s === null) ? '' : s + '';\n    },\n\n    bind: $.proxy,\n\n    each: function(collection, cb) {\n      // stupid argument order for jQuery.each\n      $.each(collection, reverseArgs);\n\n      function reverseArgs(index, value) { return cb(value, index); }\n    },\n\n    map: $.map,\n\n    filter: $.grep,\n\n    every: function(obj, test) {\n      var result = true;\n\n      if (!obj) { return result; }\n\n      $.each(obj, function(key, val) {\n        if (!(result = test.call(null, val, key, obj))) {\n          return false;\n        }\n      });\n\n      return !!result;\n    },\n\n    some: function(obj, test) {\n      var result = false;\n\n      if (!obj) { return result; }\n\n      $.each(obj, function(key, val) {\n        if (result = test.call(null, val, key, obj)) {\n          return false;\n        }\n      });\n\n      return !!result;\n    },\n\n    mixin: $.extend,\n\n    identity: function(x) { return x; },\n\n    clone: function(obj) { return $.extend(true, {}, obj); },\n\n    getIdGenerator: function() {\n      var counter = 0;\n      return function() { return counter++; };\n    },\n\n    templatify: function templatify(obj) {\n      return $.isFunction(obj) ? obj : template;\n\n      function template() { return String(obj); }\n    },\n\n    defer: function(fn) { setTimeout(fn, 0); },\n\n    debounce: function(func, wait, immediate) {\n      var timeout, result;\n\n      return function() {\n        var context = this, args = arguments, later, callNow;\n\n        later = function() {\n          timeout = null;\n          if (!immediate) { result = func.apply(context, args); }\n        };\n\n        callNow = immediate && !timeout;\n\n        clearTimeout(timeout);\n        timeout = setTimeout(later, wait);\n\n        if (callNow) { result = func.apply(context, args); }\n\n        return result;\n      };\n    },\n\n    throttle: function(func, wait) {\n      var context, args, timeout, result, previous, later;\n\n      previous = 0;\n      later = function() {\n        previous = new Date();\n        timeout = null;\n        result = func.apply(context, args);\n      };\n\n      return function() {\n        var now = new Date(),\n            remaining = wait - (now - previous);\n\n        context = this;\n        args = arguments;\n\n        if (remaining <= 0) {\n          clearTimeout(timeout);\n          timeout = null;\n          previous = now;\n          result = func.apply(context, args);\n        }\n\n        else if (!timeout) {\n          timeout = setTimeout(later, remaining);\n        }\n\n        return result;\n      };\n    },\n\n    stringify: function(val) {\n      return _.isString(val) ? val : JSON.stringify(val);\n    },\n\n    noop: function() {}\n  };\n})();\n"
  },
  {
    "path": "src/typeahead/dataset.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Dataset = (function() {\n  'use strict';\n\n  var keys, nameGenerator;\n\n  keys = {\n    val: 'tt-selectable-display',\n    obj: 'tt-selectable-object'\n  };\n\n  nameGenerator = _.getIdGenerator();\n\n  // constructor\n  // -----------\n\n  function Dataset(o, www) {\n    o = o || {};\n    o.templates = o.templates || {};\n\n    // DEPRECATED: empty will be dropped in v1\n    o.templates.notFound = o.templates.notFound || o.templates.empty;\n\n    if (!o.source) {\n      $.error('missing source');\n    }\n\n    if (!o.node) {\n      $.error('missing node');\n    }\n\n    if (o.name && !isValidName(o.name)) {\n      $.error('invalid dataset name: ' + o.name);\n    }\n\n    www.mixin(this);\n\n    this.highlight = !!o.highlight;\n    this.name = o.name || nameGenerator();\n\n    this.limit = o.limit || 5;\n    this.displayFn = getDisplayFn(o.display || o.displayKey);\n    this.templates = getTemplates(o.templates, this.displayFn);\n\n    // use duck typing to see if source is a bloodhound instance by checking\n    // for the __ttAdapter property; otherwise assume it is a function\n    this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;\n\n    // if the async option is undefined, inspect the source signature as\n    // a hint to figuring out of the source will return async suggestions\n    this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;\n\n    this._resetLastSuggestion();\n\n    this.$el = $(o.node)\n    .addClass(this.classes.dataset)\n    .addClass(this.classes.dataset + '-' + this.name);\n  }\n\n  // static methods\n  // --------------\n\n  Dataset.extractData = function extractData(el) {\n    var $el = $(el);\n\n    if ($el.data(keys.obj)) {\n      return {\n        val: $el.data(keys.val) || '',\n        obj: $el.data(keys.obj) || null\n      };\n    }\n\n    return null;\n  };\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Dataset.prototype, EventEmitter, {\n\n    // ### private\n\n    _overwrite: function overwrite(query, suggestions) {\n      suggestions = suggestions || [];\n\n      // got suggestions: overwrite dom with suggestions\n      if (suggestions.length) {\n        this._renderSuggestions(query, suggestions);\n      }\n\n      // no suggestions, expecting async: overwrite dom with pending\n      else if (this.async && this.templates.pending) {\n        this._renderPending(query);\n      }\n\n      // no suggestions, not expecting async: overwrite dom with not found\n      else if (!this.async && this.templates.notFound) {\n        this._renderNotFound(query);\n      }\n\n      // nothing to render: empty dom\n      else {\n        this._empty();\n      }\n\n      this.trigger('rendered', this.name, suggestions, false);\n    },\n\n    _append: function append(query, suggestions) {\n      suggestions = suggestions || [];\n\n      // got suggestions, sync suggestions exist: append suggestions to dom\n      if (suggestions.length && this.$lastSuggestion.length) {\n        this._appendSuggestions(query, suggestions);\n      }\n\n      // got suggestions, no sync suggestions: overwrite dom with suggestions\n      else if (suggestions.length) {\n        this._renderSuggestions(query, suggestions);\n      }\n\n      // no async/sync suggestions: overwrite dom with not found\n      else if (!this.$lastSuggestion.length && this.templates.notFound) {\n        this._renderNotFound(query);\n      }\n\n      this.trigger('rendered', this.name, suggestions, true);\n    },\n\n    _renderSuggestions: function renderSuggestions(query, suggestions) {\n      var $fragment;\n\n      $fragment = this._getSuggestionsFragment(query, suggestions);\n      this.$lastSuggestion = $fragment.children().last();\n\n      this.$el.html($fragment)\n      .prepend(this._getHeader(query, suggestions))\n      .append(this._getFooter(query, suggestions));\n    },\n\n    _appendSuggestions: function appendSuggestions(query, suggestions) {\n      var $fragment, $lastSuggestion;\n\n      $fragment = this._getSuggestionsFragment(query, suggestions);\n      $lastSuggestion = $fragment.children().last();\n\n      this.$lastSuggestion.after($fragment);\n\n      this.$lastSuggestion = $lastSuggestion;\n    },\n\n    _renderPending: function renderPending(query) {\n      var template = this.templates.pending;\n\n      this._resetLastSuggestion();\n      template && this.$el.html(template({\n        query: query,\n        dataset: this.name,\n      }));\n    },\n\n    _renderNotFound: function renderNotFound(query) {\n      var template = this.templates.notFound;\n\n      this._resetLastSuggestion();\n      template && this.$el.html(template({\n        query: query,\n        dataset: this.name,\n      }));\n    },\n\n    _empty: function empty() {\n      this.$el.empty();\n      this._resetLastSuggestion();\n    },\n\n    _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {\n      var that = this, fragment;\n\n      fragment = document.createDocumentFragment();\n      _.each(suggestions, function getSuggestionNode(suggestion) {\n        var $el, context;\n\n        context = that._injectQuery(query, suggestion);\n\n        $el = $(that.templates.suggestion(context))\n        .data(keys.obj, suggestion)\n        .data(keys.val, that.displayFn(suggestion))\n        .addClass(that.classes.suggestion + ' ' + that.classes.selectable);\n\n        fragment.appendChild($el[0]);\n      });\n\n      this.highlight && highlight({\n        className: this.classes.highlight,\n        node: fragment,\n        pattern: query\n      });\n\n      return $(fragment);\n    },\n\n    _getFooter: function getFooter(query, suggestions) {\n      return this.templates.footer ?\n        this.templates.footer({\n          query: query,\n          suggestions: suggestions,\n          dataset: this.name\n        }) : null;\n    },\n\n    _getHeader: function getHeader(query, suggestions) {\n      return this.templates.header ?\n        this.templates.header({\n          query: query,\n          suggestions: suggestions,\n          dataset: this.name\n        }) : null;\n    },\n\n    _resetLastSuggestion: function resetLastSuggestion() {\n      this.$lastSuggestion = $();\n    },\n\n    _injectQuery: function injectQuery(query, obj) {\n      return _.isObject(obj) ? _.mixin({ _query: query }, obj) : obj;\n    },\n\n    // ### public\n\n    update: function update(query) {\n      var that = this, canceled = false, syncCalled = false, rendered = 0;\n\n      // cancel possible pending update\n      this.cancel();\n\n      this.cancel = function cancel() {\n        canceled = true;\n        that.cancel = $.noop;\n        that.async && that.trigger('asyncCanceled', query);\n      };\n\n      this.source(query, sync, async);\n      !syncCalled && sync([]);\n\n      function sync(suggestions) {\n        if (syncCalled) { return; }\n\n        syncCalled = true;\n        suggestions = (suggestions || []).slice(0, that.limit);\n        rendered = suggestions.length;\n\n        that._overwrite(query, suggestions);\n\n        if (rendered < that.limit && that.async) {\n          that.trigger('asyncRequested', query);\n        }\n      }\n\n      function async(suggestions) {\n        suggestions = suggestions || [];\n\n        // if the update has been canceled or if the query has changed\n        // do not render the suggestions as they've become outdated\n        if (!canceled && rendered < that.limit) {\n          that.cancel = $.noop;\n          rendered += suggestions.length;\n          that._append(query, suggestions.slice(0, that.limit - rendered));\n\n          that.async && that.trigger('asyncReceived', query);\n        }\n      }\n    },\n\n    // cancel function gets set in #update\n    cancel: $.noop,\n\n    clear: function clear() {\n      this._empty();\n      this.cancel();\n      this.trigger('cleared');\n    },\n\n    isEmpty: function isEmpty() {\n      return this.$el.is(':empty');\n    },\n\n    destroy: function destroy() {\n      // #970\n      this.$el = $('<div>');\n    }\n  });\n\n  return Dataset;\n\n  // helper functions\n  // ----------------\n\n  function getDisplayFn(display) {\n    display = display || _.stringify;\n\n    return _.isFunction(display) ? display : displayFn;\n\n    function displayFn(obj) { return obj[display]; }\n  }\n\n  function getTemplates(templates, displayFn) {\n    return {\n      notFound: templates.notFound && _.templatify(templates.notFound),\n      pending: templates.pending && _.templatify(templates.pending),\n      header: templates.header && _.templatify(templates.header),\n      footer: templates.footer && _.templatify(templates.footer),\n      suggestion: templates.suggestion || suggestionTemplate\n    };\n\n    function suggestionTemplate(context) {\n      return $('<div>').text(displayFn(context));\n    }\n  }\n\n  function isValidName(str) {\n    // dashes, underscores, letters, and numbers\n    return (/^[_a-zA-Z0-9-]+$/).test(str);\n  }\n})();\n"
  },
  {
    "path": "src/typeahead/default_menu.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar DefaultMenu = (function() {\n  'use strict';\n\n  var s = Menu.prototype;\n\n  function DefaultMenu() {\n    Menu.apply(this, [].slice.call(arguments, 0));\n  }\n\n  _.mixin(DefaultMenu.prototype, Menu.prototype, {\n    // overrides\n    // ---------\n\n    open: function open() {\n      // only display the menu when there's something to be shown\n      !this._allDatasetsEmpty() && this._show();\n      return s.open.apply(this, [].slice.call(arguments, 0));\n    },\n\n    close: function close() {\n      this._hide();\n      return s.close.apply(this, [].slice.call(arguments, 0));\n    },\n\n    _onRendered: function onRendered() {\n      if (this._allDatasetsEmpty()) {\n        this._hide();\n      }\n\n      else {\n        this.isOpen() && this._show();\n      }\n\n      return s._onRendered.apply(this, [].slice.call(arguments, 0));\n    },\n\n    _onCleared: function onCleared() {\n      if (this._allDatasetsEmpty()) {\n        this._hide();\n      }\n\n      else {\n        this.isOpen() && this._show();\n      }\n\n      return s._onCleared.apply(this, [].slice.call(arguments, 0));\n    },\n\n    setLanguageDirection: function setLanguageDirection(dir) {\n      this.$node.css(dir === 'ltr' ? this.css.ltr : this.css.rtl);\n      return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));\n    },\n\n    // private\n    // ---------\n\n    _hide: function hide() {\n      this.$node.hide();\n    },\n\n    _show: function show() {\n      // can't use jQuery#show because $node is a span element we want\n      // display: block; not dislay: inline;\n      this.$node.css('display', 'block');\n    }\n  });\n\n  return DefaultMenu;\n})();\n"
  },
  {
    "path": "src/typeahead/event_bus.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar EventBus = (function() {\n  'use strict';\n\n  var namespace, deprecationMap;\n\n  namespace = 'typeahead:';\n\n  // DEPRECATED: will be remove in v1\n  //\n  // NOTE: there is no deprecation plan for the opened and closed event\n  // as their behavior has changed enough that it wouldn't make sense\n  deprecationMap = {\n    render: 'rendered',\n    cursorchange: 'cursorchanged',\n    select: 'selected',\n    autocomplete: 'autocompleted'\n  };\n\n  // constructor\n  // -----------\n\n  function EventBus(o) {\n    if (!o || !o.el) {\n      $.error('EventBus initialized without el');\n    }\n\n    this.$el = $(o.el);\n  }\n\n  // instance methods\n  // ----------------\n\n  _.mixin(EventBus.prototype, {\n\n    // ### private\n\n    _trigger: function(type, args) {\n      var $e;\n\n      $e = $.Event(namespace + type);\n      (args = args || []).unshift($e);\n\n      this.$el.trigger.apply(this.$el, args);\n\n      return $e;\n    },\n\n    // ### public\n\n    before: function(type) {\n      var args, $e;\n\n      args = [].slice.call(arguments, 1);\n      $e = this._trigger('before' + type, args);\n\n      return $e.isDefaultPrevented();\n    },\n\n    trigger: function(type) {\n      var deprecatedType;\n\n      this._trigger(type, [].slice.call(arguments, 1));\n\n      // TODO: remove in v1\n      if (deprecatedType = deprecationMap[type]) {\n        this._trigger(deprecatedType, [].slice.call(arguments, 1));\n      }\n    }\n  });\n\n  return EventBus;\n})();\n"
  },
  {
    "path": "src/typeahead/event_emitter.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\n// inspired by https://github.com/jharding/boomerang\n\nvar EventEmitter = (function() {\n  'use strict';\n\n  var splitter = /\\s+/, nextTick = getNextTick();\n\n  return {\n    onSync: onSync,\n    onAsync: onAsync,\n    off: off,\n    trigger: trigger\n  };\n\n  function on(method, types, cb, context) {\n    var type;\n\n    if (!cb) { return this; }\n\n    types = types.split(splitter);\n    cb = context ? bindContext(cb, context) : cb;\n\n    this._callbacks = this._callbacks || {};\n\n    while (type = types.shift()) {\n      this._callbacks[type] = this._callbacks[type] || { sync: [], async: [] };\n      this._callbacks[type][method].push(cb);\n    }\n\n    return this;\n  }\n\n  function onAsync(types, cb, context) {\n    return on.call(this, 'async', types, cb, context);\n  }\n\n  function onSync(types, cb, context) {\n    return on.call(this, 'sync', types, cb, context);\n  }\n\n  function off(types) {\n    var type;\n\n    if (!this._callbacks) { return this; }\n\n    types = types.split(splitter);\n\n    while (type = types.shift()) {\n      delete this._callbacks[type];\n    }\n\n    return this;\n  }\n\n  function trigger(types) {\n    var type, callbacks, args, syncFlush, asyncFlush;\n\n    if (!this._callbacks) { return this; }\n\n    types = types.split(splitter);\n    args = [].slice.call(arguments, 1);\n\n    while ((type = types.shift()) && (callbacks = this._callbacks[type])) {\n      syncFlush = getFlush(callbacks.sync, this, [type].concat(args));\n      asyncFlush = getFlush(callbacks.async, this, [type].concat(args));\n\n      syncFlush() && nextTick(asyncFlush);\n    }\n\n    return this;\n  }\n\n  function getFlush(callbacks, context, args) {\n    return flush;\n\n    function flush() {\n      var cancelled;\n\n      for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {\n        // only cancel if the callback explicitly returns false\n        cancelled = callbacks[i].apply(context, args) === false;\n      }\n\n      return !cancelled;\n    }\n  }\n\n  function getNextTick() {\n    var nextTickFn;\n\n    // IE10+\n    if (window.setImmediate) {\n      nextTickFn = function nextTickSetImmediate(fn) {\n        setImmediate(function() { fn(); });\n      };\n    }\n\n    // old browsers\n    else {\n      nextTickFn = function nextTickSetTimeout(fn) {\n        setTimeout(function() { fn(); }, 0);\n      };\n    }\n\n    return nextTickFn;\n  }\n\n  function bindContext(fn, context) {\n    return fn.bind ?\n      fn.bind(context) :\n      function() { fn.apply(context, [].slice.call(arguments, 0)); };\n  }\n})();\n"
  },
  {
    "path": "src/typeahead/highlight.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\n// inspired by https://github.com/jharding/bearhug\n\nvar highlight = (function(doc) {\n  'use strict';\n\n  var defaults = {\n        node: null,\n        pattern: null,\n        tagName: 'strong',\n        className: null,\n        wordsOnly: false,\n        caseSensitive: false\n      };\n\n  return function hightlight(o) {\n    var regex;\n\n    o = _.mixin({}, defaults, o);\n\n    if (!o.node || !o.pattern) {\n      // fail silently\n      return;\n    }\n\n    // support wrapping multiple patterns\n    o.pattern = _.isArray(o.pattern) ? o.pattern : [o.pattern];\n\n    regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);\n    traverse(o.node, hightlightTextNode);\n\n    function hightlightTextNode(textNode) {\n      var match, patternNode, wrapperNode;\n\n      if (match = regex.exec(textNode.data)) {\n        wrapperNode = doc.createElement(o.tagName);\n        o.className && (wrapperNode.className = o.className);\n\n        patternNode = textNode.splitText(match.index);\n        patternNode.splitText(match[0].length);\n        wrapperNode.appendChild(patternNode.cloneNode(true));\n\n        textNode.parentNode.replaceChild(wrapperNode, patternNode);\n      }\n\n      return !!match;\n    }\n\n    function traverse(el, hightlightTextNode) {\n      var childNode, TEXT_NODE_TYPE = 3;\n\n      for (var i = 0; i < el.childNodes.length; i++) {\n        childNode = el.childNodes[i];\n\n        if (childNode.nodeType === TEXT_NODE_TYPE) {\n          i += hightlightTextNode(childNode) ? 1 : 0;\n        }\n\n        else {\n          traverse(childNode, hightlightTextNode);\n        }\n      }\n    }\n  };\n\n  function getRegex(patterns, caseSensitive, wordsOnly) {\n    var escapedPatterns = [], regexStr;\n\n    for (var i = 0, len = patterns.length; i < len; i++) {\n      escapedPatterns.push(_.escapeRegExChars(patterns[i]));\n    }\n\n    regexStr = wordsOnly ?\n      '\\\\b(' + escapedPatterns.join('|') + ')\\\\b' :\n      '(' + escapedPatterns.join('|') + ')';\n\n    return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, 'i');\n  }\n})(window.document);\n"
  },
  {
    "path": "src/typeahead/input.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Input = (function() {\n  'use strict';\n\n  var specialKeyCodeMap;\n\n  specialKeyCodeMap = {\n    9: 'tab',\n    27: 'esc',\n    37: 'left',\n    39: 'right',\n    13: 'enter',\n    38: 'up',\n    40: 'down'\n  };\n\n  // constructor\n  // -----------\n\n  function Input(o, www) {\n    o = o || {};\n\n    if (!o.input) {\n      $.error('input is missing');\n    }\n\n    www.mixin(this);\n\n    this.$hint = $(o.hint);\n    this.$input = $(o.input);\n\n    // the query defaults to whatever the value of the input is\n    // on initialization, it'll most likely be an empty string\n    this.query = this.$input.val();\n\n    // for tracking when a change event should be triggered\n    this.queryWhenFocused = this.hasFocus() ? this.query : null;\n\n    // helps with calculating the width of the input's value\n    this.$overflowHelper = buildOverflowHelper(this.$input);\n\n    // detect the initial lang direction\n    this._checkLanguageDirection();\n\n    // if no hint, noop all the hint related functions\n    if (this.$hint.length === 0) {\n      this.setHint =\n      this.getHint =\n      this.clearHint =\n      this.clearHintIfInvalid = _.noop;\n    }\n  }\n\n  // static methods\n  // --------------\n\n  Input.normalizeQuery = function(str) {\n    // strips leading whitespace and condenses all whitespace\n    return (_.toStr(str)).replace(/^\\s*/g, '').replace(/\\s{2,}/g, ' ');\n  };\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Input.prototype, EventEmitter, {\n\n    // ### event handlers\n\n    _onBlur: function onBlur() {\n      this.resetInputValue();\n      this.trigger('blurred');\n    },\n\n    _onFocus: function onFocus() {\n      this.queryWhenFocused = this.query;\n      this.trigger('focused');\n    },\n\n    _onKeydown: function onKeydown($e) {\n      // which is normalized and consistent (but not for ie)\n      var keyName = specialKeyCodeMap[$e.which || $e.keyCode];\n\n      this._managePreventDefault(keyName, $e);\n      if (keyName && this._shouldTrigger(keyName, $e)) {\n        this.trigger(keyName + 'Keyed', $e);\n      }\n    },\n\n    _onInput: function onInput() {\n      this._setQuery(this.getInputValue());\n      this.clearHintIfInvalid();\n      this._checkLanguageDirection();\n    },\n\n    // ### private\n\n    _managePreventDefault: function managePreventDefault(keyName, $e) {\n      var preventDefault;\n\n      switch (keyName) {\n        case 'up':\n        case 'down':\n          preventDefault = !withModifier($e);\n          break;\n\n        default:\n          preventDefault = false;\n      }\n\n      preventDefault && $e.preventDefault();\n    },\n\n    _shouldTrigger: function shouldTrigger(keyName, $e) {\n      var trigger;\n\n      switch (keyName) {\n        case 'tab':\n          trigger = !withModifier($e);\n          break;\n\n        default:\n          trigger = true;\n      }\n\n      return trigger;\n    },\n\n    _checkLanguageDirection: function checkLanguageDirection() {\n      var dir = (this.$input.css('direction') || 'ltr').toLowerCase();\n\n      if (this.dir !== dir) {\n        this.dir = dir;\n        this.$hint.attr('dir', dir);\n        this.trigger('langDirChanged', dir);\n      }\n    },\n\n    _setQuery: function setQuery(val, silent) {\n      var areEquivalent, hasDifferentWhitespace;\n\n      areEquivalent = areQueriesEquivalent(val, this.query);\n      hasDifferentWhitespace = areEquivalent ?\n        this.query.length !== val.length : false;\n\n      this.query = val;\n\n      if (!silent && !areEquivalent) {\n        this.trigger('queryChanged', this.query);\n      }\n\n      else if (!silent && hasDifferentWhitespace) {\n        this.trigger('whitespaceChanged', this.query);\n      }\n    },\n\n    // ### public\n\n    bind: function() {\n      var that = this, onBlur, onFocus, onKeydown, onInput;\n\n      // bound functions\n      onBlur = _.bind(this._onBlur, this);\n      onFocus = _.bind(this._onFocus, this);\n      onKeydown = _.bind(this._onKeydown, this);\n      onInput = _.bind(this._onInput, this);\n\n      this.$input\n      .on('blur.tt', onBlur)\n      .on('focus.tt', onFocus)\n      .on('keydown.tt', onKeydown);\n\n      // ie8 don't support the input event\n      // ie9 doesn't fire the input event when characters are removed\n      if (!_.isMsie() || _.isMsie() > 9) {\n        this.$input.on('input.tt', onInput);\n      }\n\n      else {\n        this.$input.on('keydown.tt keypress.tt cut.tt paste.tt', function($e) {\n          // if a special key triggered this, ignore it\n          if (specialKeyCodeMap[$e.which || $e.keyCode]) { return; }\n\n          // give the browser a chance to update the value of the input\n          // before checking to see if the query changed\n          _.defer(_.bind(that._onInput, that, $e));\n        });\n      }\n\n      return this;\n    },\n\n    focus: function focus() {\n      this.$input.focus();\n    },\n\n    blur: function blur() {\n      this.$input.blur();\n    },\n\n    getLangDir: function getLangDir() {\n      return this.dir;\n    },\n\n    getQuery: function getQuery() {\n      return this.query || '';\n    },\n\n    setQuery: function setQuery(val, silent) {\n      this.setInputValue(val);\n      this._setQuery(val, silent);\n    },\n\n    hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {\n      return this.query !== this.queryWhenFocused;\n    },\n\n    getInputValue: function getInputValue() {\n      return this.$input.val();\n    },\n\n    setInputValue: function setInputValue(value) {\n      this.$input.val(value);\n      this.clearHintIfInvalid();\n      this._checkLanguageDirection();\n    },\n\n    resetInputValue: function resetInputValue() {\n      this.setInputValue(this.query);\n    },\n\n    getHint: function getHint() {\n      return this.$hint.val();\n    },\n\n    setHint: function setHint(value) {\n      this.$hint.val(value);\n    },\n\n    clearHint: function clearHint() {\n      this.setHint('');\n    },\n\n    clearHintIfInvalid: function clearHintIfInvalid() {\n      var val, hint, valIsPrefixOfHint, isValid;\n\n      val = this.getInputValue();\n      hint = this.getHint();\n      valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;\n      isValid = val !== '' && valIsPrefixOfHint && !this.hasOverflow();\n\n      !isValid && this.clearHint();\n    },\n\n    hasFocus: function hasFocus() {\n      return this.$input.is(':focus');\n    },\n\n    hasOverflow: function hasOverflow() {\n      // 2 is arbitrary, just picking a small number to handle edge cases\n      var constraint = this.$input.width() - 2;\n\n      this.$overflowHelper.text(this.getInputValue());\n\n      return this.$overflowHelper.width() >= constraint;\n    },\n\n    isCursorAtEnd: function() {\n      var valueLength, selectionStart, range;\n\n      valueLength = this.$input.val().length;\n      selectionStart = this.$input[0].selectionStart;\n\n      if (_.isNumber(selectionStart)) {\n       return selectionStart === valueLength;\n      }\n\n      else if (document.selection) {\n        // NOTE: this won't work unless the input has focus, the good news\n        // is this code should only get called when the input has focus\n        range = document.selection.createRange();\n        range.moveStart('character', -valueLength);\n\n        return valueLength === range.text.length;\n      }\n\n      return true;\n    },\n\n    destroy: function destroy() {\n      this.$hint.off('.tt');\n      this.$input.off('.tt');\n      this.$overflowHelper.remove();\n\n      // #970\n      this.$hint = this.$input = this.$overflowHelper = $('<div>');\n    }\n  });\n\n  return Input;\n\n  // helper functions\n  // ----------------\n\n  function buildOverflowHelper($input) {\n    return $('<pre aria-hidden=\"true\"></pre>')\n    .css({\n      // position helper off-screen\n      position: 'absolute',\n      visibility: 'hidden',\n      // avoid line breaks and whitespace collapsing\n      whiteSpace: 'pre',\n      // use same font css as input to calculate accurate width\n      fontFamily: $input.css('font-family'),\n      fontSize: $input.css('font-size'),\n      fontStyle: $input.css('font-style'),\n      fontVariant: $input.css('font-variant'),\n      fontWeight: $input.css('font-weight'),\n      wordSpacing: $input.css('word-spacing'),\n      letterSpacing: $input.css('letter-spacing'),\n      textIndent: $input.css('text-indent'),\n      textRendering: $input.css('text-rendering'),\n      textTransform: $input.css('text-transform')\n    })\n    .insertAfter($input);\n  }\n\n  function areQueriesEquivalent(a, b) {\n    return Input.normalizeQuery(a) === Input.normalizeQuery(b);\n  }\n\n  function withModifier($e) {\n    return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;\n  }\n})();\n"
  },
  {
    "path": "src/typeahead/menu.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Menu = (function() {\n  'use strict';\n\n  // constructor\n  // -----------\n\n  function Menu(o, www) {\n    var that = this;\n\n    o = o || {};\n\n    if (!o.node) {\n      $.error('node is required');\n    }\n\n    www.mixin(this);\n\n    this.$node = $(o.node);\n\n    // the latest query #update was called with\n    this.query = null;\n    this.datasets = _.map(o.datasets, initializeDataset);\n\n    function initializeDataset(oDataset) {\n      var node = that.$node.find(oDataset.node).first();\n      oDataset.node = node.length ? node : $('<div>').appendTo(that.$node);\n\n      return new Dataset(oDataset, www);\n    }\n  }\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Menu.prototype, EventEmitter, {\n\n    // ### event handlers\n\n    _onSelectableClick: function onSelectableClick($e) {\n      this.trigger('selectableClicked', $($e.currentTarget));\n    },\n\n    _onRendered: function onRendered(type, dataset, suggestions, async) {\n      this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());\n      this.trigger('datasetRendered', dataset, suggestions, async);\n    },\n\n    _onCleared: function onCleared() {\n      this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());\n      this.trigger('datasetCleared');\n    },\n\n    _propagate: function propagate() {\n      this.trigger.apply(this, arguments);\n    },\n\n    // ### private\n\n    _allDatasetsEmpty: function allDatasetsEmpty() {\n      return _.every(this.datasets, isDatasetEmpty);\n\n      function isDatasetEmpty(dataset) { return dataset.isEmpty(); }\n    },\n\n    _getSelectables: function getSelectables() {\n      return this.$node.find(this.selectors.selectable);\n    },\n\n    _removeCursor: function _removeCursor() {\n      var $selectable = this.getActiveSelectable();\n      $selectable && $selectable.removeClass(this.classes.cursor);\n    },\n\n    _ensureVisible: function ensureVisible($el) {\n      var elTop, elBottom, nodeScrollTop, nodeHeight;\n\n      elTop = $el.position().top;\n      elBottom = elTop + $el.outerHeight(true);\n      nodeScrollTop = this.$node.scrollTop();\n      nodeHeight = this.$node.height() +\n        parseInt(this.$node.css('paddingTop'), 10) +\n        parseInt(this.$node.css('paddingBottom'), 10);\n\n      if (elTop < 0) {\n        this.$node.scrollTop(nodeScrollTop + elTop);\n      }\n\n      else if (nodeHeight < elBottom) {\n        this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));\n      }\n    },\n\n    // ### public\n\n    bind: function() {\n    var that = this, onSelectableClick;\n\n      onSelectableClick = _.bind(this._onSelectableClick, this);\n      this.$node.on('click.tt', this.selectors.selectable, onSelectableClick);\n\n      _.each(this.datasets, function(dataset) {\n        dataset\n        .onSync('asyncRequested', that._propagate, that)\n        .onSync('asyncCanceled', that._propagate, that)\n        .onSync('asyncReceived', that._propagate, that)\n        .onSync('rendered', that._onRendered, that)\n        .onSync('cleared', that._onCleared, that);\n      });\n\n      return this;\n    },\n\n    isOpen: function isOpen() {\n      return this.$node.hasClass(this.classes.open);\n    },\n\n    open: function open() {\n      this.$node.addClass(this.classes.open);\n    },\n\n    close: function close() {\n      this.$node.removeClass(this.classes.open);\n      this._removeCursor();\n    },\n\n    setLanguageDirection: function setLanguageDirection(dir) {\n      this.$node.attr('dir', dir);\n    },\n\n    selectableRelativeToCursor: function selectableRelativeToCursor(delta) {\n      var $selectables, $oldCursor, oldIndex, newIndex;\n\n      $oldCursor = this.getActiveSelectable();\n      $selectables = this._getSelectables();\n\n      // shifting before and after modulo to deal with -1 index\n      oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;\n      newIndex = oldIndex + delta;\n      newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;\n\n      // wrap new index if less than -1\n      newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;\n\n      return newIndex === -1 ? null : $selectables.eq(newIndex);\n    },\n\n    setCursor: function setCursor($selectable) {\n      this._removeCursor();\n\n      if ($selectable = $selectable && $selectable.first()) {\n        $selectable.addClass(this.classes.cursor);\n\n        // in the case of scrollable overflow\n        // make sure the cursor is visible in the node\n        this._ensureVisible($selectable);\n      }\n    },\n\n    getSelectableData: function getSelectableData($el) {\n      return ($el && $el.length) ? Dataset.extractData($el) : null;\n    },\n\n    getActiveSelectable: function getActiveSelectable() {\n      var $selectable = this._getSelectables().filter(this.selectors.cursor).first();\n\n      return $selectable.length ? $selectable : null;\n    },\n\n    getTopSelectable: function getTopSelectable() {\n      var $selectable = this._getSelectables().first();\n\n      return $selectable.length ? $selectable : null;\n    },\n\n    update: function update(query) {\n      var isValidUpdate = query !== this.query;\n\n      // don't update if the query hasn't changed\n      if (isValidUpdate) {\n        this.query = query;\n        _.each(this.datasets, updateDataset);\n      }\n\n      return isValidUpdate;\n\n      function updateDataset(dataset) { dataset.update(query); }\n    },\n\n    empty: function empty() {\n      _.each(this.datasets, clearDataset);\n\n      this.query = null;\n      this.$node.addClass(this.classes.empty);\n\n      function clearDataset(dataset) { dataset.clear(); }\n    },\n\n    destroy: function destroy() {\n      this.$node.off('.tt');\n\n      // #970\n      this.$node = $('<div>');\n\n      _.each(this.datasets, destroyDataset);\n\n      function destroyDataset(dataset) { dataset.destroy(); }\n    }\n  });\n\n  return Menu;\n})();\n"
  },
  {
    "path": "src/typeahead/plugin.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\n(function() {\n  'use strict';\n\n  var old, keys, methods;\n\n  old = $.fn.typeahead;\n\n  keys = {\n    www: 'tt-www',\n    attrs: 'tt-attrs',\n    typeahead: 'tt-typeahead'\n  };\n\n  methods = {\n    // supported signatures:\n    // function(o, dataset, dataset, ...)\n    // function(o, [dataset, dataset, ...])\n    initialize: function initialize(o, datasets) {\n      var www;\n\n      datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);\n\n      o = o || {};\n      www = WWW(o.classNames);\n\n      return this.each(attach);\n\n      function attach() {\n        var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu,\n            eventBus, input, menu, typeahead, MenuConstructor;\n\n        // highlight is a top-level config that needs to get inherited\n        // from all of the datasets\n        _.each(datasets, function(d) { d.highlight = !!o.highlight; });\n\n        $input = $(this);\n        $wrapper = $(www.html.wrapper);\n        $hint = $elOrNull(o.hint);\n        $menu = $elOrNull(o.menu);\n\n        defaultHint = o.hint !== false && !$hint;\n        defaultMenu = o.menu !== false && !$menu;\n\n        defaultHint && ($hint = buildHintFromInput($input, www));\n        defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));\n\n        // hint should be empty on init\n        $hint && $hint.val('');\n        $input = prepInput($input, www);\n\n        // only apply inline styles and make dom changes if necessary\n        if (defaultHint || defaultMenu) {\n          $wrapper.css(www.css.wrapper);\n          $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);\n\n          $input\n          .wrap($wrapper)\n          .parent()\n          .prepend(defaultHint ? $hint : null)\n          .append(defaultMenu ? $menu : null);\n        }\n\n        MenuConstructor = defaultMenu ? DefaultMenu : Menu;\n\n        eventBus = new EventBus({ el: $input });\n        input = new Input({ hint: $hint, input: $input, }, www);\n        menu = new MenuConstructor({\n          node: $menu,\n          datasets: datasets\n        }, www);\n\n        typeahead = new Typeahead({\n          input: input,\n          menu: menu,\n          eventBus: eventBus,\n          minLength: o.minLength\n        }, www);\n\n        $input.data(keys.www, www);\n        $input.data(keys.typeahead, typeahead);\n      }\n    },\n\n    isEnabled: function isEnabled() {\n      var enabled;\n\n      ttEach(this.first(), function(t) { enabled = t.isEnabled(); });\n      return enabled;\n    },\n\n    enable: function enable() {\n      ttEach(this, function(t) { t.enable(); });\n      return this;\n    },\n\n    disable: function disable() {\n      ttEach(this, function(t) { t.disable(); });\n      return this;\n    },\n\n    isActive: function isActive() {\n      var active;\n\n      ttEach(this.first(), function(t) { active = t.isActive(); });\n      return active;\n    },\n\n    activate: function activate() {\n      ttEach(this, function(t) { t.activate(); });\n      return this;\n    },\n\n    deactivate: function deactivate() {\n      ttEach(this, function(t) { t.deactivate(); });\n      return this;\n    },\n\n    isOpen: function isOpen() {\n      var open;\n\n      ttEach(this.first(), function(t) { open = t.isOpen(); });\n      return open;\n    },\n\n    open: function open() {\n      ttEach(this, function(t) { t.open(); });\n      return this;\n    },\n\n    close: function close() {\n      ttEach(this, function(t) { t.close(); });\n      return this;\n    },\n\n    select: function select(el) {\n      var success = false, $el = $(el);\n\n      ttEach(this.first(), function(t) { success = t.select($el); });\n      return success;\n    },\n\n    autocomplete: function autocomplete(el) {\n      var success = false, $el = $(el);\n\n      ttEach(this.first(), function(t) { success = t.autocomplete($el); });\n      return success;\n    },\n\n    moveCursor: function moveCursoe(delta) {\n      var success = false;\n\n      ttEach(this.first(), function(t) { success = t.moveCursor(delta); });\n      return success;\n    },\n\n    // mirror jQuery#val functionality: reads opearte on first match,\n    // write operates on all matches\n    val: function val(newVal) {\n      var query;\n\n      if (!arguments.length) {\n        ttEach(this.first(), function(t) { query = t.getVal(); });\n        return query;\n      }\n\n      else {\n        ttEach(this, function(t) { t.setVal(newVal); });\n        return this;\n      }\n    },\n\n    destroy: function destroy() {\n      ttEach(this, function(typeahead, $input) {\n        revert($input);\n        typeahead.destroy();\n      });\n\n      return this;\n    }\n  };\n\n  $.fn.typeahead = function(method) {\n    // methods that should only act on intialized typeaheads\n    if (methods[method]) {\n      return methods[method].apply(this, [].slice.call(arguments, 1));\n    }\n\n    else {\n      return methods.initialize.apply(this, arguments);\n    }\n  };\n\n  $.fn.typeahead.noConflict = function noConflict() {\n    $.fn.typeahead = old;\n    return this;\n  };\n\n  // helper methods\n  // --------------\n\n  function ttEach($els, fn) {\n    $els.each(function() {\n      var $input = $(this), typeahead;\n\n      (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);\n    });\n  }\n\n  function buildHintFromInput($input, www) {\n    return $input.clone()\n    .addClass(www.classes.hint)\n    .removeData()\n    .css(www.css.hint)\n    .css(getBackgroundStyles($input))\n    .prop('readonly', true)\n    .removeAttr('id name placeholder required')\n    .attr({ autocomplete: 'off', spellcheck: 'false', tabindex: -1 });\n  }\n\n  function prepInput($input, www) {\n    // store the original values of the attrs that get modified\n    // so modifications can be reverted on destroy\n    $input.data(keys.attrs, {\n      dir: $input.attr('dir'),\n      autocomplete: $input.attr('autocomplete'),\n      spellcheck: $input.attr('spellcheck'),\n      style: $input.attr('style')\n    });\n\n    $input\n    .addClass(www.classes.input)\n    .attr({ autocomplete: 'off', spellcheck: false });\n\n    // ie7 does not like it when dir is set to auto\n    try { !$input.attr('dir') && $input.attr('dir', 'auto'); } catch (e) {}\n\n    return $input;\n  }\n\n  function getBackgroundStyles($el) {\n    return {\n      backgroundAttachment: $el.css('background-attachment'),\n      backgroundClip: $el.css('background-clip'),\n      backgroundColor: $el.css('background-color'),\n      backgroundImage: $el.css('background-image'),\n      backgroundOrigin: $el.css('background-origin'),\n      backgroundPosition: $el.css('background-position'),\n      backgroundRepeat: $el.css('background-repeat'),\n      backgroundSize: $el.css('background-size')\n    };\n  }\n\n  function revert($input) {\n    var www, $wrapper;\n\n    www = $input.data(keys.www);\n    $wrapper = $input.parent().filter(www.selectors.wrapper);\n\n    // need to remove attrs that weren't previously defined and\n    // revert attrs that originally had a value\n    _.each($input.data(keys.attrs), function(val, key) {\n      _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);\n    });\n\n    $input\n    .removeData(keys.typeahead)\n    .removeData(keys.www)\n    .removeData(keys.attr)\n    .removeClass(www.classes.input);\n\n    if ($wrapper.length) {\n      $input.detach().insertAfter($wrapper);\n      $wrapper.remove();\n    }\n  }\n\n  function $elOrNull(obj) {\n    var isValid, $el;\n\n    isValid = _.isJQuery(obj) || _.isElement(obj);\n    $el = isValid ? $(obj).first() : [];\n\n    return $el.length ? $el : null;\n  }\n})();\n"
  },
  {
    "path": "src/typeahead/typeahead.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar Typeahead = (function() {\n  'use strict';\n\n  // constructor\n  // -----------\n\n  function Typeahead(o, www) {\n    var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed,\n        onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged,\n        onWhitespaceChanged;\n\n    o = o || {};\n\n    if (!o.input) {\n      $.error('missing input');\n    }\n\n    if (!o.menu) {\n      $.error('missing menu');\n    }\n\n    if (!o.eventBus) {\n      $.error('missing event bus');\n    }\n\n    www.mixin(this);\n\n    this.eventBus = o.eventBus;\n    this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;\n\n    this.input = o.input;\n    this.menu = o.menu;\n\n    this.enabled = true;\n\n    // activate the typeahead on init if the input has focus\n    this.active = false;\n    this.input.hasFocus() && this.activate();\n\n    // detect the initial lang direction\n    this.dir = this.input.getLangDir();\n\n    this._hacks();\n\n    this.menu.bind()\n    .onSync('selectableClicked', this._onSelectableClicked, this)\n    .onSync('asyncRequested', this._onAsyncRequested, this)\n    .onSync('asyncCanceled', this._onAsyncCanceled, this)\n    .onSync('asyncReceived', this._onAsyncReceived, this)\n    .onSync('datasetRendered', this._onDatasetRendered, this)\n    .onSync('datasetCleared', this._onDatasetCleared, this);\n\n    // composed event handlers for input\n    onFocused = c(this, 'activate', 'open', '_onFocused');\n    onBlurred = c(this, 'deactivate', '_onBlurred');\n    onEnterKeyed = c(this, 'isActive', 'isOpen', '_onEnterKeyed');\n    onTabKeyed = c(this, 'isActive', 'isOpen', '_onTabKeyed');\n    onEscKeyed = c(this, 'isActive', '_onEscKeyed');\n    onUpKeyed = c(this, 'isActive', 'open', '_onUpKeyed');\n    onDownKeyed = c(this, 'isActive', 'open', '_onDownKeyed');\n    onLeftKeyed = c(this, 'isActive', 'isOpen', '_onLeftKeyed');\n    onRightKeyed = c(this, 'isActive', 'isOpen', '_onRightKeyed');\n    onQueryChanged = c(this, '_openIfActive', '_onQueryChanged');\n    onWhitespaceChanged = c(this, '_openIfActive', '_onWhitespaceChanged');\n\n    this.input.bind()\n    .onSync('focused', onFocused, this)\n    .onSync('blurred', onBlurred, this)\n    .onSync('enterKeyed', onEnterKeyed, this)\n    .onSync('tabKeyed', onTabKeyed, this)\n    .onSync('escKeyed', onEscKeyed, this)\n    .onSync('upKeyed', onUpKeyed, this)\n    .onSync('downKeyed', onDownKeyed, this)\n    .onSync('leftKeyed', onLeftKeyed, this)\n    .onSync('rightKeyed', onRightKeyed, this)\n    .onSync('queryChanged', onQueryChanged, this)\n    .onSync('whitespaceChanged', onWhitespaceChanged, this)\n    .onSync('langDirChanged', this._onLangDirChanged, this);\n  }\n\n  // instance methods\n  // ----------------\n\n  _.mixin(Typeahead.prototype, {\n\n    // here's where hacks get applied and we don't feel bad about it\n    _hacks: function hacks() {\n      var $input, $menu;\n\n      // these default values are to make testing easier\n      $input = this.input.$input || $('<div>');\n      $menu = this.menu.$node || $('<div>');\n\n      // #705: if there's scrollable overflow, ie doesn't support\n      // blur cancellations when the scrollbar is clicked\n      //\n      // #351: preventDefault won't cancel blurs in ie <= 8\n      $input.on('blur.tt', function($e) {\n        var active, isActive, hasActive;\n\n        active = document.activeElement;\n        isActive = $menu.is(active);\n        hasActive = $menu.has(active).length > 0;\n\n        if (_.isMsie() && (isActive || hasActive)) {\n          $e.preventDefault();\n          // stop immediate in order to prevent Input#_onBlur from\n          // getting exectued\n          $e.stopImmediatePropagation();\n          _.defer(function() { $input.focus(); });\n        }\n      });\n\n      // #351: prevents input blur due to clicks within menu\n      $menu.on('mousedown.tt', function($e) { $e.preventDefault(); });\n    },\n\n    // ### event handlers\n\n    _onSelectableClicked: function onSelectableClicked(type, $el) {\n      this.select($el);\n    },\n\n    _onDatasetCleared: function onDatasetCleared() {\n      this._updateHint();\n    },\n\n    _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {\n      this._updateHint();\n      this.eventBus.trigger('render', suggestions, async, dataset);\n    },\n\n    _onAsyncRequested: function onAsyncRequested(type, dataset, query) {\n      this.eventBus.trigger('asyncrequest', query, dataset);\n    },\n\n    _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {\n      this.eventBus.trigger('asynccancel', query, dataset);\n    },\n\n    _onAsyncReceived: function onAsyncReceived(type, dataset, query) {\n      this.eventBus.trigger('asyncreceive', query, dataset);\n    },\n\n    _onFocused: function onFocused() {\n      this._minLengthMet() && this.menu.update(this.input.getQuery());\n    },\n\n    _onBlurred: function onBlurred() {\n      if (this.input.hasQueryChangedSinceLastFocus()) {\n        this.eventBus.trigger('change', this.input.getQuery());\n      }\n    },\n\n    _onEnterKeyed: function onEnterKeyed(type, $e) {\n      var $selectable;\n\n      if ($selectable = this.menu.getActiveSelectable()) {\n        this.select($selectable) && $e.preventDefault();\n      }\n    },\n\n    _onTabKeyed: function onTabKeyed(type, $e) {\n      var $selectable;\n\n      if ($selectable = this.menu.getActiveSelectable()) {\n        this.select($selectable) && $e.preventDefault();\n      }\n\n      else if ($selectable = this.menu.getTopSelectable()) {\n        this.autocomplete($selectable) && $e.preventDefault();\n      }\n    },\n\n    _onEscKeyed: function onEscKeyed() {\n      this.close();\n    },\n\n    _onUpKeyed: function onUpKeyed() {\n      this.moveCursor(-1);\n    },\n\n    _onDownKeyed: function onDownKeyed() {\n      this.moveCursor(+1);\n    },\n\n    _onLeftKeyed: function onLeftKeyed() {\n      if (this.dir === 'rtl' && this.input.isCursorAtEnd()) {\n        this.autocomplete(this.menu.getTopSelectable());\n      }\n    },\n\n    _onRightKeyed: function onRightKeyed() {\n      if (this.dir === 'ltr' && this.input.isCursorAtEnd()) {\n        this.autocomplete(this.menu.getTopSelectable());\n      }\n    },\n\n    _onQueryChanged: function onQueryChanged(e, query) {\n      this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();\n    },\n\n    _onWhitespaceChanged: function onWhitespaceChanged() {\n      this._updateHint();\n    },\n\n    _onLangDirChanged: function onLangDirChanged(e, dir) {\n      if (this.dir !== dir) {\n        this.dir = dir;\n        this.menu.setLanguageDirection(dir);\n      }\n    },\n\n    // ### private\n\n    _openIfActive: function openIfActive() {\n      this.isActive() && this.open();\n    },\n\n    _minLengthMet: function minLengthMet(query) {\n      query = _.isString(query) ? query : (this.input.getQuery() || '');\n\n      return query.length >= this.minLength;\n    },\n\n    _updateHint: function updateHint() {\n      var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;\n\n      $selectable = this.menu.getTopSelectable();\n      data = this.menu.getSelectableData($selectable);\n      val = this.input.getInputValue();\n\n      if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {\n        query = Input.normalizeQuery(val);\n        escapedQuery = _.escapeRegExChars(query);\n\n        // match input value, then capture trailing text\n        frontMatchRegEx = new RegExp('^(?:' + escapedQuery + ')(.+$)', 'i');\n        match = frontMatchRegEx.exec(data.val);\n\n        // clear hint if there's no trailing text\n        match && this.input.setHint(val + match[1]);\n      }\n\n      else {\n        this.input.clearHint();\n      }\n    },\n\n    // ### public\n\n    isEnabled: function isEnabled() {\n      return this.enabled;\n    },\n\n    enable: function enable() {\n      this.enabled = true;\n    },\n\n    disable: function disable() {\n      this.enabled = false;\n    },\n\n    isActive: function isActive() {\n      return this.active;\n    },\n\n    activate: function activate() {\n      // already active\n      if (this.isActive()) {\n        return true;\n      }\n\n      // unable to activate either due to the typeahead being disabled\n      // or due to the active event being prevented\n      else if (!this.isEnabled() || this.eventBus.before('active')) {\n        return false;\n      }\n\n      // activate\n      else {\n        this.active = true;\n        this.eventBus.trigger('active');\n        return true;\n      }\n    },\n\n    deactivate: function deactivate() {\n      // already idle\n      if (!this.isActive()) {\n        return true;\n      }\n\n      // unable to deactivate due to the idle event being prevented\n      else if (this.eventBus.before('idle')) {\n        return false;\n      }\n\n      // deactivate\n      else {\n        this.active = false;\n        this.close();\n        this.eventBus.trigger('idle');\n        return true;\n      }\n    },\n\n    isOpen: function isOpen() {\n      return this.menu.isOpen();\n    },\n\n    open: function open() {\n      if (!this.isOpen() && !this.eventBus.before('open')) {\n        this.menu.open();\n        this._updateHint();\n        this.eventBus.trigger('open');\n      }\n\n      return this.isOpen();\n    },\n\n    close: function close() {\n      if (this.isOpen() && !this.eventBus.before('close')) {\n        this.menu.close();\n        this.input.clearHint();\n        this.input.resetInputValue();\n        this.eventBus.trigger('close');\n      }\n      return !this.isOpen();\n    },\n\n    setVal: function setVal(val) {\n      // expect val to be a string, so be safe, and coerce\n      this.input.setQuery(_.toStr(val));\n    },\n\n    getVal: function getVal() {\n      return this.input.getQuery();\n    },\n\n    select: function select($selectable) {\n      var data = this.menu.getSelectableData($selectable);\n\n      if (data && !this.eventBus.before('select', data.obj)) {\n        this.input.setQuery(data.val, true);\n\n        this.eventBus.trigger('select', data.obj);\n        this.close();\n\n        // return true if selection succeeded\n        return true;\n      }\n\n      return false;\n    },\n\n    autocomplete: function autocomplete($selectable) {\n      var query, data, isValid;\n\n      query = this.input.getQuery();\n      data = this.menu.getSelectableData($selectable);\n      isValid = data && query !== data.val;\n\n      if (isValid && !this.eventBus.before('autocomplete', data.obj)) {\n        this.input.setQuery(data.val);\n        this.eventBus.trigger('autocomplete', data.obj);\n\n        // return true if autocompletion succeeded\n        return true;\n      }\n\n      return false;\n    },\n\n    moveCursor: function moveCursor(delta) {\n      var query, $candidate, data, payload, cancelMove;\n\n      query = this.input.getQuery();\n      $candidate = this.menu.selectableRelativeToCursor(delta);\n      data = this.menu.getSelectableData($candidate);\n      payload = data ? data.obj : null;\n\n      // update will return true when it's a new query and new suggestions\n      // need to be fetched – in this case we don't want to move the cursor\n      cancelMove = this._minLengthMet() && this.menu.update(query);\n\n      if (!cancelMove && !this.eventBus.before('cursorchange', payload)) {\n        this.menu.setCursor($candidate);\n\n        // cursor moved to different selectable\n        if (data) {\n          this.input.setInputValue(data.val);\n        }\n\n        // cursor moved off of selectables, back to input\n        else {\n          this.input.resetInputValue();\n          this._updateHint();\n        }\n\n        this.eventBus.trigger('cursorchange', payload);\n\n        // return true if move succeeded\n        return true;\n      }\n\n      return false;\n    },\n\n    destroy: function destroy() {\n      this.input.destroy();\n      this.menu.destroy();\n    }\n  });\n\n  return Typeahead;\n\n  // helper functions\n  // ----------------\n\n  function c(ctx) {\n    var methods = [].slice.call(arguments, 1);\n\n    return function() {\n      var args = [].slice.call(arguments);\n\n      _.each(methods, function(method) {\n        return ctx[method].apply(ctx, args);\n      });\n    };\n  }\n})();\n"
  },
  {
    "path": "src/typeahead/www.js",
    "content": "/*\n * typeahead.js\n * https://github.com/twitter/typeahead.js\n * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT\n */\n\nvar WWW = (function() {\n  'use strict';\n\n  var defaultClassNames = {\n    wrapper: 'twitter-typeahead',\n    input: 'tt-input',\n    hint: 'tt-hint',\n    menu: 'tt-menu',\n    dataset: 'tt-dataset',\n    suggestion: 'tt-suggestion',\n    selectable: 'tt-selectable',\n    empty: 'tt-empty',\n    open: 'tt-open',\n    cursor: 'tt-cursor',\n    highlight: 'tt-highlight'\n  };\n\n  return build;\n\n  function build(o) {\n    var www, classes;\n\n    classes = _.mixin({}, defaultClassNames, o);\n\n    www = {\n      css: buildCss(),\n      classes: classes,\n      html: buildHtml(classes),\n      selectors: buildSelectors(classes)\n    };\n\n    return {\n      css: www.css,\n      html: www.html,\n      classes: www.classes,\n      selectors: www.selectors,\n      mixin: function(o) { _.mixin(o, www); }\n    };\n  }\n\n  function buildHtml(c) {\n    return {\n      wrapper: '<span class=\"' + c.wrapper + '\"></span>',\n      menu: '<div class=\"' + c.menu + '\"></div>'\n    };\n  }\n\n  function buildSelectors(classes) {\n    var selectors = {};\n    _.each(classes, function(v, k) { selectors[k] = '.' + v; });\n\n    return selectors;\n  }\n\n  function buildCss() {\n    var css =  {\n      wrapper: {\n        position: 'relative',\n        display: 'inline-block'\n      },\n      hint: {\n        position: 'absolute',\n        top: '0',\n        left: '0',\n        borderColor: 'transparent',\n        boxShadow: 'none',\n        // #741: fix hint opacity issue on iOS\n        opacity: '1'\n      },\n      input: {\n        position: 'relative',\n        verticalAlign: 'top',\n        backgroundColor: 'transparent'\n      },\n      inputWithNoHint: {\n        position: 'relative',\n        verticalAlign: 'top'\n      },\n      menu: {\n        position: 'absolute',\n        top: '100%',\n        left: '0',\n        zIndex: '100',\n        display: 'none'\n      },\n      ltr: {\n        left: '0',\n        right: 'auto'\n      },\n      rtl: {\n        left: 'auto',\n        right:' 0'\n      }\n    };\n\n    // ie specific styling\n    if (_.isMsie()) {\n       // ie6-8 (and 9?) doesn't fire hover and click events for elements with\n       // transparent backgrounds, for a workaround, use 1x1 transparent gif\n      _.mixin(css.input, {\n        backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)'\n      });\n    }\n\n    return css;\n  }\n})();\n"
  },
  {
    "path": "test/bloodhound/bloodhound_spec.js",
    "content": "describe('Bloodhound', function() {\n\n  function build(o) {\n    return new Bloodhound(_.mixin({\n      datumTokenizer: datumTokenizer,\n      queryTokenizer: queryTokenizer\n    }, o || {}));\n  }\n\n  beforeEach(function() {\n    jasmine.Remote.useMock();\n    jasmine.Prefetch.useMock();\n    jasmine.Transport.useMock();\n    jasmine.PersistentStorage.useMock();\n  });\n\n  afterEach(function() {\n    clearAjaxRequests();\n  });\n\n  describe('#initialize', function() {\n    beforeEach(function() {\n      this.bloodhound = build({ initialize: false });\n      spyOn(this.bloodhound, '_initialize').andCallThrough();\n    });\n\n    it('should not initialize if intialize option is false', function() {\n      expect(this.bloodhound._initialize).not.toHaveBeenCalled();\n    });\n\n    it('should not support reinitialization by default', function() {\n      var p1, p2;\n\n      p1 = this.bloodhound.initialize();\n      p2 = this.bloodhound.initialize();\n\n      expect(p1).toBe(p2);\n      expect(this.bloodhound._initialize.callCount).toBe(1);\n    });\n\n    it('should reinitialize if reintialize flag is true', function() {\n      var p1, p2;\n\n      p1 = this.bloodhound.initialize();\n      p2 = this.bloodhound.initialize(true);\n\n      expect(p1).not.toBe(p2);\n      expect(this.bloodhound._initialize.callCount).toBe(2);\n    });\n\n    it('should clear the index', function() {\n      this.bloodhound = build({ initialize: false, prefetch: '/prefetch' });\n      spyOn(this.bloodhound, 'clear');\n      this.bloodhound.initialize();\n\n      expect(this.bloodhound.clear).toHaveBeenCalled();\n    });\n\n    it('should load data from prefetch cache if available', function() {\n      this.bloodhound = build({ initialize: false, prefetch: '/prefetch' });\n      this.bloodhound.prefetch.fromCache.andReturn(fixtures.serialized.simple);\n      this.bloodhound.initialize();\n\n      expect(this.bloodhound.all()).toEqual(fixtures.data.simple);\n      expect(this.bloodhound.prefetch.fromNetwork).not.toHaveBeenCalled();\n    });\n\n    it('should load data from prefetch network as fallback', function() {\n      this.bloodhound = build({ initialize: false, prefetch: '/prefetch' });\n      this.bloodhound.prefetch.fromCache.andReturn(null);\n      this.bloodhound.prefetch.fromNetwork.andCallFake(fakeFromNetwork);\n      this.bloodhound.initialize();\n\n      expect(this.bloodhound.all()).toEqual(fixtures.data.simple);\n\n      function fakeFromNetwork(cb) { cb(null, fixtures.data.simple); }\n    });\n\n    it('should store prefetch network data in the prefetch cache', function() {\n      this.bloodhound = build({ initialize: false, prefetch: '/prefetch' });\n      this.bloodhound.prefetch.fromCache.andReturn(null);\n      this.bloodhound.prefetch.fromNetwork.andCallFake(fakeFromNetwork);\n      this.bloodhound.initialize();\n\n      expect(this.bloodhound.prefetch.store)\n      .toHaveBeenCalledWith(fixtures.serialized.simple);\n\n      function fakeFromNetwork(cb) { cb(null, fixtures.data.simple); }\n    });\n\n    it('should add local after prefetch is loaded', function() {\n      this.bloodhound = build({\n        initialize: false,\n        local: [{ foo: 'bar' }],\n        prefetch: '/prefetch'\n      });\n      this.bloodhound.prefetch.fromNetwork.andCallFake(fakeFromNetwork);\n\n      expect(this.bloodhound.all()).toEqual([]);\n      this.bloodhound.initialize();\n      expect(this.bloodhound.all()).toEqual([{ foo: 'bar' }]);\n\n      function fakeFromNetwork(cb) { cb(null, []); }\n    });\n  });\n\n  describe('#add', function() {\n    it('should add datums to search index', function() {\n      var spy = jasmine.createSpy();\n\n      this.bloodhound = build().add(fixtures.data.simple);\n\n      this.bloodhound.search('big', spy);\n\n      expect(spy).toHaveBeenCalledWith([\n        { value: 'big' },\n        { value: 'bigger' },\n        { value: 'biggest' }\n      ]);\n    });\n  });\n\n  describe('#get', function() {\n    beforeEach(function() {\n      this.bloodhound = build({\n        identify: function(d) { return d.value; },\n        local: fixtures.data.simple\n      });\n    });\n\n    it('should support array signature', function() {\n      expect(this.bloodhound.get(['big', 'bigger'])).toEqual([\n        { value: 'big' },\n        { value: 'bigger' }\n      ]);\n    });\n\n    it('should support splat signature', function() {\n      expect(this.bloodhound.get('big', 'bigger')).toEqual([\n        { value: 'big' },\n        { value: 'bigger' }\n      ]);\n    });\n\n    it('should return nothing for unknown ids', function() {\n      expect(this.bloodhound.get('big', 'foo', 'bigger')).toEqual([\n        { value: 'big' },\n        { value: 'bigger' }\n      ]);\n    });\n  });\n\n  describe('#clear', function() {\n    it('should remove all datums to search index', function() {\n      var spy = jasmine.createSpy();\n\n      this.bloodhound = build({ local: fixtures.data.simple }).clear();\n\n      this.bloodhound.search('big', spy);\n\n      expect(spy).toHaveBeenCalledWith([]);\n    });\n  });\n\n  describe('#clearPrefetchCache', function() {\n    it('should clear persistent storage', function() {\n      this.bloodhound = build({ prefetch: '/prefetch' }).clearPrefetchCache();\n      expect(this.bloodhound.prefetch.clear).toHaveBeenCalled();\n    });\n  });\n\n  describe('#clearRemoteCache', function() {\n    it('should clear remote request cache', function() {\n      spyOn(Transport, 'resetCache');\n      this.bloodhound = build({ remote: '/remote' }).clearRemoteCache();\n      expect(Transport.resetCache).toHaveBeenCalled();\n    });\n  });\n\n  describe('#all', function() {\n    it('should return all local results', function() {\n      this.bloodhound = build({ local: fixtures.data.simple });\n      expect(this.bloodhound.all()).toEqual(fixtures.data.simple);\n    });\n  });\n\n  describe('#search – local', function() {\n    it('should return sync matches', function() {\n      var spy = jasmine.createSpy();\n\n      this.bloodhound = build({ local: fixtures.data.simple });\n\n      this.bloodhound.search('big', spy);\n\n      expect(spy).toHaveBeenCalledWith([\n        { value: 'big' },\n        { value: 'bigger' },\n        { value: 'biggest' }\n      ]);\n    });\n  });\n\n  describe('#search – prefetch', function() {\n    it('should return sync matches', function() {\n      var spy = jasmine.createSpy();\n\n      this.bloodhound = build({ initialize: false, prefetch: '/prefetch' });\n      this.bloodhound.prefetch.fromCache.andReturn(fixtures.serialized.simple);\n      this.bloodhound.initialize();\n\n      this.bloodhound.search('big', spy);\n\n      expect(spy).toHaveBeenCalledWith([\n        { value: 'big' },\n        { value: 'bigger' },\n        { value: 'biggest' }\n      ]);\n    });\n  });\n\n  describe('#search – remote', function() {\n    it('should return async matches', function() {\n      var spy = jasmine.createSpy();\n\n      this.bloodhound = build({ remote: '/remote' });\n      this.bloodhound.remote.get.andCallFake(fakeGet);\n      this.bloodhound.search('dog', $.noop, spy);\n\n      expect(spy.callCount).toBe(1);\n\n      function fakeGet(o, cb) { cb(fixtures.data.animals); }\n    });\n  });\n\n  describe('#search – integration', function() {\n    it('should backfill when local/prefetch is not sufficient', function() {\n      var syncSpy, asyncSpy;\n\n      syncSpy = jasmine.createSpy();\n      asyncSpy = jasmine.createSpy();\n\n      this.bloodhound = build({\n        sufficient: 3,\n        local: fixtures.data.simple,\n        remote: '/remote'\n      });\n      this.bloodhound.remote.get.andCallFake(fakeGet);\n\n      this.bloodhound.search('big', syncSpy, asyncSpy);\n\n      expect(syncSpy).toHaveBeenCalledWith([\n        { value: 'big' },\n        { value: 'bigger' },\n        { value: 'biggest' }\n      ]);\n      expect(asyncSpy).not.toHaveBeenCalled();\n\n      this.bloodhound.search('bigg', syncSpy, asyncSpy);\n\n      expect(syncSpy).toHaveBeenCalledWith([\n        { value: 'bigger' },\n        { value: 'biggest' }\n      ]);\n      expect(asyncSpy).toHaveBeenCalledWith(fixtures.data.animals);\n\n      function fakeGet(o, cb) { cb(fixtures.data.animals); }\n    });\n\n    it('should remove duplicates from backfill', function() {\n      var syncSpy, asyncSpy;\n\n      syncSpy = jasmine.createSpy();\n      asyncSpy = jasmine.createSpy();\n\n      this.bloodhound = build({\n        identify: function(d) { return d.value; },\n        local: fixtures.data.animals,\n        remote: '/remote'\n      });\n      this.bloodhound.remote.get.andCallFake(fakeGet);\n\n      this.bloodhound.search('dog', syncSpy, asyncSpy);\n\n      expect(syncSpy).toHaveBeenCalledWith([{ value: 'dog' }]);\n      expect(asyncSpy).toHaveBeenCalledWith([\n        { value: 'cat' },\n        { value: 'moose' }\n      ]);\n\n      function fakeGet(o, cb) { cb(fixtures.data.animals); }\n    });\n  });\n\n  // helper functions\n  // ----------------\n\n  function datumTokenizer(d) { return $.trim(d.value).split(/\\s+/); }\n  function queryTokenizer(s) { return $.trim(s).split(/\\s+/); }\n});\n"
  },
  {
    "path": "test/bloodhound/lru_cache_spec.js",
    "content": "describe('LruCache', function() {\n\n  beforeEach(function() {\n    this.cache = new LruCache(3);\n  });\n\n  it('should make entries retrievable by their keys', function() {\n    var key = 'key', val = 42;\n\n    this.cache.set(key, val);\n    expect(this.cache.get(key)).toBe(val);\n  });\n\n  it('should return undefined if key has not been set', function() {\n    expect(this.cache.get('wat?')).toBeUndefined();\n  });\n\n  it('should hold up to maxSize entries', function() {\n    this.cache.set('one', 1);\n    this.cache.set('two', 2);\n    this.cache.set('three', 3);\n    this.cache.set('four', 4);\n\n    expect(this.cache.get('one')).toBeUndefined();\n    expect(this.cache.get('two')).toBe(2);\n    expect(this.cache.get('three')).toBe(3);\n    expect(this.cache.get('four')).toBe(4);\n  });\n\n  it('should evict lru entry if cache is full', function() {\n    this.cache.set('one', 1);\n    this.cache.set('two', 2);\n    this.cache.set('three', 3);\n    this.cache.get('one');\n    this.cache.set('four', 4);\n\n    expect(this.cache.get('one')).toBe(1);\n    expect(this.cache.get('two')).toBeUndefined();\n    expect(this.cache.get('three')).toBe(3);\n    expect(this.cache.get('four')).toBe(4);\n    expect(this.cache.size).toBe(3);\n  });\n});\n"
  },
  {
    "path": "test/bloodhound/options_parser_spec.js",
    "content": "describe('options parser', function() {\n\n  function build(o) {\n    return oParser(_.mixin({\n      datumTokenizer: $.noop,\n      queryTokenizer: $.noop\n    }, o || {}));\n  }\n\n  function prefetch(o) {\n    return oParser({\n      datumTokenizer: $.noop,\n      queryTokenizer: $.noop,\n      prefetch: _.mixin({\n        url: '/example'\n      }, o || {})\n    });\n  }\n\n  function remote(o) {\n    return oParser({\n      datumTokenizer: $.noop,\n      queryTokenizer: $.noop,\n      remote: _.mixin({\n        url: '/example'\n      }, o || {})\n    });\n  }\n\n  it('should throw exception if datumTokenizer is not set', function() {\n    expect(parse).toThrow();\n    function parse() { build({ datumTokenizer: null }); }\n  });\n\n  it('should throw exception if queryTokenizer is not set', function() {\n    expect(parse).toThrow();\n    function parse() { build({ queryTokenizer: null }); }\n  });\n\n  it('should wrap sorter', function() {\n    var o = build({ sorter: function(a, b) {  return a -b; } });\n    expect(o.sorter([2, 1, 3])).toEqual([1, 2, 3]);\n  });\n\n  it('should default sorter to identity function', function() {\n    var o = build();\n    expect(o.sorter([2, 1, 3])).toEqual([2, 1, 3]);\n  });\n\n  describe('local', function() {\n    it('should default to empty array', function() {\n      var o = build();\n      expect(o.local).toEqual([]);\n    });\n\n    it('should support function', function() {\n      var o = build({ local: function() { return [1]; } });\n      expect(o.local).toEqual([1]);\n    });\n\n    it('should support arrays', function() {\n      var o = build({ local: [1] });\n      expect(o.local).toEqual([1]);\n    });\n  });\n\n  describe('prefetch', function() {\n    it('should throw exception if url is not set', function() {\n      expect(parse).toThrow();\n      function parse() { prefetch({ url: null }); }\n    });\n\n    it('should support simple string format', function() {\n      expect(build({ prefetch: '/prefetch' }).prefetch).toBeDefined();\n    });\n\n    it('should default ttl to 1 day', function() {\n      var o = prefetch();\n      expect(o.prefetch.ttl).toBe(86400000);\n    });\n\n    it('should default cache to true', function() {\n      var o = prefetch();\n      expect(o.prefetch.cache).toBe(true);\n    });\n\n    it('should default transform to identiy function', function() {\n      var o = prefetch();\n      expect(o.prefetch.transform('foo')).toBe('foo');\n    });\n\n    it('should default cacheKey to url', function() {\n      var o = prefetch();\n      expect(o.prefetch.cacheKey).toBe(o.prefetch.url);\n    });\n\n    it('should default transport to jQuery.ajax', function() {\n      var o = prefetch();\n      expect(o.prefetch.transport).toBe($.ajax);\n    });\n\n    it('should prepend verison to thumbprint', function() {\n      var o = prefetch();\n      expect(o.prefetch.thumbprint).toBe('%VERSION%');\n\n      o = prefetch({ thumbprint: 'foo' });\n      expect(o.prefetch.thumbprint).toBe('%VERSION%foo');\n    });\n\n    it('should wrap custom transport to be deferred compatible', function() {\n      var o, errDeferred, successDeferred;\n\n      o = prefetch({ transport: errTransport });\n      errDeferred = o.prefetch.transport('q');\n\n      o = prefetch({ transport: successTransport });\n      successDeferred = o.prefetch.transport('q');\n\n      waits(0);\n      runs(function() {\n        expect(errDeferred.isRejected()).toBe(true);\n        expect(successDeferred.isResolved()).toBe(true);\n      });\n\n      function errTransport(q, success, error) { error(); }\n      function successTransport(q, success, error) { success(); }\n    });\n  });\n\n  describe('remote', function() {\n    it('should throw exception if url is not set', function() {\n      expect(parse).toThrow();\n      function parse() { remote({ url: null }); }\n    });\n\n    it('should support simple string format', function() {\n      expect(build({ remote: '/remote' }).remote).toBeDefined();\n    });\n\n    it('should default transform to identiy function', function() {\n      var o = remote();\n      expect(o.remote.transform('foo')).toBe('foo');\n    });\n\n    it('should default transport to jQuery.ajax', function() {\n      var o = remote();\n      expect(o.remote.transport).toBe($.ajax);\n    });\n\n    it('should default limiter to debouce', function() {\n      var o = remote();\n      expect(o.remote.limiter.name).toBe('debounce');\n    });\n\n    it('should default prepare to identity function', function() {\n      var o = remote();\n      expect(o.remote.prepare('q', { url: '/foo' })).toEqual({ url: '/foo' });\n    });\n\n    it('should support wildcard for prepare', function() {\n      var o = remote({ wildcard: '%FOO' });\n      expect(o.remote.prepare('=', { url: '/%FOO' })).toEqual({ url: '/%3D' });\n    });\n\n    it('should support replace for prepare', function() {\n      var o = remote({ replace: function() { return '/bar'; } });\n      expect(o.remote.prepare('q', { url: '/foo' })).toEqual({ url: '/bar' });\n    });\n\n    it('should should rateLimitBy for limiter', function() {\n      var o = remote({ rateLimitBy: 'throttle' });\n      expect(o.remote.limiter.name).toBe('throttle');\n    });\n\n    it('should wrap custom transport to be deferred compatible', function() {\n      var o, errDeferred, successDeferred;\n\n      o = remote({ transport: errTransport });\n      errDeferred = o.remote.transport('q');\n\n      o = remote({ transport: successTransport });\n      successDeferred = o.remote.transport('q');\n\n      waits(0);\n      runs(function() {\n        expect(errDeferred.isRejected()).toBe(true);\n        expect(successDeferred.isResolved()).toBe(true);\n      });\n\n      function errTransport(q, success, error) { error(); }\n      function successTransport(q, success, error) { success(); }\n    });\n  });\n});\n"
  },
  {
    "path": "test/bloodhound/persistent_storage_spec.js",
    "content": "describe('PersistentStorage', function() {\n  var engine, ls;\n\n  // test suite is dependent on localStorage being available\n  if (!window.localStorage) {\n    console.warn('no localStorage support – skipping PersistentStorage suite');\n    return;\n  }\n\n  // for good measure!\n  localStorage.clear();\n\n  beforeEach(function() {\n    ls = {\n      get length() { return localStorage.length; },\n      key: spyThrough('key'),\n      clear: spyThrough('clear'),\n      getItem: spyThrough('getItem'),\n      setItem: spyThrough('setItem'),\n      removeItem: spyThrough('removeItem')\n    };\n\n    engine = new PersistentStorage('ns', ls);\n    spyOn(Date.prototype, 'getTime').andReturn(0);\n  });\n\n  afterEach(function() {\n    localStorage.clear();\n  });\n\n  // public methods\n  // --------------\n\n  describe('#get', function() {\n    it('should access localStorage with prefixed key', function() {\n      engine.get('key');\n      expect(ls.getItem).toHaveBeenCalledWith('__ns__key');\n    });\n\n    it('should return undefined when key does not exist', function() {\n      expect(engine.get('does not exist')).toEqual(undefined);\n    });\n\n    it('should return value as correct type', function() {\n      engine.set('string', 'i am a string');\n      engine.set('number', 42);\n      engine.set('boolean', true);\n      engine.set('null', null);\n      engine.set('object', { obj: true });\n\n      expect(engine.get('string')).toEqual('i am a string');\n      expect(engine.get('number')).toEqual(42);\n      expect(engine.get('boolean')).toEqual(true);\n      expect(engine.get('null')).toBeNull();\n      expect(engine.get('object')).toEqual({ obj: true });\n    });\n\n    it('should expire stale keys', function() {\n      engine.set('key', 'value', -1);\n\n      expect(engine.get('key')).toBeNull();\n      expect(ls.getItem('__ns__key__ttl')).toBeNull();\n    });\n  });\n\n  describe('#set', function() {\n    it('should access localStorage with prefixed key', function() {\n      engine.set('key', 'val');\n      expect(ls.setItem.mostRecentCall.args[0]).toEqual('__ns__key');\n    });\n\n    it('should JSON.stringify value before storing', function() {\n      engine.set('key', 'val');\n      expect(ls.setItem.mostRecentCall.args[1]).toEqual(JSON.stringify('val'));\n    });\n\n    it('should store ttl if provided', function() {\n      var ttl = 1;\n      engine.set('key', 'value', ttl);\n\n      expect(ls.setItem.argsForCall[0])\n      .toEqual(['__ns__key__ttl__', ttl.toString()]);\n    });\n\n    it('should call clear if the localStorage limit has been reached', function() {\n      var spy;\n\n      ls.setItem.andCallFake(function() {\n        var err = new Error();\n        err.name = 'QuotaExceededError';\n\n        throw err;\n      });\n\n      engine.clear = spy = jasmine.createSpy();\n      engine.set('key', 'value', 1);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should noop if the localStorage limit has been reached', function() {\n      var get, set, remove, clear, isExpired;\n\n      ls.setItem.andCallFake(function() {\n        var err = new Error();\n        err.name = 'QuotaExceededError';\n\n        throw err;\n      });\n\n      get = engine.get;\n      set = engine.set;\n      remove = engine.remove;\n      clear = engine.clear;\n      isExpired = engine.isExpired;\n\n      engine.set('key', 'value', 1);\n\n      expect(engine.get).not.toBe(get);\n      expect(engine.set).not.toBe(set);\n      expect(engine.remove).not.toBe(remove);\n      expect(engine.clear).not.toBe(clear);\n      expect(engine.isExpired).not.toBe(isExpired);\n    });\n  });\n\n  describe('#remove', function() {\n\n    it('should remove key from storage', function() {\n      engine.set('key', 'val');\n      engine.remove('key');\n\n      expect(engine.get('key')).toBeNull();\n    });\n  });\n\n  describe('#clear', function() {\n    it('should work with namespaces that contain regex characters', function() {\n      engine = new PersistentStorage('ns?()');\n      engine.set('key1', 'val1');\n      engine.set('key2', 'val2');\n      engine.clear();\n\n      expect(engine.get('key1')).toEqual(undefined);\n      expect(engine.get('key2')).toEqual(undefined);\n    });\n\n    it('should remove all keys that exist in namespace of engine', function() {\n      engine.set('key1', 'val1');\n      engine.set('key2', 'val2');\n      engine.set('key3', 'val3');\n      engine.set('key4', 'val4', 0);\n      engine.clear();\n\n      expect(engine.get('key1')).toEqual(undefined);\n      expect(engine.get('key2')).toEqual(undefined);\n      expect(engine.get('key3')).toEqual(undefined);\n      expect(engine.get('key4')).toEqual(undefined);\n    });\n\n    it('should not affect keys with different namespace', function() {\n      ls.setItem('diff_namespace', 'val');\n      engine.clear();\n\n      expect(ls.getItem('diff_namespace')).toEqual('val');\n    });\n  });\n\n  describe('#isExpired', function() {\n    it('should be false for keys without ttl', function() {\n      engine.set('key', 'value');\n      expect(engine.isExpired('key')).toBe(false);\n    });\n\n    it('should be false for fresh keys', function() {\n      engine.set('key', 'value', 1);\n      expect(engine.isExpired('key')).toBe(false);\n    });\n\n    it('should be true for stale keys', function() {\n      engine.set('key', 'value', -1);\n      expect(engine.isExpired('key')).toBe(true);\n    });\n  });\n\n  // compatible across browsers\n  function spyThrough(method) {\n    return jasmine.createSpy().andCallFake(fake);\n\n    function fake() {\n      return localStorage[method].apply(localStorage, arguments);\n    }\n  }\n});\n"
  },
  {
    "path": "test/bloodhound/prefetch_spec.js",
    "content": "describe('Prefetch', function() {\n\n  function build(o) {\n    return new Prefetch(_.mixin({\n      url: '/prefetch',\n      ttl: 3600,\n      cache: true,\n      thumbprint: '',\n      cacheKey: 'cachekey',\n      prepare: function(x) { return x; },\n      transform: function(x) { return x; },\n      transport: $.ajax\n    }, o || {}));\n  }\n\n  beforeEach(function() {\n    jasmine.PersistentStorage.useMock();\n\n    this.prefetch = build();\n    this.storage = this.prefetch.storage;\n    this.thumbprint = this.prefetch.thumbprint;\n  });\n\n  describe('#clear', function() {\n    it('should clear cache storage', function() {\n      this.prefetch.clear();\n      expect(this.storage.clear).toHaveBeenCalled();\n    });\n  });\n\n  describe('#store', function() {\n    it('should store data in the storage cache', function() {\n      this.prefetch.store({ foo: 'bar' });\n\n      expect(this.storage.set)\n      .toHaveBeenCalledWith('data', { foo: 'bar' }, 3600);\n    });\n\n    it('should store thumbprint in the storage cache', function() {\n      this.prefetch.store({ foo: 'bar' });\n\n      expect(this.storage.set)\n      .toHaveBeenCalledWith('thumbprint', jasmine.any(String), 3600);\n    });\n\n    it('should store protocol in the storage cache', function() {\n      this.prefetch.store({ foo: 'bar' });\n\n      expect(this.storage.set)\n      .toHaveBeenCalledWith('protocol', location.protocol, 3600);\n    });\n\n    it('should be noop if cache option is false', function() {\n      this.prefetch = build({ cache: false });\n\n      this.prefetch.store({ foo: 'bar' });\n\n      expect(this.storage.set).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('#fromCache', function() {\n    it('should return data if available', function() {\n      this.storage.get\n      .andCallFake(fakeStorageGet({ foo: 'bar' }, this.thumbprint));\n\n      expect(this.prefetch.fromCache()).toEqual({ foo: 'bar' });\n    });\n\n    it('should return null if data is expired', function() {\n      this.storage.get\n      .andCallFake(fakeStorageGet({ foo: 'bar' }, 'foo'));\n\n      expect(this.prefetch.fromCache()).toBeNull();\n    });\n\n    it('should return null if data does not exist', function() {\n      this.storage.get\n      .andCallFake(fakeStorageGet(null, this.thumbprint));\n\n      expect(this.prefetch.fromCache()).toBeNull();\n    });\n\n    it('should return null if cache option is false', function() {\n      this.prefetch = build({ cache: false });\n\n      this.storage.get\n      .andCallFake(fakeStorageGet({ foo: 'bar' }, this.thumbprint));\n\n      expect(this.prefetch.fromCache()).toBeNull();\n      expect(this.storage.get).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('#fromNetwork', function() {\n    it('should have sensible default request settings', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      spyOn(this.prefetch, 'transport').andReturn($.Deferred());\n\n      this.prefetch.fromNetwork(spy);\n\n      expect(this.prefetch.transport).toHaveBeenCalledWith({\n        url: '/prefetch',\n        type: 'GET',\n        dataType: 'json'\n      });\n    });\n\n    it('should transform request settings with prepare', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      spyOn(this.prefetch, 'prepare').andReturn({ foo: 'bar' });\n      spyOn(this.prefetch, 'transport').andReturn($.Deferred());\n\n      this.prefetch.fromNetwork(spy);\n\n      expect(this.prefetch.transport).toHaveBeenCalledWith({ foo: 'bar' });\n    });\n\n    it('should transform the response using transform', function() {\n      var spy;\n\n      this.prefetch = build({\n        transform: function() { return { bar: 'foo' }; }\n      });\n\n      spy = jasmine.createSpy();\n      spyOn(this.prefetch, 'transport')\n      .andReturn($.Deferred().resolve({ foo: 'bar' }));\n\n      this.prefetch.fromNetwork(spy);\n\n      expect(spy).toHaveBeenCalledWith(null, { bar: 'foo' });\n    });\n\n    it('should invoke callback with data if success', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      spyOn(this.prefetch, 'transport')\n      .andReturn($.Deferred().resolve({ foo: 'bar' }));\n\n      this.prefetch.fromNetwork(spy);\n\n      expect(spy).toHaveBeenCalledWith(null, { foo: 'bar' });\n    });\n\n    it('should invoke callback with err argument true if failure', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      spyOn(this.prefetch, 'transport').andReturn($.Deferred().reject());\n\n      this.prefetch.fromNetwork(spy);\n\n      expect(spy).toHaveBeenCalledWith(true);\n    });\n  });\n\n  function fakeStorageGet(data, thumbprint, protocol) {\n    return function(key) {\n      var val;\n\n      switch (key) {\n        case 'data':\n          val = data;\n          break;\n        case 'protocol':\n          val = protocol || location.protocol;\n          break;\n        case 'thumbprint':\n          val = thumbprint;\n          break;\n      }\n\n      return val;\n    };\n  }\n});\n"
  },
  {
    "path": "test/bloodhound/remote_spec.js",
    "content": "describe('Remote', function() {\n\n  beforeEach(function() {\n    jasmine.Transport.useMock();\n\n    this.remote = new Remote({\n      url: '/test?q=%QUERY',\n      prepare: function(x) { return x; },\n      transform: function(x) { return x; }\n    });\n\n    this.transport = this.remote.transport;\n  });\n\n  describe('#cancelLastRequest', function() {\n    it('should cancel last request', function() {\n      this.remote.cancelLastRequest();\n      expect(this.transport.cancel).toHaveBeenCalled();\n    });\n  });\n\n  describe('#get', function() {\n    it('should have sensible default request settings', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      spyOn(this.remote, 'prepare');\n\n      this.remote.get('foo', spy);\n\n      expect(this.remote.prepare).toHaveBeenCalledWith('foo', {\n        url: '/test?q=%QUERY',\n        type: 'GET',\n        dataType: 'json'\n      });\n    });\n\n    it('should transform request settings with prepare', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      spyOn(this.remote, 'prepare').andReturn([{ foo: 'bar' }]);\n\n      this.remote.get('foo', spy);\n\n      expect(this.transport.get)\n      .toHaveBeenCalledWith([{ foo: 'bar' }], jasmine.any(Function));\n    });\n\n    it('should transform response with transform', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      spyOn(this.remote, 'transform').andReturn([{ foo: 'bar' }]);\n      this.transport.get.andCallFake(function(_, cb) { cb(null, {}); });\n\n      this.remote.get('foo', spy);\n\n      expect(spy).toHaveBeenCalledWith([{ foo: 'bar' }]);\n    });\n\n    it('should return empty array on error', function() {\n      var spy;\n\n      spy = jasmine.createSpy();\n      this.transport.get.andCallFake(function(_, cb) { cb(true); });\n\n      this.remote.get('foo', spy);\n\n      expect(spy).toHaveBeenCalledWith([]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/bloodhound/search_index_spec.js",
    "content": "describe('SearchIndex', function() {\n\n  function build(o) {\n    return new SearchIndex(_.mixin({\n      datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),\n      queryTokenizer: Bloodhound.tokenizers.whitespace\n    }, o || {}));\n  }\n\n  beforeEach(function() {\n    this.index = build();\n    this.index.add(fixtures.data.simple);\n  });\n\n  it('should support serialization/deserialization', function() {\n    var serialized = this.index.serialize();\n\n    this.index.bootstrap(serialized);\n\n    expect(this.index.search('smaller')).toEqual([{ value: 'smaller' }]);\n  });\n\n  it('should be able to add data on the fly', function() {\n    this.index.add({ value: 'new' });\n\n    expect(this.index.search('new')).toEqual([{ value: 'new' }]);\n  });\n\n  it('#get should return datums by id', function() {\n    this.index = build({ identify: function(d) { return d.value; } });\n    this.index.add(fixtures.data.simple);\n\n    expect(this.index.get(['big', 'bigger'])).toEqual([\n      { value: 'big' },\n      { value: 'bigger' }\n    ]);\n  });\n\n  it('#search should return datums that match the given query', function() {\n    expect(this.index.search('big')).toEqual([\n      { value: 'big' },\n      { value: 'bigger' },\n      { value: 'biggest' }\n    ]);\n\n    expect(this.index.search('small')).toEqual([\n      { value: 'small' },\n      { value: 'smaller' },\n      { value: 'smallest' }\n    ]);\n  });\n\n  it('#search should return an empty array of there are no matches', function() {\n    expect(this.index.search('wtf')).toEqual([]);\n  });\n\n  it('#serach should handle multi-token queries', function() {\n    this.index.add({ value: 'foo bar' });\n    expect(this.index.search('foo b')).toEqual([{ value: 'foo bar' }]);\n  });\n\n  it('#all should return all datums', function() {\n    expect(this.index.all()).toEqual(fixtures.data.simple);\n  });\n\n  it('#reset should empty the search index', function() {\n    this.index.reset();\n    expect(this.index.datums).toEqual([]);\n    expect(this.index.trie.i).toEqual([]);\n    expect(this.index.trie.c).toEqual({});\n  });\n});\n"
  },
  {
    "path": "test/bloodhound/tokenizers_spec.js",
    "content": "describe('tokenizers', function() {\n\n  it('.whitespace should tokenize on whitespace', function() {\n    var tokens = tokenizers.whitespace('big-deal ok');\n    expect(tokens).toEqual(['big-deal', 'ok']);\n  });\n\n  it('.whitespace should treat null as empty string', function() {\n    var tokens = tokenizers.whitespace(null);\n    expect(tokens).toEqual([]);\n  });\n\n  it('.whitespace should treat undefined as empty string', function() {\n    var tokens = tokenizers.whitespace(undefined);\n    expect(tokens).toEqual([]);\n  });\n\n  it('.nonword should tokenize on non-word characters', function() {\n    var tokens = tokenizers.nonword('big-deal ok');\n    expect(tokens).toEqual(['big', 'deal', 'ok']);\n  });\n\n  it('.nonword should treat null as empty string', function() {\n    var tokens = tokenizers.nonword(null);\n    expect(tokens).toEqual([]);\n  });\n\n  it('.nonword should treat undefined as empty string', function() {\n    var tokens = tokenizers.nonword(undefined);\n    expect(tokens).toEqual([]);\n  });\n\n  it('.obj.whitespace should tokenize on whitespace', function() {\n    var t = tokenizers.obj.whitespace('val');\n    var tokens = t({ val: 'big-deal ok' });\n\n    expect(tokens).toEqual(['big-deal', 'ok']);\n  });\n\n  it('.obj.whitespace should accept multiple properties', function() {\n    var t = tokenizers.obj.whitespace('one', 'two');\n    var tokens = t({ one: 'big-deal ok', two: 'buzz' });\n\n    expect(tokens).toEqual(['big-deal', 'ok', 'buzz']);\n  });\n\n  it('.obj.whitespace should accept array', function() {\n    var t = tokenizers.obj.whitespace(['one', 'two']);\n    var tokens = t({ one: 'big-deal ok', two: 'buzz' });\n\n    expect(tokens).toEqual(['big-deal', 'ok', 'buzz']);\n  });\n\n  it('.obj.nonword should tokenize on non-word characters', function() {\n    var t = tokenizers.obj.nonword('val');\n    var tokens = t({ val: 'big-deal ok' });\n\n    expect(tokens).toEqual(['big', 'deal', 'ok']);\n  });\n\n  it('.obj.nonword should accept multiple properties', function() {\n    var t = tokenizers.obj.nonword('one', 'two');\n    var tokens = t({ one: 'big-deal ok', two: 'buzz' });\n\n    expect(tokens).toEqual(['big', 'deal', 'ok', 'buzz']);\n  });\n\n  it('.obj.nonword should accept array', function() {\n    var t = tokenizers.obj.nonword(['one', 'two']);\n    var tokens = t({ one: 'big-deal ok', two: 'buzz' });\n\n    expect(tokens).toEqual(['big', 'deal', 'ok', 'buzz']);\n  });\n});\n"
  },
  {
    "path": "test/bloodhound/transport_spec.js",
    "content": "describe('Transport', function() {\n\n  beforeEach(function() {\n    jasmine.Ajax.useMock();\n    jasmine.Clock.useMock();\n\n    this.transport = new Transport({ transport: $.ajax });\n  });\n\n  afterEach(function() {\n    // run twice to flush out  on-deck requests\n    $.each(ajaxRequests, drop);\n    $.each(ajaxRequests, drop);\n\n    clearAjaxRequests();\n    Transport.resetCache();\n\n    function drop(i, req) {\n      req.readyState !== 4 && req.response(fixtures.ajaxResps.ok);\n    }\n  });\n\n  it('should use jQuery.ajax as the default transport mechanism', function() {\n    var req, resp = fixtures.ajaxResps.ok, spy = jasmine.createSpy();\n\n    this.transport.get('/test', spy);\n\n    req = mostRecentAjaxRequest();\n    req.response(resp);\n\n    expect(req.url).toBe('/test');\n    expect(spy).toHaveBeenCalledWith(null, resp.parsed);\n  });\n\n  it('should respect maxPendingRequests configuration', function() {\n    for (var i = 0; i < 10; i++) {\n      this.transport.get('/test' + i, $.noop);\n    }\n\n    expect(ajaxRequests.length).toBe(6);\n  });\n\n  it('should support rate limiting', function() {\n    this.transport = new Transport({ transport: $.ajax, limiter: limiter });\n\n    for (var i = 0; i < 5; i++) {\n      this.transport.get('/test' + i, $.noop);\n    }\n\n    jasmine.Clock.tick(100);\n    expect(ajaxRequests.length).toBe(1);\n\n    function limiter(fn) { return _.debounce(fn, 20); }\n  });\n\n  it('should cache most recent requests', function() {\n    var spy1 = jasmine.createSpy(), spy2 = jasmine.createSpy();\n\n    this.transport.get('/test1', $.noop);\n    mostRecentAjaxRequest().response(fixtures.ajaxResps.ok);\n\n    this.transport.get('/test2', $.noop);\n    mostRecentAjaxRequest().response(fixtures.ajaxResps.ok1);\n\n    expect(ajaxRequests.length).toBe(2);\n\n    this.transport.get('/test1', spy1);\n    this.transport.get('/test2', spy2);\n\n    jasmine.Clock.tick(0);\n\n    // no ajax requests were made on subsequent requests\n    expect(ajaxRequests.length).toBe(2);\n\n    expect(spy1).toHaveBeenCalledWith(null, fixtures.ajaxResps.ok.parsed);\n    expect(spy2).toHaveBeenCalledWith(null, fixtures.ajaxResps.ok1.parsed);\n  });\n\n  it('should not cache requests if cache option is false', function() {\n    this.transport = new Transport({ transport: $.ajax, cache: false });\n\n    this.transport.get('/test1', $.noop);\n    mostRecentAjaxRequest().response(fixtures.ajaxResps.ok);\n    this.transport.get('/test1', $.noop);\n    mostRecentAjaxRequest().response(fixtures.ajaxResps.ok);\n\n    expect(ajaxRequests.length).toBe(2);\n  });\n\n  it('should prevent dog pile', function() {\n    var spy1 = jasmine.createSpy(), spy2 = jasmine.createSpy();\n\n    this.transport.get('/test1', spy1);\n    this.transport.get('/test1', spy2);\n\n    mostRecentAjaxRequest().response(fixtures.ajaxResps.ok);\n\n    expect(ajaxRequests.length).toBe(1);\n\n    waitsFor(function() { return spy1.callCount && spy2.callCount; });\n\n    runs(function() {\n      expect(spy1).toHaveBeenCalledWith(null, fixtures.ajaxResps.ok.parsed);\n      expect(spy2).toHaveBeenCalledWith(null, fixtures.ajaxResps.ok.parsed);\n    });\n  });\n\n  it('should always make a request for the last call to #get', function() {\n    var spy = jasmine.createSpy();\n\n    for (var i = 0; i < 6; i++) {\n      this.transport.get('/test' + i, $.noop);\n    }\n\n    this.transport.get('/test' + i, spy);\n    expect(ajaxRequests.length).toBe(6);\n\n    _.each(ajaxRequests, function(req) {\n      req.response(fixtures.ajaxResps.ok);\n    });\n\n    expect(ajaxRequests.length).toBe(7);\n    mostRecentAjaxRequest().response(fixtures.ajaxResps.ok);\n\n    expect(spy).toHaveBeenCalled();\n  });\n\n  it('should invoke the callback with err set to true on failure', function() {\n    var req, resp = fixtures.ajaxResps.err, spy = jasmine.createSpy();\n\n    this.transport.get('/test', spy);\n\n    req = mostRecentAjaxRequest();\n    req.response(resp);\n\n    expect(req.url).toBe('/test');\n    expect(spy).toHaveBeenCalledWith(true);\n  });\n\n  it('should not send cancelled requests', function() {\n    this.transport = new Transport({ transport: $.ajax, limiter: limiter });\n\n    this.transport.get('/test', $.noop);\n    this.transport.cancel();\n\n    jasmine.Clock.tick(100);\n    expect(ajaxRequests.length).toBe(0);\n\n    function limiter(fn) { return _.debounce(fn, 20); }\n  });\n\n  it('should not send outdated requests', function() {\n    this.transport = new Transport({ transport: $.ajax, limiter: limiter });\n\n    // warm cache\n    this.transport.get('/test1', $.noop);\n    jasmine.Clock.tick(100);\n    mostRecentAjaxRequest().response(fixtures.ajaxResps.ok);\n\n    expect(mostRecentAjaxRequest().url).toBe('/test1');\n    expect(ajaxRequests.length).toBe(1);\n\n    // within the same rate-limit cycle, request test2 and test1. test2 becomes\n    // outdated after test1 is requested and no request is sent for test1\n    // because it's a cache hit\n    this.transport.get('/test2', $.noop);\n    this.transport.get('/test1', $.noop);\n\n    jasmine.Clock.tick(100);\n\n    expect(ajaxRequests.length).toBe(1);\n\n    function limiter(fn) { return _.debounce(fn, 20); }\n  });\n});\n"
  },
  {
    "path": "test/ci",
    "content": "#!/bin/bash -x\n\nif [ \"$TEST_SUITE\" == \"unit\" ]; then\n  ./node_modules/karma/bin/karma start --single-run --browsers PhantomJS\nelif [ \"$TRAVIS_SECURE_ENV_VARS\" == \"true\" -a \"$TEST_SUITE\" == \"integration\" ]; then\n  static -p 8888 &\n  sleep 3\n  # integration tests are flaky, don't let them fail the build\n  ./node_modules/mocha/bin/mocha --harmony -R spec ./test/integration/test.js || true\nelse\n  echo \"Not running any tests\"\nfi\n"
  },
  {
    "path": "test/fixtures/ajax_responses.js",
    "content": "var fixtures = fixtures || {};\n\nfixtures.ajaxResps = {\n  ok: {\n    status: 200,\n    responseText: '[{ \"value\": \"big\" }, { \"value\": \"bigger\" }, { \"value\": \"biggest\" }, { \"value\": \"small\" }, { \"value\": \"smaller\" }, { \"value\": \"smallest\" }]'\n  },\n  ok1: {\n    status: 200,\n    responseText: '[\"dog\", \"cat\", \"moose\"]'\n  },\n  err: {\n    status: 500\n  }\n};\n\n$.each(fixtures.ajaxResps, function(i, resp) {\n  resp.responseText && (resp.parsed = $.parseJSON(resp.responseText));\n});\n"
  },
  {
    "path": "test/fixtures/data.js",
    "content": "var fixtures = fixtures || {};\n\nfixtures.data = {\n  simple: [\n    { value: 'big' },\n    { value: 'bigger' },\n    { value: 'biggest' },\n    { value: 'small' },\n    { value: 'smaller' },\n    { value: 'smallest' }\n  ],\n  animals: [\n    { value: 'dog' },\n    { value: 'cat' },\n    { value: 'moose' }\n  ]\n};\n\nfixtures.serialized = {\n  simple: {\n    \"datums\": {\n        \"{\\\"value\\\":\\\"big\\\"}\": {\n            \"value\": \"big\"\n        },\n        \"{\\\"value\\\":\\\"bigger\\\"}\": {\n            \"value\": \"bigger\"\n        },\n        \"{\\\"value\\\":\\\"biggest\\\"}\": {\n            \"value\": \"biggest\"\n        },\n        \"{\\\"value\\\":\\\"small\\\"}\": {\n            \"value\": \"small\"\n        },\n        \"{\\\"value\\\":\\\"smaller\\\"}\": {\n            \"value\": \"smaller\"\n        },\n        \"{\\\"value\\\":\\\"smallest\\\"}\": {\n            \"value\": \"smallest\"\n        }\n    },\n    \"trie\": {\n        \"i\": [],\n        \"c\": {\n            \"b\": {\n                \"i\": [\"{\\\"value\\\":\\\"big\\\"}\", \"{\\\"value\\\":\\\"bigger\\\"}\", \"{\\\"value\\\":\\\"biggest\\\"}\"],\n                \"c\": {\n                    \"i\": {\n                        \"i\": [\"{\\\"value\\\":\\\"big\\\"}\", \"{\\\"value\\\":\\\"bigger\\\"}\", \"{\\\"value\\\":\\\"biggest\\\"}\"],\n                        \"c\": {\n                            \"g\": {\n                                \"i\": [\"{\\\"value\\\":\\\"big\\\"}\", \"{\\\"value\\\":\\\"bigger\\\"}\", \"{\\\"value\\\":\\\"biggest\\\"}\"],\n                                \"c\": {\n                                    \"g\": {\n                                        \"i\": [\"{\\\"value\\\":\\\"bigger\\\"}\", \"{\\\"value\\\":\\\"biggest\\\"}\"],\n                                        \"c\": {\n                                            \"e\": {\n                                                \"i\": [\"{\\\"value\\\":\\\"bigger\\\"}\", \"{\\\"value\\\":\\\"biggest\\\"}\"],\n                                                \"c\": {\n                                                    \"r\": {\n                                                        \"i\": [\"{\\\"value\\\":\\\"bigger\\\"}\"],\n                                                        \"c\": {}\n                                                    },\n                                                    \"s\": {\n                                                        \"i\": [\"{\\\"value\\\":\\\"biggest\\\"}\"],\n                                                        \"c\": {\n                                                            \"t\": {\n                                                                \"i\": [\"{\\\"value\\\":\\\"biggest\\\"}\"],\n                                                                \"c\": {}\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            },\n            \"s\": {\n                \"i\": [\"{\\\"value\\\":\\\"small\\\"}\", \"{\\\"value\\\":\\\"smaller\\\"}\", \"{\\\"value\\\":\\\"smallest\\\"}\"],\n                \"c\": {\n                    \"m\": {\n                        \"i\": [\"{\\\"value\\\":\\\"small\\\"}\", \"{\\\"value\\\":\\\"smaller\\\"}\", \"{\\\"value\\\":\\\"smallest\\\"}\"],\n                        \"c\": {\n                            \"a\": {\n                                \"i\": [\"{\\\"value\\\":\\\"small\\\"}\", \"{\\\"value\\\":\\\"smaller\\\"}\", \"{\\\"value\\\":\\\"smallest\\\"}\"],\n                                \"c\": {\n                                    \"l\": {\n                                        \"i\": [\"{\\\"value\\\":\\\"small\\\"}\", \"{\\\"value\\\":\\\"smaller\\\"}\", \"{\\\"value\\\":\\\"smallest\\\"}\"],\n                                        \"c\": {\n                                            \"l\": {\n                                                \"i\": [\"{\\\"value\\\":\\\"small\\\"}\", \"{\\\"value\\\":\\\"smaller\\\"}\", \"{\\\"value\\\":\\\"smallest\\\"}\"],\n                                                \"c\": {\n                                                    \"e\": {\n                                                        \"i\": [\"{\\\"value\\\":\\\"smaller\\\"}\", \"{\\\"value\\\":\\\"smallest\\\"}\"],\n                                                        \"c\": {\n                                                            \"r\": {\n                                                                \"i\": [\"{\\\"value\\\":\\\"smaller\\\"}\"],\n                                                                \"c\": {}\n                                                            },\n                                                            \"s\": {\n                                                                \"i\": [\"{\\\"value\\\":\\\"smallest\\\"}\"],\n                                                                \"c\": {\n                                                                    \"t\": {\n                                                                        \"i\": [\"{\\\"value\\\":\\\"smallest\\\"}\"],\n                                                                        \"c\": {}\n                                                                    }\n                                                                }\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n}\n"
  },
  {
    "path": "test/fixtures/html.js",
    "content": "var fixtures = fixtures || {};\n\nfixtures.html = {\n  input: '<input class=\"tt-input\" type=\"text\" autocomplete=\"false\" spellcheck=\"false\">',\n  hint: '<input class=\"tt-hint\" type=\"text\" autocomplete=\"false\" spellcheck=\"false\" disabled>',\n  dataset: [\n    '<div class=\"tt-dataset-test\">',\n      '<div class=\"tt-selectable\"><p>one</p></div>',\n      '<div class=\"tt-selectable\"><p>two</p></div>',\n      '<div class=\"tt-selectable\"><p>three</p></div>',\n    '</div>'\n  ].join('')\n};\n"
  },
  {
    "path": "test/helpers/typeahead_mocks.js",
    "content": "(function(root) {\n  var components;\n\n  components = [\n    'Bloodhound',\n    'Prefetch',\n    'Remote',\n    'PersistentStorage',\n    'Transport',\n    'SearchIndex',\n    'Input',\n    'Dataset',\n    'Menu'\n    ];\n\n  for (var i = 0; i < components.length; i++) {\n    makeMockable(components[i]);\n  }\n\n  function makeMockable(component) {\n    var Original, Mock;\n\n    Original = root[component];\n    Mock = mock(Original);\n\n    jasmine[component] = { useMock: useMock, uninstallMock: uninstallMock };\n\n    function useMock() {\n      root[component] = Mock;\n      jasmine.getEnv().currentSpec.after(uninstallMock);\n    }\n\n    function uninstallMock() {\n      root[component] = Original;\n    }\n  }\n\n  function mock(Constructor) {\n    var constructorSpy;\n\n    Mock.prototype = Constructor.prototype;\n    constructorSpy = jasmine.createSpy('mock constructor').andCallFake(Mock);\n\n    // copy instance methods\n    for (var key in Constructor) {\n      if (typeof Constructor[key] === 'function') {\n        constructorSpy[key] = Constructor[key];\n      }\n    }\n\n    return constructorSpy;\n\n    function Mock() {\n      var instance = _.mixin({}, Constructor.prototype);\n\n      for (var key in instance) {\n        if (typeof instance[key] === 'function') {\n          spyOn(instance, key);\n\n          // special case for some components\n          if (key === 'bind') {\n            instance[key].andCallFake(function() { return this; });\n          }\n        }\n      }\n\n      // have the event emitter methods call through\n      instance.onSync && instance.onSync.andCallThrough();\n      instance.onAsync && instance.onAsync.andCallThrough();\n      instance.off && instance.off.andCallThrough();\n      instance.trigger && instance.trigger.andCallThrough();\n\n      instance.constructor = Constructor;\n\n      return instance;\n    }\n  }\n})(this);\n"
  },
  {
    "path": "test/integration/test.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title></title>\n    <script src=\"../../bower_components/jquery/jquery.js\"></script>\n    <script src=\"../../dist/typeahead.bundle.js\"></script>\n\n    <style>\n      .container {\n        width: 800px;\n        margin: 50px auto;\n      }\n\n      .typeahead-wrapper {\n        display: block;\n        margin: 50px 0;\n      }\n\n      .tt-menu {\n        background-color: #fff;\n        border: 1px solid #000;\n      }\n\n      .tt-suggestion.tt-cursor {\n        background-color: #ccc;\n      }\n    </style>\n  </head>\n\n  <body>\n    <div class=\"container\">\n      <form action=\"/where\" method=\"GET\">\n        <div class=\"typeahead-wrapper\">\n          <input id=\"states\" name=\"states\" type=\"text\">\n          <input type=\"submit\">\n        </div>\n      </form>\n    </div>\n\n    <script>\n      var states = new Bloodhound({\n        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('val'),\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          { val: 'Alabama' },\n          { val: 'Alaska' },\n          { val: 'Arizona' },\n          { val: 'Arkansas' },\n          { val: 'California' },\n          { val: 'Colorado' },\n          { val: 'Connecticut' },\n          { val: 'Delaware' },\n          { val: 'Florida' },\n          { val: 'Georgia' },\n          { val: 'Hawaii' },\n          { val: 'Idaho' },\n          { val: 'Illinois' },\n          { val: 'Indiana' },\n          { val: 'Iowa' },\n          { val: 'Kansas' },\n          { val: 'Kentucky' },\n          { val: 'Louisiana' },\n          { val: 'Maine' },\n          { val: 'Maryland' },\n          { val: 'Massachusetts' },\n          { val: 'Michigan' },\n          { val: 'Minnesota' },\n          { val: 'Mississippi' },\n          { val: 'Missouri' },\n          { val: 'Montana' },\n          { val: 'Nebraska' },\n          { val: 'Nevada' },\n          { val: 'New Hampshire' },\n          { val: 'New Jersey' },\n          { val: 'New Mexico' },\n          { val: 'New York' },\n          { val: 'North Carolina' },\n          { val: 'North Dakota' },\n          { val: 'Ohio' },\n          { val: 'Oklahoma' },\n          { val: 'Oregon' },\n          { val: 'Pennsylvania' },\n          { val: 'Rhode Island' },\n          { val: 'South Carolina' },\n          { val: 'South Dakota' },\n          { val: 'Tennessee' },\n          { val: 'Texas' },\n          { val: 'Utah' },\n          { val: 'Vermont' },\n          { val: 'Virginia' },\n          { val: 'Washington' },\n          { val: 'West Virginia' },\n          { val: 'Wisconsin' },\n          { val: 'Wyoming' },\n          { val: 'this is a very long value so deal with it' }\n        ]\n      });\n\n      $('#states').typeahead({\n        highlight: true\n      },\n      {\n        display: 'val',\n        source: states\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/test.js",
    "content": "/* jshint esnext: true, evil: true, sub: true */\n\nvar wd = require('yiewd'),\n    colors = require('colors'),\n    expect = require('chai').expect,\n    _ = require('underscore'),\n    f = require('util').format,\n    env = process.env;\n\nvar browser, caps;\n\nbrowser = (process.env.BROWSER || 'chrome').split(':');\n\ncaps = {\n  name: f('[%s] typeahead.js ui', browser.join(' , ')),\n  browserName: browser[0]\n};\n\nsetIf(caps, 'version', browser[1]);\nsetIf(caps, 'platform', browser[2]);\nsetIf(caps, 'tunnel-identifier', env['TRAVIS_JOB_NUMBER']);\nsetIf(caps, 'build', env['TRAVIS_BUILD_NUMBER']);\nsetIf(caps, 'tags', env['CI'] ? ['CI'] : ['local']);\n\nfunction setIf(obj, key, val) {\n  val && (obj[key] = val);\n}\n\ndescribe('jquery-typeahead.js', function() {\n  var driver, body, input, hint, dropdown, allPassed = true;\n\n  this.timeout(300000);\n\n  before(function(done) {\n    var host = 'ondemand.saucelabs.com', port = 80, username, password;\n\n    if (env['CI']) {\n      host = 'localhost';\n      port = 4445;\n      username = env['SAUCE_USERNAME'];\n      password = env['SAUCE_ACCESS_KEY'];\n    }\n\n    driver = wd.remote(host, port, username, password);\n    driver.configureHttp({\n      timeout: 30000,\n      retries: 5,\n      retryDelay: 200\n    });\n\n    driver.on('status', function(info) {\n      console.log(info.cyan);\n    });\n\n    driver.on('command', function(meth, path, data) {\n      console.log(' > ' + meth.yellow, path.grey, data || '');\n    });\n\n    driver.run(function*() {\n      yield this.init(caps);\n      yield this.get('http://localhost:8888/test/integration/test.html');\n\n      body = yield this.elementByTagName('body');\n      input = yield this.elementById('states');\n      hint = yield this.elementByClassName('tt-hint');\n      dropdown = yield this.elementByClassName('tt-menu');\n\n      done();\n    });\n  });\n\n  afterEach(function(done) {\n    allPassed = allPassed && (this.currentTest.state === 'passed');\n\n    driver.run(function*() {\n      yield body.click();\n      yield this.execute('window.jQuery(\"#states\").typeahead(\"val\", \"\")');\n      done();\n    });\n  });\n\n  after(function(done) {\n    driver.run(function*() {\n      yield this.quit();\n      yield driver.sauceJobStatus(allPassed);\n      done();\n    });\n  });\n\n  describe('on blur', function() {\n    it('should close dropdown', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n        expect(yield dropdown.isDisplayed()).to.equal(true);\n\n        yield body.click();\n        expect(yield dropdown.isDisplayed()).to.equal(false);\n\n        done();\n      });\n    });\n\n    it('should clear hint', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n        expect(yield hint.getValue()).to.equal('michigan');\n\n        yield body.click();\n        expect(yield hint.getValue()).to.equal('');\n\n        done();\n      });\n    });\n  });\n\n  describe('on query change', function() {\n    it('should open dropdown if suggestions', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n\n        expect(yield dropdown.isDisplayed()).to.equal(true);\n\n        done();\n      });\n    });\n\n    it('should close dropdown if no suggestions', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('huh?');\n\n        expect(yield dropdown.isDisplayed()).to.equal(false);\n\n        done();\n      });\n    });\n\n    it('should render suggestions if suggestions', function(done) {\n      driver.run(function*() {\n        var suggestions;\n\n        yield input.click();\n        yield input.type('mi');\n\n        suggestions = yield dropdown.elementsByClassName('tt-suggestion');\n\n        expect(suggestions).to.have.length('4');\n        expect(yield suggestions[0].text()).to.equal('Michigan');\n        expect(yield suggestions[1].text()).to.equal('Minnesota');\n        expect(yield suggestions[2].text()).to.equal('Mississippi');\n        expect(yield suggestions[3].text()).to.equal('Missouri');\n\n        done();\n      });\n    });\n\n    it('should show hint if top suggestion is a match', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n\n        expect(yield hint.getValue()).to.equal('michigan');\n\n        done();\n      });\n    });\n\n    it('should match hint to query', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('NeW    JE');\n\n        expect(yield hint.getValue()).to.equal('NeW    JErsey');\n\n        done();\n      });\n    });\n\n    it('should not show hint if top suggestion is not a match', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('ham');\n\n        expect(yield hint.getValue()).to.equal('');\n\n        done();\n      });\n    });\n\n    it('should not show hint if there is query overflow', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('this    is    a very long    value     so ');\n\n        expect(yield hint.getValue()).to.equal('');\n\n        done();\n      });\n    });\n  });\n\n  describe('on up arrow', function() {\n    it('should cycle through suggestions', function(done) {\n      driver.run(function*() {\n        var suggestions;\n\n        yield input.click();\n        yield input.type('mi');\n\n        suggestions = yield dropdown.elementsByClassName('tt-suggestion');\n\n        yield input.type(wd.SPECIAL_KEYS['Up arrow']);\n        expect(yield input.getValue()).to.equal('Missouri');\n        expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Up arrow']);\n        expect(yield input.getValue()).to.equal('Mississippi');\n        expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Up arrow']);\n        expect(yield input.getValue()).to.equal('Minnesota');\n        expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Up arrow']);\n        expect(yield input.getValue()).to.equal('Michigan');\n        expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Up arrow']);\n        expect(yield input.getValue()).to.equal('mi');\n        expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n        expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n        expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n        expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n\n        done();\n      });\n    });\n  });\n\n  describe('on down arrow', function() {\n    it('should cycle through suggestions', function(done) {\n      driver.run(function*() {\n        var suggestions;\n\n        yield input.click();\n        yield input.type('mi');\n\n        suggestions = yield dropdown.elementsByClassName('tt-suggestion');\n\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        expect(yield input.getValue()).to.equal('Michigan');\n        expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        expect(yield input.getValue()).to.equal('Minnesota');\n        expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        expect(yield input.getValue()).to.equal('Mississippi');\n        expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        expect(yield input.getValue()).to.equal('Missouri');\n        expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor');\n\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        expect(yield input.getValue()).to.equal('mi');\n        expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n        expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n        expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n        expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable');\n\n        done();\n      });\n    });\n  });\n\n  describe('on escape', function() {\n    it('should close dropdown', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n        expect(yield dropdown.isDisplayed()).to.equal(true);\n\n        yield input.type(wd.SPECIAL_KEYS['Escape']);\n        expect(yield dropdown.isDisplayed()).to.equal(false);\n\n        done();\n      });\n    });\n\n    it('should clear hint', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n        expect(yield hint.getValue()).to.equal('michigan');\n\n        yield input.type(wd.SPECIAL_KEYS['Escape']);\n        expect(yield hint.getValue()).to.equal('');\n\n        done();\n      });\n    });\n  });\n\n  describe('on tab', function() {\n    it('should autocomplete if hint is present', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n\n        yield input.type(wd.SPECIAL_KEYS['Tab']);\n        expect(yield input.getValue()).to.equal('Michigan');\n\n        done();\n      });\n    });\n\n    it('should select if cursor is on suggestion', function(done) {\n      driver.run(function*() {\n        var suggestions;\n\n        yield input.click();\n        yield input.type('mi');\n\n        suggestions = yield dropdown.elementsByClassName('tt-suggestion');\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        yield input.type(wd.SPECIAL_KEYS['Tab']);\n\n        expect(yield dropdown.isDisplayed()).to.equal(false);\n        expect(yield input.getValue()).to.equal('Minnesota');\n\n        done();\n      });\n    });\n  });\n\n  describe('on right arrow', function() {\n    it('should autocomplete if hint is present', function(done) {\n      driver.run(function*() {\n        yield input.click();\n        yield input.type('mi');\n\n        yield input.type(wd.SPECIAL_KEYS['Right arrow']);\n        expect(yield input.getValue()).to.equal('Michigan');\n\n        done();\n      });\n    });\n  });\n\n  describe('on suggestion click', function() {\n    it('should select suggestion', function(done) {\n      driver.run(function*() {\n        var suggestions;\n\n        yield input.click();\n        yield input.type('mi');\n\n        suggestions = yield dropdown.elementsByClassName('tt-suggestion');\n        yield suggestions[1].click();\n\n        expect(yield dropdown.isDisplayed()).to.equal(false);\n        expect(yield input.getValue()).to.equal('Minnesota');\n\n        done();\n      });\n    });\n  });\n\n  describe('on enter', function() {\n    it('should select if cursor is on suggestion', function(done) {\n      driver.run(function*() {\n        var suggestions;\n\n        yield input.click();\n        yield input.type('mi');\n\n        suggestions = yield dropdown.elementsByClassName('tt-suggestion');\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        yield input.type(wd.SPECIAL_KEYS['Down arrow']);\n        yield input.type(wd.SPECIAL_KEYS['Return']);\n\n        expect(yield dropdown.isDisplayed()).to.equal(false);\n        expect(yield input.getValue()).to.equal('Minnesota');\n\n        done();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/playground.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <script src=\"../bower_components/jquery/jquery.js\"></script>\n    <script src=\"../dist/typeahead.bundle.js\"></script>\n\n    <style>\n      .container {\n        width: 800px;\n        margin: 50px auto;\n      }\n\n      .typeahead-wrapper {\n        display: block;\n        margin: 50px 0;\n      }\n\n      .tt-dropdown-menu {\n        background-color: #fff;\n        border: 1px solid #000;\n      }\n\n      .tt-suggestion.tt-cursor {\n        background-color: #ccc;\n      }\n\n      .triggered-events {\n        float: right;\n        width: 500px;\n        height: 300px;\n      }\n    </style>\n  </head>\n\n  <body>\n    <div class=\"container\">\n      <textarea class=\"triggered-events\"></textarea>\n      <form action=\"/where\" method=\"GET\">\n        <div class=\"typeahead-wrapper\">\n          <input class=\"states\" name=\"states\" type=\"text\" placeholder=\"states\" value=\"Michigan\">\n          <input type=\"submit\">\n        </div>\n      </form>\n      <div class=\"typeahead-wrapper\">\n        <input class=\"bad-tokens\" type=\"text\" placeholder=\"bad tokens\">\n      </div>\n      <div class=\"typeahead-wrapper\">\n        <input class=\"regex-symbols\" type=\"text\" placeholder=\"regex symbols\">\n      </div>\n      <div class=\"typeahead-wrapper\">\n        <input class=\"header-footer\" type=\"text\" placeholder=\"header footer\">\n      </div>\n      <div class=\"typeahead-wrapper\">\n        <input class=\"ltr\" type=\"text\" placeholder=\"ltr\">\n      </div>\n      <div class=\"typeahead-wrapper\">\n        <input class=\"rtl\" type=\"text\" placeholder=\"rtl\">\n      </div>\n      <div class=\"typeahead-wrapper\">\n        <input class=\"mixed\" type=\"text\" placeholder=\"mixed\">\n      </div>\n    </div>\n    </div>\n\n    <script>\n      var states = new Bloodhound({\n        datumTokenizer: Bloodhound.tokenizers.whitespace,\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          'Alabama',\n          'Alaska',\n          'Arizona',\n          'Arkansas',\n          'California',\n          'Colorado',\n          'Connecticut',\n          'Delaware',\n          'Florida',\n          'Georgia',\n          'Hawaii',\n          'Idaho',\n          'Illinois',\n          'Indiana',\n          'Iowa',\n          'Kansas',\n          'Kentucky',\n          'Louisiana',\n          'Maine',\n          'Maryland',\n          'Massachusetts',\n          'Michigan',\n          'Minnesota',\n          'Mississippi',\n          'Missouri',\n          'Montana',\n          'Nebraska',\n          'Nevada',\n          'New Hampshire',\n          'New Jersey',\n          'New Mexico',\n          'New York',\n          'North Carolina',\n          'North Dakota',\n          'Ohio',\n          'Oklahoma',\n          'Oregon',\n          'Pennsylvania',\n          'Rhode Island',\n          'South Carolina',\n          'South Dakota',\n          'Tennessee',\n          'Texas',\n          'Utah',\n          'Vermont',\n          'Virginia',\n          'Washington',\n          'West Virginia',\n          'Wisconsin',\n          'Wyoming'\n        ]\n      });\n\n      states.initialize();\n\n      $('.states').typeahead({\n        highlight: true\n      },\n      {\n        source: states\n      });\n\n\n      var badTokens = new Bloodhound({\n        datumTokenizer: function(d) { return d.tokens; },\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          {\n            value1: 'all bad',\n            jake: '111',\n            tokens: ['  ', ' ', null, undefined, false, 'all', 'bad']\n          },\n          {\n            value1: 'whitespace',\n            jake: '112',\n            tokens: ['  ', ' ', '\\t', '\\n', 'whitespace']\n          },\n          {\n            value1: 'undefined',\n            jake: '113',\n            tokens: [undefined, 'undefined']\n          },\n          {\n            value1: 'null',\n            jake: '114',\n            tokens: [null, 'null']\n          },\n          {\n            value1: 'false',\n            jake: '115',\n            tokens: [false, 'false']\n          }\n        ]\n      });\n\n      badTokens.initialize();\n\n      $('.bad-tokens').typeahead(null, {\n        displayKey: 'value1',\n        source: badTokens\n      });\n\n      var regexSymbols = new Bloodhound({\n        datumTokenizer: function(d) { \n          return Bloodhound.tokenizers.whitespace(d.val); \n        },\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          { val: '*.js' },\n          { val: '[Tt]ypeahead.js' },\n          { val: '^typeahead.js$' },\n          { val: 'typeahead.js(0.8.2)' },\n          { val: 'typeahead.js(@\\\\d.\\\\d.\\\\d)' },\n          { val: 'typeahead.js@0.8.2' }\n        ]\n      });\n\n      regexSymbols.initialize();\n\n      $('.regex-symbols').typeahead(null, {\n        displayKey: 'val',\n        source: regexSymbols\n      });\n\n      var abc = new Bloodhound({\n        datumTokenizer: function(d) { \n          return Bloodhound.tokenizers.whitespace(d.val); \n        },\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          { val: 'a' }, \n          { val: 'ab' }, \n          { val: 'abc' }, \n          { val: 'abcd' }, \n          { val: 'abcde' }\n        ]\n      });\n\n      abc.initialize();\n\n      $('.header-footer').typeahead(null, {\n        displayKey: 'val',\n        source: abc,\n        templates: {\n          header: '<h3>Header</h3>',\n          footer: '<h3>Footer</h3>'\n        }\n      },\n      {\n        displayKey: 'val',\n        source: abc,\n        templates: {\n          header: '<h3>start</h3>',\n          footer: '<h3>end</h3>',\n          empty: '<h3>empty</h3>'\n        }\n      });\n\n      var ltr = new Bloodhound({\n        datumTokenizer: function(d) { \n          return Bloodhound.tokenizers.whitespace(d.val); \n        },\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          { val: \"one\" },\n          { val: \"two three\" },\n          { val: \"four\" },\n          { val: \"five six\" },\n          { val: \"seven\" }\n        ]\n      });\n\n      ltr.initialize();\n\n      $('.ltr').typeahead({\n        highlight: true\n      },\n      {\n        displayKey: 'val',\n        source: ltr\n      });\n\n      var rtl = new Bloodhound({\n        datumTokenizer: function(d) { \n          return Bloodhound.tokenizers.whitespace(d.val); \n        },\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          { val: \"שלום\" },\n          { val: \"ערב טוב\" },\n          { val: \"מה שלומך\" },\n          { val: \"רב תודות\" },\n          { val: \"אין דבר\" }\n        ]\n      });\n\n      rtl.initialize();\n\n      $('.rtl').typeahead({\n        highlight: true\n      },\n      {\n        displayKey: 'val',\n        source: rtl\n      });\n\n      var mixed = new Bloodhound({\n        datumTokenizer: function(d) { \n          return Bloodhound.tokenizers.whitespace(d.val); \n        },\n        queryTokenizer: Bloodhound.tokenizers.whitespace,\n        local: [\n          { val: \"שלום\" },\n          { val: \"ערב טוב\" },\n          { val: \"מה שלומך\" },\n          { val: \"one\" },\n          { val: \"two three\" }\n        ]\n      });\n\n      mixed.initialize();\n\n      $('.mixed').typeahead({\n        highlight: true\n      },\n      {\n        displayKey: 'val',\n        source: mixed\n      });\n\n\n      $('input').on([\n        'typeahead:active',\n        'typeahead:idle',\n        'typeahead:open',\n        'typeahead:close',\n        'typeahead:change',\n        'typeahead:render',\n        'typeahead:select',\n        'typeahead:autocomplete',\n        'typeahead:cursorchange',\n      ].join(' '), logTypeaheadEvent);\n\n      $('form').on('submit', logSubmitEvent);\n      \n      function logSubmitEvent($e) {\n        var text; \n\n        $e && $e.preventDefault(); \n\n        text = JSON.stringify($(this).serializeArray()); \n        writeToTextarea('submit', text);\n      }\n\n      function logTypeaheadEvent($e) {\n        var args, type, text;\n\n        args = [].slice.call(arguments, 1);\n        type = $e.type;\n        text = window.JSON ? JSON.stringify(args) : '';\n\n        writeToTextarea(type, text);\n      }\n\n      function writeToTextarea(/* lines */) {\n        var $textarea, val, text;\n\n        $textarea = $('.triggered-events');\n        val = $textarea.val();\n        text = [].join.call(arguments, '\\n');\n\n        $textarea.val([val, text, '\\n'].join('\\n'));\n        $textarea[0].scrollTop = $textarea[0].scrollHeight;\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/typeahead/dataset_spec.js",
    "content": "describe('Dataset', function() {\n  var www = WWW(), mockSuggestions, mockSuggestionsDisplayFn;\n\n  mockSuggestions = [\n    { value: 'one', raw: { value: 'one' } },\n    { value: 'two', raw: { value: 'two' } },\n    { value: 'html', raw: { value: '<b>html</b>' } }\n  ];\n\n  mockSuggestionsDisplayFn = [\n    { display: '4' },\n    { display: '5' },\n    { display: '6' }\n  ];\n\n  beforeEach(function() {\n    this.dataset = new Dataset({\n      name: 'test',\n      node: $('<div>'),\n      source: this.source = jasmine.createSpy('source')\n    }, www);\n  });\n\n  it('should throw an error if source is missing', function() {\n    expect(noSource).toThrow();\n\n    function noSource() { new Dataset({}, www); }\n  });\n\n  it('should throw an error if the name is not a valid class name', function() {\n    expect(fn).toThrow();\n\n    function fn() {\n      var d = new Dataset({\n        name: 'a space',\n        node: $('<div>'),\n        source: $.noop\n      }, www);\n    }\n  });\n\n  describe('#getRoot', function() {\n    it('should return the root element', function() {\n      var sel = 'div' + www.selectors.dataset + www.selectors.dataset + '-test';\n      expect(this.dataset.$el).toBe(sel);\n    });\n  });\n\n  describe('#update', function() {\n    it('should render suggestions', function() {\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('one');\n      expect(this.dataset.$el).toContainText('two');\n      expect(this.dataset.$el).toContainText('html');\n    });\n\n    it('should escape html chars from display value when using default template', function() {\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('<b>html</b>');\n    });\n\n    it('should respect limit option', function() {\n      this.dataset.limit = 2;\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('one');\n      expect(this.dataset.$el).toContainText('two');\n      expect(this.dataset.$el).not.toContainText('three');\n    });\n\n    it('should allow custom display functions', function() {\n      this.dataset = new Dataset({\n        name: 'test',\n        node: $('<div>'),\n        display: function(o) { return o.display; },\n        source: this.source = jasmine.createSpy('source')\n      }, www);\n\n      this.source.andCallFake(syncMockSuggestionsDisplayFn);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('4');\n      expect(this.dataset.$el).toContainText('5');\n      expect(this.dataset.$el).toContainText('6');\n    });\n\n    it('should ignore async invocations of sync', function() {\n      this.source.andCallFake(asyncSync);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).not.toContainText('one');\n    });\n\n    it('should ignore subesequent invocations of sync', function() {\n      this.source.andCallFake(multipleSync);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el.find('.tt-suggestion')).toHaveLength(3);\n    });\n\n    it('should trigger asyncRequested when needing/expecting backfill', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.async = true;\n      this.dataset.onSync('asyncRequested', spy);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should not trigger asyncRequested when not expecting backfill', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.async = false;\n      this.dataset.onSync('asyncRequested', spy);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n\n    it('should not trigger asyncRequested when not expecting backfill', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.limit = 2;\n      this.dataset.async = true;\n      this.dataset.onSync('asyncRequested', spy);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n\n    it('should trigger asyncCanceled when pending aysnc is canceled', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.async = true;\n      this.dataset.onSync('asyncCanceled', spy);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n      this.dataset.cancel();\n\n      waits(100);\n\n      runs(function() {\n        expect(spy).toHaveBeenCalled();\n      });\n    });\n\n    it('should not trigger asyncCanceled when cancel happens after update', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.async = true;\n      this.dataset.onSync('asyncCanceled', spy);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n\n      waits(100);\n\n      runs(function() {\n        this.dataset.cancel();\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    it('should trigger asyncReceived when aysnc is received', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.async = true;\n      this.dataset.onSync('asyncReceived', spy);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n\n      waits(100);\n\n      runs(function() {\n        expect(spy).toHaveBeenCalled();\n      });\n    });\n\n    it('should not trigger asyncReceived if canceled', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.async = true;\n      this.dataset.onSync('asyncReceived', spy);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n      this.dataset.cancel();\n\n      waits(100);\n\n      runs(function() {\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    it('should not modify sync when async is added', function() {\n      var $test;\n\n      this.dataset.async = true;\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n      $test = this.dataset.$el.find('.tt-suggestion').first();\n      $test.addClass('test');\n\n      waits(100);\n\n      runs(function() {\n        expect($test).toHaveClass('test');\n      });\n    });\n\n    it('should respect limit option in regard to async', function() {\n      this.dataset.async = true;\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n      this.dataset.update('woah');\n\n      waits(100);\n\n      runs(function() {\n        expect(this.dataset.$el.find('.tt-suggestion')).toHaveLength(5);\n      });\n    });\n\n    it('should cancel pending async', function() {\n      var spy1 = jasmine.createSpy(), spy2 = jasmine.createSpy();\n\n      this.dataset.async = true;\n      this.dataset.onSync('asyncCanceled', spy1);\n      this.dataset.onSync('asyncReceived', spy2);\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n\n\n      this.dataset.update('woah');\n      this.dataset.update('woah again');\n\n      waits(100);\n\n      runs(function() {\n        expect(spy1.callCount).toBe(1);\n        expect(spy2.callCount).toBe(1);\n      });\n    });\n\n    it('should render notFound when no suggestions are available', function() {\n      this.dataset = new Dataset({\n        source: this.source,\n        node: $('<div>'),\n        templates: {\n          notFound: '<h2>empty</h2>'\n        }\n      }, www);\n\n      this.source.andCallFake(syncEmptySuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('empty');\n    });\n\n    it('should render pending when no suggestions are available but async is pending', function() {\n      this.dataset = new Dataset({\n        source: this.source,\n        node: $('<div>'),\n        async: true,\n        templates: {\n          pending: '<h2>pending</h2>'\n        }\n      }, www);\n\n      this.source.andCallFake(syncEmptySuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('pending');\n    });\n\n    it('should render header when suggestions are rendered', function() {\n      this.dataset = new Dataset({\n        source: this.source,\n        node: $('<div>'),\n        templates: {\n          header: '<h2>header</h2>'\n        }\n      }, www);\n\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('header');\n    });\n\n    it('should render footer when suggestions are rendered', function() {\n      this.dataset = new Dataset({\n        source: this.source,\n        node: $('<div>'),\n        templates: {\n          footer: function(c) { return '<p>' + c.query + '</p>'; }\n        }\n      }, www);\n\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).toContainText('woah');\n    });\n\n    it('should not render header/footer if there is no content', function() {\n      this.dataset = new Dataset({\n        source: this.source,\n        node: $('<div>'),\n        templates: {\n          header: '<h2>header</h2>',\n          footer: '<h2>footer</h2>'\n        }\n      }, www);\n\n      this.source.andCallFake(syncEmptySuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.$el).not.toContainText('header');\n      expect(this.dataset.$el).not.toContainText('footer');\n    });\n\n    it('should not render stale suggestions', function() {\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n      this.dataset.update('woah');\n\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('nelly');\n\n      waits(100);\n\n      runs(function() {\n        expect(this.dataset.$el).toContainText('one');\n        expect(this.dataset.$el).toContainText('two');\n        expect(this.dataset.$el).toContainText('html');\n        expect(this.dataset.$el).not.toContainText('four');\n        expect(this.dataset.$el).not.toContainText('five');\n      });\n    });\n\n    it('should not render async suggestions if update was canceled', function() {\n      this.source.andCallFake(fakeGetWithAsyncSuggestions);\n      this.dataset.update('woah');\n      this.dataset.cancel();\n\n      waits(100);\n\n      runs(function() {\n        var rendered = this.dataset.$el.find('.tt-suggestion');\n        expect(rendered).toHaveLength(3);\n      });\n    });\n\n    it('should trigger rendered after suggestions are rendered', function() {\n      var spy;\n\n      this.dataset.onSync('rendered', spy = jasmine.createSpy());\n\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      waitsFor(function() { return spy.callCount; });\n    });\n  });\n\n  describe('#clear', function() {\n    it('should clear suggestions', function() {\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      this.dataset.clear();\n      expect(this.dataset.$el).toBeEmpty();\n    });\n\n    it('should cancel pending updates', function() {\n      var spy;\n\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n      spy = spyOn(this.dataset, 'cancel');\n\n      this.dataset.clear();\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should trigger cleared', function() {\n      var spy;\n\n      this.dataset.onSync('cleared', spy = jasmine.createSpy());\n      this.dataset.clear();\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('#isEmpty', function() {\n    it('should return true when empty', function() {\n      expect(this.dataset.isEmpty()).toBe(true);\n    });\n\n    it('should return false when not empty', function() {\n      this.source.andCallFake(syncMockSuggestions);\n      this.dataset.update('woah');\n\n      expect(this.dataset.isEmpty()).toBe(false);\n    });\n  });\n\n  describe('#destroy', function() {\n    it('should set dataset element to dummy element', function() {\n      var $prevEl = this.dataset.$el;\n\n      this.dataset.destroy();\n      expect(this.dataset.$el).not.toBe($prevEl);\n    });\n  });\n\n  // helper functions\n  // ----------------\n\n  function syncEmptySuggestions(q, sync, async) {\n    sync([]);\n  }\n\n  function syncMockSuggestions(q, sync, async) {\n    sync(mockSuggestions);\n  }\n\n  function syncMockSuggestionsDisplayFn(q, sync, async) {\n    sync(mockSuggestionsDisplayFn);\n  }\n\n  function asyncSync(q, sync, async) {\n    setTimeout(function() { sync(mockSuggestions); }, 0);\n  }\n\n  function multipleSync(q, sync, async) {\n    sync(mockSuggestions);\n    sync(mockSuggestions);\n  }\n\n  function fakeGetWithAsyncSuggestions(query, sync, async) {\n    sync(mockSuggestions);\n\n    setTimeout(function() {\n      async([\n        { value: 'four', raw: { value: 'four' } },\n        { value: 'five', raw: { value: 'five' } },\n        { value: 'six', raw: { value: 'six' } },\n        { value: 'seven', raw: { value: 'seven' } },\n        { value: 'eight', raw: { value: 'eight' } },\n      ]);\n    }, 0);\n  }\n});\n"
  },
  {
    "path": "test/typeahead/default_results_spec.js",
    "content": "describe('DefaultMenu', function() {\n  var www = WWW();\n\n  beforeEach(function() {\n    var $fixture;\n\n    jasmine.Dataset.useMock();\n\n    setFixtures('<div id=\"menu-fixture\"></div>');\n\n    $fixture = $('#jasmine-fixtures');\n    this.$node = $fixture.find('#menu-fixture');\n    this.$node.html(fixtures.html.dataset);\n\n    this.view = new DefaultMenu({ node: this.$node, datasets: [{}] }, www).bind();\n    this.dataset = this.view.datasets[0];\n  });\n\n  describe('when rendered is triggered on a dataset', function() {\n    it('should hide menu if empty', function() {\n      this.dataset.isEmpty.andReturn(true);\n\n      this.view._show();\n      this.dataset.trigger('rendered');\n\n      expect(this.$node).not.toBeVisible();\n    });\n\n    it('should not show menu if not open', function() {\n      this.dataset.isEmpty.andReturn(false);\n\n      this.view._hide();\n      this.dataset.trigger('rendered');\n\n      expect(this.$node).not.toBeVisible();\n    });\n\n    it('should show menu if not empty and open', function() {\n      this.dataset.isEmpty.andReturn(false);\n\n      this.view._hide();\n      this.view.open();\n      this.dataset.trigger('rendered');\n\n      expect(this.$node).toBeVisible();\n    });\n  });\n\n  describe('when cleared is triggered on a dataset', function() {\n    it('should hide menu if empty', function() {\n      this.dataset.isEmpty.andReturn(true);\n\n      this.view._show();\n      this.dataset.trigger('cleared');\n\n      expect(this.$node).not.toBeVisible();\n    });\n\n    it('should not show menu if not open', function() {\n      this.dataset.isEmpty.andReturn(false);\n\n      this.view._hide();\n      this.dataset.trigger('cleared');\n\n      expect(this.$node).not.toBeVisible();\n    });\n\n    it('should show menu if not empty and open', function() {\n      this.dataset.isEmpty.andReturn(false);\n\n      this.view._hide();\n      this.view.open();\n      this.dataset.trigger('cleared');\n\n      expect(this.$node).toBeVisible();\n    });\n  });\n\n  describe('#open', function() {\n    it('should show menu if not empty', function() {\n      spyOn(this.view, '_allDatasetsEmpty').andReturn(false);\n      this.view.open();\n\n      expect(this.$node[0].getAttribute('style')).toMatch(/display: block/);\n    });\n\n    it('should not show menu if empty', function() {\n      spyOn(this.view, '_allDatasetsEmpty').andReturn(true);\n      this.view.open();\n\n      expect(this.$node).not.toHaveAttr('style', 'display: block;');\n    });\n  });\n\n  describe('#close', function() {\n    it('should hide menu', function() {\n      this.view._show();\n      this.view.close();\n\n      expect(this.$node).not.toBeVisible();\n    });\n  });\n});\n"
  },
  {
    "path": "test/typeahead/event_bus_spec.js",
    "content": "describe('EventBus', function() {\n\n  beforeEach(function() {\n    var $fixture;\n\n    setFixtures(fixtures.html.input);\n\n    $fixture = $('#jasmine-fixtures');\n    this.$el = $fixture.find('.tt-input');\n\n    this.eventBus = new EventBus({ el: this.$el });\n  });\n\n  it('#trigger should trigger event', function() {\n    var spy = jasmine.createSpy();\n\n    this.$el.on('typeahead:fiz', spy);\n\n    this.eventBus.trigger('fiz');\n    expect(spy).toHaveBeenCalled();\n  });\n\n  it('#before should return false if default was not prevented', function() {\n    var spy = jasmine.createSpy();\n\n    this.$el.on('typeahead:beforefiz', spy);\n\n    expect(this.eventBus.before('fiz')).toBe(false);\n    expect(spy).toHaveBeenCalled();\n  });\n\n  it('#before should return true if default was prevented', function() {\n    var spy = jasmine.createSpy().andCallFake(prevent);\n\n    this.$el.on('typeahead:beforefiz', spy);\n\n    expect(this.eventBus.before('fiz')).toBe(true);\n    expect(spy).toHaveBeenCalled();\n\n    function prevent($e) { $e.preventDefault(); }\n  });\n});\n"
  },
  {
    "path": "test/typeahead/event_emitter_spec.js",
    "content": "describe('EventEmitter', function() {\n\n  beforeEach(function() {\n    this.spy = jasmine.createSpy();\n    this.target = _.mixin({}, EventEmitter);\n  });\n\n  it('methods should be chainable', function() {\n    expect(this.target.onSync()).toEqual(this.target);\n    expect(this.target.onAsync()).toEqual(this.target);\n    expect(this.target.off()).toEqual(this.target);\n    expect(this.target.trigger()).toEqual(this.target);\n  });\n\n  it('#on should take the context a callback should be called in', function() {\n    var context = { val: 3 }, cbContext;\n\n    this.target.onSync('xevent', setCbContext, context).trigger('xevent');\n\n    waitsFor(assertCbContext, 'callback was called in the wrong context');\n\n    function setCbContext() { cbContext = this; }\n    function assertCbContext() { return cbContext === context; }\n  });\n\n  it('#onAsync callbacks should be invoked asynchronously', function() {\n    this.target.onAsync('event', this.spy).trigger('event');\n\n    expect(this.spy.callCount).toBe(0);\n    waitsFor(assertCallCount(this.spy, 1), 'the callback was not invoked');\n  });\n\n  it('#onSync callbacks should be invoked synchronously', function() {\n    this.target.onSync('event', this.spy).trigger('event');\n\n    expect(this.spy.callCount).toBe(1);\n  });\n\n  it('#off should remove callbacks', function() {\n    this.target\n    .onSync('event1 event2', this.spy)\n    .onAsync('event1 event2', this.spy)\n    .off('event1 event2')\n    .trigger('event1 event2');\n\n    waits(100);\n    runs(assertCallCount(this.spy, 0));\n  });\n\n  it('methods should accept multiple event types', function() {\n    this.target\n    .onSync('event1 event2', this.spy)\n    .onAsync('event1 event2', this.spy)\n    .trigger('event1 event2');\n\n    expect(this.spy.callCount).toBe(2);\n    waitsFor(assertCallCount(this.spy, 4), 'the callback was not invoked');\n  });\n\n  it('the event type should be passed to the callback', function() {\n    this.target\n    .onSync('sync', this.spy)\n    .onAsync('async', this.spy)\n    .trigger('sync async');\n\n    waitsFor(assertArgs(this.spy, 0, ['sync']), 'bad args');\n    waitsFor(assertArgs(this.spy, 1, ['async']), 'bad args');\n  });\n\n  it('arbitrary args should be passed to the callback', function() {\n    this.target\n    .onSync('event', this.spy)\n    .onAsync('event', this.spy)\n    .trigger('event', 1, 2);\n\n    waitsFor(assertArgs(this.spy, 0, ['event', 1, 2]), 'bad args');\n    waitsFor(assertArgs(this.spy, 1, ['event', 1, 2]), 'bad args');\n  });\n\n  it('callback execution should be cancellable', function() {\n    var cancelSpy = jasmine.createSpy().andCallFake(cancel);\n\n    this.target\n    .onSync('one', cancelSpy)\n    .onSync('one', this.spy)\n    .onAsync('two', cancelSpy)\n    .onAsync('two', this.spy)\n    .onSync('three', cancelSpy)\n    .onAsync('three', this.spy)\n    .trigger('one two three');\n\n    waitsFor(assertCallCount(cancelSpy, 3));\n    waitsFor(assertCallCount(this.spy, 0));\n\n    function cancel() { return false; }\n  });\n\n  function assertCallCount(spy, expected) {\n    return function() { return spy.callCount === expected; };\n  }\n\n  function assertArgs(spy, call, expected) {\n    return function() {\n      var env = jasmine.getEnv(),\n          actual = spy.calls[call] ? spy.calls[call].args : undefined;\n\n      return env.equals_(actual, expected);\n    };\n  }\n\n});\n"
  },
  {
    "path": "test/typeahead/highlight_spec.js",
    "content": "describe('highlight', function() {\n  it('should allow tagName to be specified', function() {\n    var before = 'abcde',\n        after = 'a<span>bcd</span>e',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'bcd', tagName: 'span' });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should allow className to be specified', function() {\n    var before = 'abcde',\n        after = 'a<strong class=\"one two\">bcd</strong>e',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'bcd', className: 'one two' });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should be case insensitive by default', function() {\n    var before = 'ABCDE',\n        after = 'A<strong>BCD</strong>E',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'bcd' });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should support case sensitivity', function() {\n    var before = 'ABCDE',\n        after = 'ABCDE',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'bcd', caseSensitive: true });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should support words only matching', function() {\n    var before = 'tone one phone',\n        after = 'tone <strong>one</strong> phone',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'one', wordsOnly: true });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should support matching multiple patterns', function() {\n    var before = 'tone one phone',\n        after = '<strong>tone</strong> one <strong>phone</strong>',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: ['tone', 'phone'] });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should support regex chars in the pattern', function() {\n    var before = '*.js when?',\n        after = '<strong>*.</strong>js when<strong>?</strong>',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: ['*.', '?'] });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should work on complex html structures', function() {\n    var before = [\n          '<div>abcde',\n            '<span>abcde</span>',\n            '<div><p>abcde</p></div>',\n          '</div>'\n        ].join(''),\n        after = [\n          '<div><strong>abc</strong>de',\n            '<span><strong>abc</strong>de</span>',\n            '<div><p><strong>abc</strong>de</p></div>',\n          '</div>'\n        ].join(''),\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'abc' });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should ignore html tags and attributes', function() {\n    var before = '<span class=\"class\"></span>',\n        after = '<span class=\"class\"></span>',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: ['span', 'class'] });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should not match across tags', function() {\n    var before = 'a<span>b</span>c',\n        after = 'a<span>b</span>c',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'abc' });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  it('should ignore html comments', function() {\n    var before = '<!-- abc -->',\n        after = '<!-- abc -->',\n        testNode = buildTestNode(before);\n\n    highlight({ node: testNode, pattern: 'abc' });\n    expect(testNode.innerHTML).toEqual(after);\n  });\n\n  function buildTestNode(content) {\n    var node = document.createElement('div');\n    node.innerHTML = content;\n\n    return node;\n  }\n});\n"
  },
  {
    "path": "test/typeahead/input_spec.js",
    "content": "describe('Input', function() {\n  var KEYS, www;\n\n   KEYS = {\n    enter: 13,\n    esc: 27,\n    tab: 9,\n    left: 37,\n    right: 39,\n    up: 38,\n    down: 40,\n    normal: 65 // \"A\" key\n  };\n\n  www = WWW();\n\n  beforeEach(function() {\n    var $fixture;\n\n    setFixtures(fixtures.html.input + fixtures.html.hint);\n\n    $fixture = $('#jasmine-fixtures');\n    this.$input = $fixture.find('.tt-input');\n    this.$hint = $fixture.find('.tt-hint');\n\n    this.view = new Input({ input: this.$input, hint: this.$hint }, www).bind();\n  });\n\n  it('should throw an error if no input is provided', function() {\n    expect(noInput).toThrow();\n\n    function noInput() { new Input({}, www); }\n  });\n\n  describe('when the blur DOM event is triggered', function() {\n    it('should reset the input value', function() {\n      this.view.setQuery('wine');\n      this.view.setInputValue('cheese');\n\n      this.$input.blur();\n\n      expect(this.$input.val()).toBe('wine');\n    });\n\n    it('should trigger blurred', function() {\n      var spy;\n\n      this.view.onSync('blurred', spy = jasmine.createSpy());\n      this.$input.blur();\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when the focus DOM event is triggered', function() {\n    it('should update queryWhenFocused', function() {\n      this.view.setQuery('hi');\n      this.$input.focus();\n      expect(this.view.hasQueryChangedSinceLastFocus()).toBe(false);\n      this.view.setQuery('bye');\n      expect(this.view.hasQueryChangedSinceLastFocus()).toBe(true);\n    });\n\n    it('should trigger focused', function() {\n      var spy;\n\n      this.view.onSync('focused', spy = jasmine.createSpy());\n      this.$input.focus();\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when the keydown DOM event is triggered by tab', function() {\n    it('should trigger tabKeyed if no modifiers were pressed', function() {\n      var spy;\n\n      this.view.onSync('tabKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.tab);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should not trigger tabKeyed if modifiers were pressed', function() {\n      var spy;\n\n      this.view.onSync('tabKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.tab, true);\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('when the keydown DOM event is triggered by esc', function() {\n    it('should trigger escKeyed', function() {\n      var spy;\n\n      this.view.onSync('escKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.esc);\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when the keydown DOM event is triggered by left', function() {\n    it('should trigger leftKeyed', function() {\n      var spy;\n\n      this.view.onSync('leftKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.left);\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when the keydown DOM event is triggered by right', function() {\n    it('should trigger rightKeyed', function() {\n      var spy;\n\n      this.view.onSync('rightKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.right);\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when the keydown DOM event is triggered by enter', function() {\n    it('should trigger enterKeyed', function() {\n      var spy;\n\n      this.view.onSync('enterKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.enter);\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when the keydown DOM event is triggered by up', function() {\n    it('should trigger upKeyed', function() {\n      var spy;\n\n      this.view.onSync('upKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.up);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should prevent default if no modifers were pressed', function() {\n      var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.up);\n\n      expect($e.preventDefault).toHaveBeenCalled();\n    });\n\n    it('should not prevent default if modifers were pressed', function() {\n      var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.up, true);\n\n      expect($e.preventDefault).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('when the keydown DOM event is triggered by down', function() {\n    it('should trigger downKeyed', function() {\n      var spy;\n\n      this.view.onSync('downKeyed', spy = jasmine.createSpy());\n      simulateKeyEvent(this.$input, 'keydown', KEYS.down);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should prevent default if no modifers were pressed', function() {\n      var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.down);\n\n      expect($e.preventDefault).toHaveBeenCalled();\n    });\n\n    it('should not prevent default if modifers were pressed', function() {\n      var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.down, true);\n\n      expect($e.preventDefault).not.toHaveBeenCalled();\n    });\n  });\n\n  // NOTE: have to treat these as async because the ie polyfill acts\n  // in a async manner\n  describe('when the input DOM event is triggered', function() {\n    it('should update query', function() {\n      this.view.setQuery('wine');\n      this.view.setInputValue('cheese');\n\n      simulateInputEvent(this.$input);\n\n      waitsFor(function() { return this.view.getQuery() === 'cheese'; });\n    });\n\n    it('should trigger queryChanged if the query changed', function() {\n      var spy;\n\n      this.view.setQuery('wine');\n      this.view.setInputValue('cheese');\n      this.view.onSync('queryChanged', spy = jasmine.createSpy());\n\n      simulateInputEvent(this.$input);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should trigger whitespaceChanged if whitespace changed', function() {\n      var spy;\n\n      this.view.setQuery('wine  bar');\n      this.view.setInputValue('wine bar');\n      this.view.onSync('whitespaceChanged', spy = jasmine.createSpy());\n\n      simulateInputEvent(this.$input);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should clear hint if invalid', function() {\n      spyOn(this.view, 'clearHintIfInvalid');\n      simulateInputEvent(this.$input);\n      expect(this.view.clearHintIfInvalid).toHaveBeenCalled();\n    });\n\n    it('should check lang direction', function() {\n      var spy;\n\n      this.$input.css('direction', 'rtl');\n      this.view.onSync('langDirChanged', spy = jasmine.createSpy());\n\n      simulateInputEvent(this.$input);\n\n      expect(this.view.dir).toBe('rtl');\n      expect(this.$hint).toHaveAttr('dir', 'rtl');\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('.normalizeQuery', function() {\n    it('should strip leading whitespace', function() {\n      expect(Input.normalizeQuery('  foo')).toBe('foo');\n    });\n\n    it('should condense whitespace', function() {\n      expect(Input.normalizeQuery('foo   bar')).toBe('foo bar');\n    });\n\n    it('should play nice with non-string values', function() {\n      expect(Input.normalizeQuery(2)).toBe('2');\n      expect(Input.normalizeQuery([])).toBe('');\n      expect(Input.normalizeQuery(null)).toBe('');\n      expect(Input.normalizeQuery(undefined)).toBe('');\n      expect(Input.normalizeQuery(false)).toBe('false');\n    });\n  });\n\n  describe('#focus', function() {\n    it('should focus the input', function() {\n      this.$input.blur();\n      this.view.focus();\n\n      expect(this.$input).toBeFocused();\n    });\n  });\n\n  describe('#blur', function() {\n    it('should blur the input', function() {\n      this.$input.focus();\n      this.view.blur();\n\n      expect(this.$input).not.toBeFocused();\n    });\n  });\n\n  describe('#getQuery', function() {\n    it('should act as getter to the query property', function() {\n      this.view.setQuery('mouse');\n      expect(this.view.getQuery()).toBe('mouse');\n    });\n  });\n\n  describe('#setQuery', function() {\n    it('should act as setter to the query property', function() {\n      this.view.setQuery('mouse');\n      expect(this.view.getQuery()).toBe('mouse');\n    });\n\n    it('should update input value', function() {\n      this.view.setQuery('mouse');\n      expect(this.view.getInputValue()).toBe('mouse');\n    });\n\n    it('should trigger queryChanged if the query changed', function() {\n      var spy;\n\n      this.view.setQuery('wine');\n      this.view.onSync('queryChanged', spy = jasmine.createSpy());\n      this.view.setQuery('cheese');\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should trigger whitespaceChanged if whitespace changed', function() {\n      var spy;\n\n      this.view.setQuery('wine   bar');\n      this.view.onSync('whitespaceChanged', spy = jasmine.createSpy());\n      this.view.setQuery('wine bar');\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should clear hint if invalid', function() {\n      spyOn(this.view, 'clearHintIfInvalid');\n      simulateInputEvent(this.$input);\n      expect(this.view.clearHintIfInvalid).toHaveBeenCalled();\n    });\n  });\n\n  describe('#hasQueryChangedSinceLastFocus', function() {\n    it('should return true if the query has changed since focus', function() {\n      this.view.setQuery('hi');\n      this.$input.focus();\n      this.view.setQuery('bye');\n      expect(this.view.hasQueryChangedSinceLastFocus()).toBe(true);\n    });\n\n    it('should return false if the query has not changed since focus', function() {\n      this.view.setQuery('hi');\n      this.$input.focus();\n      expect(this.view.hasQueryChangedSinceLastFocus()).toBe(false);\n    });\n  });\n\n  describe('#getInputValue', function() {\n    it('should act as getter to the input value', function() {\n      this.$input.val('cheese');\n      expect(this.view.getInputValue()).toBe('cheese');\n    });\n  });\n\n  describe('#setInputValue', function() {\n    it('should act as setter to the input value', function() {\n      this.view.setInputValue('cheese');\n      expect(this.view.getInputValue()).toBe('cheese');\n    });\n\n    it('should clear hint if invalid', function() {\n      spyOn(this.view, 'clearHintIfInvalid');\n      this.view.setInputValue('cheese head');\n      expect(this.view.clearHintIfInvalid).toHaveBeenCalled();\n    });\n\n    it('should check lang direction', function() {\n      var spy;\n\n      this.$input.css('direction', 'rtl');\n      this.view.onSync('langDirChanged', spy = jasmine.createSpy());\n\n      simulateInputEvent(this.$input);\n\n      expect(this.view.dir).toBe('rtl');\n      expect(this.$hint).toHaveAttr('dir', 'rtl');\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('#getHint/#setHint', function() {\n    it('should act as getter/setter to value of hint', function() {\n      this.view.setHint('mountain');\n      expect(this.view.getHint()).toBe('mountain');\n    });\n  });\n\n  describe('#resetInputValue', function() {\n    it('should reset input value to last query', function() {\n      this.view.setQuery('cheese');\n      this.view.setInputValue('wine');\n\n      this.view.resetInputValue();\n      expect(this.view.getInputValue()).toBe('cheese');\n    });\n  });\n\n  describe('#clearHint', function() {\n    it('should set the hint value to the empty string', function() {\n      this.view.setHint('cheese');\n      this.view.clearHint();\n\n      expect(this.view.getHint()).toBe('');\n    });\n  });\n\n  describe('#clearHintIfInvalid', function() {\n    it('should clear hint if input value is empty string', function() {\n      this.view.setInputValue('');\n      this.view.setHint('cheese');\n      this.view.clearHintIfInvalid();\n\n      expect(this.view.getHint()).toBe('');\n    });\n\n    it('should clear hint if input value is not prefix of input', function() {\n      this.view.setInputValue('milk');\n      this.view.setHint('cheese');\n      this.view.clearHintIfInvalid();\n\n      expect(this.view.getHint()).toBe('');\n    });\n\n    it('should clear hint if overflow exists', function() {\n      spyOn(this.view, 'hasOverflow').andReturn(true);\n      this.view.setInputValue('che');\n      this.view.setHint('cheese');\n      this.view.clearHintIfInvalid();\n\n      expect(this.view.getHint()).toBe('');\n    });\n\n    it('should not clear hint if input value is prefix of input', function() {\n      this.view.setInputValue('che');\n      this.view.setHint('cheese');\n      this.view.clearHintIfInvalid();\n\n      expect(this.view.getHint()).toBe('cheese');\n    });\n  });\n\n  describe('#hasOverflow', function() {\n    it('should return true if the input has overflow text', function() {\n      var longStr = new Array(1000).join('a');\n\n      this.view.setInputValue(longStr);\n      expect(this.view.hasOverflow()).toBe(true);\n    });\n\n    it('should return false if the input has no overflow text', function() {\n      var shortStr = 'aah';\n\n      this.view.setInputValue(shortStr);\n      expect(this.view.hasOverflow()).toBe(false);\n    });\n  });\n\n  describe('#isCursorAtEnd', function() {\n    it('should return true if the text cursor is at the end', function() {\n      this.view.setInputValue('boo');\n\n      setCursorPosition(this.$input, 3);\n      expect(this.view.isCursorAtEnd()).toBe(true);\n    });\n\n    it('should return false if the text cursor is not at the end', function() {\n      this.view.setInputValue('boo');\n\n      setCursorPosition(this.$input, 1);\n      expect(this.view.isCursorAtEnd()).toBe(false);\n    });\n  });\n\n  describe('#destroy', function() {\n    it('should remove event handlers', function() {\n      var $input, $hint;\n\n      $hint = this.view.$hint;\n      $input = this.view.$input;\n\n      spyOn($hint, 'off');\n      spyOn($input, 'off');\n\n      this.view.destroy();\n\n      expect($hint.off).toHaveBeenCalledWith('.tt');\n      expect($input.off).toHaveBeenCalledWith('.tt');\n    });\n\n    it('should set references to DOM elements to dummy element', function() {\n      var $hint, $input, $overflowHelper;\n\n      $hint = this.view.$hint;\n      $input = this.view.$input;\n      $overflowHelper = this.view.$overflowHelper;\n\n      this.view.destroy();\n\n      expect(this.view.$hint).not.toBe($hint);\n      expect(this.view.$input).not.toBe($input);\n      expect(this.view.$overflowHelper).not.toBe($overflowHelper);\n    });\n  });\n\n  // helper functions\n  // ----------------\n\n  function simulateInputEvent($node) {\n    var $e, type;\n\n    type = _.isMsie() ? 'keypress' : 'input';\n    $e = $.Event(type);\n\n    $node.trigger($e);\n  }\n\n  function simulateKeyEvent($node, type, key, withModifier) {\n    var $e;\n\n    $e = $.Event(type, {\n      keyCode: key,\n      altKey: !!withModifier,\n      ctrlKey: !!withModifier,\n      metaKey: !!withModifier,\n      shiftKey: !!withModifier\n    });\n\n    spyOn($e, 'preventDefault');\n    $node.trigger($e);\n\n    return $e;\n  }\n\n  function setCursorPosition($input, pos) {\n    var input = $input[0], range;\n\n    if (input.setSelectionRange) {\n      input.focus();\n      input.setSelectionRange(pos, pos);\n    }\n\n    else if (input.createTextRange) {\n      range = input.createTextRange();\n      range.collapse(true);\n      range.moveEnd('character', pos);\n      range.moveStart('character', pos);\n      range.select();\n    }\n  }\n});\n"
  },
  {
    "path": "test/typeahead/plugin_spec.js",
    "content": "describe('$plugin', function() {\n\n  beforeEach(function() {\n    var $fixture;\n\n    setFixtures('<input class=\"test-input\" type=\"text\" autocomplete=\"on\">');\n\n    $fixture = $('#jasmine-fixtures');\n    this.$input = $fixture.find('.test-input');\n\n    this.$input.typeahead(null, {\n      displayKey: 'v',\n      source: function(q, sync) {\n        sync([{ v: '1' }, { v: '2' }, { v: '3' }]);\n      }\n    });\n  });\n\n  it('#enable should enable the typaahead', function() {\n    this.$input.typeahead('disable');\n    expect(this.$input.typeahead('isEnabled')).toBe(false);\n\n    this.$input.typeahead('enable');\n    expect(this.$input.typeahead('isEnabled')).toBe(true);\n  });\n\n  it('#disable should disable the typaahead', function() {\n    this.$input.typeahead('enable');\n    expect(this.$input.typeahead('isEnabled')).toBe(true);\n\n    this.$input.typeahead('disable');\n    expect(this.$input.typeahead('isEnabled')).toBe(false);\n  });\n\n  it('#activate should activate the typaahead', function() {\n    this.$input.typeahead('deactivate');\n    expect(this.$input.typeahead('isActive')).toBe(false);\n\n    this.$input.typeahead('activate');\n    expect(this.$input.typeahead('isActive')).toBe(true);\n  });\n\n  it('#activate should fail to activate the typaahead if disabled', function() {\n    this.$input.typeahead('deactivate');\n    expect(this.$input.typeahead('isActive')).toBe(false);\n    this.$input.typeahead('disable');\n\n    this.$input.typeahead('activate');\n    expect(this.$input.typeahead('isActive')).toBe(false);\n  });\n\n  it('#deactivate should deactivate the typaahead', function() {\n    this.$input.typeahead('activate');\n    expect(this.$input.typeahead('isActive')).toBe(true);\n\n    this.$input.typeahead('deactivate');\n    expect(this.$input.typeahead('isActive')).toBe(false);\n  });\n\n  it('#open should open the menu', function() {\n    this.$input.typeahead('close');\n    expect(this.$input.typeahead('isOpen')).toBe(false);\n\n    this.$input.typeahead('open');\n    expect(this.$input.typeahead('isOpen')).toBe(true);\n  });\n\n  it('#close should close the menu', function() {\n    this.$input.typeahead('open');\n    expect(this.$input.typeahead('isOpen')).toBe(true);\n\n    this.$input.typeahead('close');\n    expect(this.$input.typeahead('isOpen')).toBe(false);\n  });\n\n  it('#select should select selectable', function() {\n    var $el;\n\n    // activate and set val to render some selectables\n    this.$input.typeahead('activate');\n    this.$input.typeahead('val', 'o');\n    $el = $('.tt-selectable').first();\n\n    expect(this.$input.typeahead('select', $el)).toBe(true);\n    expect(this.$input.typeahead('val')).toBe('1');\n  });\n\n  it('#select should return false if not valid selectable', function() {\n    var body;\n\n    // activate and set val to render some selectables\n    this.$input.typeahead('activate');\n    this.$input.typeahead('val', 'o');\n    body = document.body;\n\n    expect(this.$input.typeahead('select', body)).toBe(false);\n  });\n\n  it('#autocomplete should autocomplete to selectable', function() {\n    var $el;\n\n    // activate and set val to render some selectables\n    this.$input.typeahead('activate');\n    this.$input.typeahead('val', 'o');\n    $el = $('.tt-selectable').first();\n\n    expect(this.$input.typeahead('autocomplete', $el)).toBe(true);\n    expect(this.$input.typeahead('val')).toBe('1');\n  });\n\n  it('#autocomplete should return false if not valid selectable', function() {\n    var body;\n\n    // activate and set val to render some selectables\n    this.$input.typeahead('activate');\n    this.$input.typeahead('val', 'o');\n    body = document.body;\n\n    expect(this.$input.typeahead('autocomplete', body)).toBe(false);\n  });\n\n  it('#moveCursor should move cursor', function() {\n    var $el;\n\n    // activate and set val to render some selectables\n    this.$input.typeahead('activate');\n    this.$input.typeahead('val', 'o');\n    $el = $('.tt-selectable').first();\n\n    expect($el).not.toHaveClass('tt-cursor');\n    expect(this.$input.typeahead('moveCursor', 1)).toBe(true);\n    expect($el).toHaveClass('tt-cursor');\n  });\n\n  it('#select should return false if not valid selectable', function() {\n    var body;\n\n    // activate and set val to render some selectables\n    this.$input.typeahead('activate');\n    this.$input.typeahead('val', 'o');\n    body = document.body;\n\n    expect(this.$input.typeahead('select', body)).toBe(false);\n  });\n\n  it('#val() should typeahead value of element', function() {\n    var $els;\n\n    this.$input.typeahead('val', 'foo');\n    $els = this.$input.add('<div>');\n\n    expect($els.typeahead('val')).toBe('foo');\n  });\n\n  it('#val(q) should set query', function() {\n    this.$input.typeahead('val', 'foo');\n    expect(this.$input.typeahead('val')).toBe('foo');\n  });\n\n  it('#destroy should revert modified attributes', function() {\n    expect(this.$input).toHaveAttr('autocomplete', 'off');\n    expect(this.$input).toHaveAttr('dir');\n    expect(this.$input).toHaveAttr('spellcheck');\n    expect(this.$input).toHaveAttr('style');\n\n    this.$input.typeahead('destroy');\n\n    expect(this.$input).toHaveAttr('autocomplete', 'on');\n    expect(this.$input).not.toHaveAttr('dir');\n    expect(this.$input).not.toHaveAttr('spellcheck');\n    expect(this.$input).not.toHaveAttr('style');\n  });\n\n  it('#destroy should remove data', function() {\n    expect(this.$input.data('tt-www')).toBeTruthy();\n    expect(this.$input.data('tt-attrs')).toBeTruthy();\n    expect(this.$input.data('tt-typeahead')).toBeTruthy();\n\n    this.$input.typeahead('destroy');\n\n    expect(this.$input.data('tt-www')).toBeFalsy();\n    expect(this.$input.data('tt-attrs')).toBeFalsy();\n    expect(this.$input.data('tt-typeahead')).toBeFalsy();\n  });\n\n  it('#destroy should remove add classes', function() {\n    expect(this.$input).toHaveClass('tt-input');\n    this.$input.typeahead('destroy');\n    expect(this.$input).not.toHaveClass('tt-input');\n  });\n\n  it('#destroy should revert DOM changes', function() {\n    expect($('.twitter-typeahead')).toExist();\n    this.$input.typeahead('destroy');\n    expect($('.twitter-typeahead')).not.toExist();\n  });\n});\n"
  },
  {
    "path": "test/typeahead/results_spec.js",
    "content": "describe('Menu', function() {\n  var www = WWW();\n\n  beforeEach(function() {\n    var $fixture;\n\n    jasmine.Dataset.useMock();\n\n    setFixtures('<div id=\"menu-fixture\"></div>');\n\n    $fixture = $('#jasmine-fixtures');\n    this.$node = $fixture.find('#menu-fixture');\n    this.$node.html(fixtures.html.dataset);\n\n    this.view = new Menu({ node: this.$node, datasets: [{}] }, www).bind();\n    this.dataset = this.view.datasets[0];\n  });\n\n  it('should throw an error if node is missing', function() {\n    expect(noNode).toThrow();\n    function noNode() { new Menu({ datasets: [{}] }, www); }\n  });\n\n  describe('when click event is triggered on a selectable', function() {\n    it('should trigger selectableClicked', function() {\n      var spy;\n\n      this.view.onSync('selectableClicked', spy = jasmine.createSpy());\n\n      this.$node.find(www.selectors.selectable).first().click();\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when rendered is triggered on a dataset', function() {\n    it('should add empty class to node if empty', function() {\n      this.dataset.isEmpty.andReturn(true);\n\n      this.$node.removeClass(www.classes.empty);\n      this.dataset.trigger('rendered');\n\n      expect(this.$node).toHaveClass(www.classes.empty);\n    });\n\n    it('should remove empty class from node if not empty', function() {\n      this.dataset.isEmpty.andReturn(false);\n\n      this.$node.addClass(www.classes.empty);\n      this.dataset.trigger('rendered');\n\n      expect(this.$node).not.toHaveClass(www.classes.empty);\n    });\n\n    it('should trigger datasetRendered', function() {\n      var spy;\n\n      this.view.onSync('datasetRendered', spy = jasmine.createSpy());\n      this.dataset.trigger('rendered');\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when cleared is triggered on a dataset', function() {\n    it('should add empty class to node if empty', function() {\n      this.dataset.isEmpty.andReturn(true);\n\n      this.$node.removeClass(www.classes.empty);\n      this.dataset.trigger('cleared');\n\n      expect(this.$node).toHaveClass(www.classes.empty);\n    });\n\n    it('should remove empty class from node if not empty', function() {\n      this.dataset.isEmpty.andReturn(false);\n\n      this.$node.addClass(www.classes.empty);\n      this.dataset.trigger('cleared');\n\n      expect(this.$node).not.toHaveClass(www.classes.empty);\n    });\n\n    it('should trigger datasetCleared', function() {\n      var spy;\n\n      this.view.onSync('datasetCleared', spy = jasmine.createSpy());\n      this.dataset.trigger('cleared');\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when asyncRequested is triggered on a dataset', function() {\n    it('should propagate event', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.onSync('asyncRequested', spy);\n      this.dataset.trigger('asyncRequested');\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when asyncCanceled is triggered on a dataset', function() {\n    it('should propagate event', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.onSync('asyncCanceled', spy);\n      this.dataset.trigger('asyncCanceled');\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('when asyncReceieved is triggered on a dataset', function() {\n    it('should propagate event', function() {\n      var spy = jasmine.createSpy();\n\n      this.dataset.onSync('asyncReceived', spy);\n      this.dataset.trigger('asyncReceived');\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('#open', function() {\n    it('should add open class to node', function() {\n      this.$node.removeClass(www.classes.open);\n      this.view.open();\n\n      expect(this.$node).toHaveClass(www.classes.open);\n    });\n  });\n\n  describe('#close', function() {\n    it('should remove open class to node', function() {\n      this.$node.addClass(www.classes.open);\n      this.view.close();\n\n      expect(this.$node).not.toHaveClass(www.classes.open);\n    });\n\n    it('should remove cursor', function() {\n      var $selectable;\n\n      $selectable = this.view._getSelectables().first();\n      this.view.setCursor($selectable);\n\n      expect($selectable).toHaveClass(www.classes.cursor);\n\n      this.view.close();\n\n      expect($selectable).not.toHaveClass(www.classes.cursor);\n    });\n  });\n\n  describe('#setLanguageDirection', function() {\n    it('should update css for given language direction', function() {\n      this.view.setLanguageDirection('rtl');\n      expect(this.$node).toHaveAttr('dir', 'rtl');\n\n      this.view.setLanguageDirection('ltr');\n      expect(this.$node).toHaveAttr('dir', 'ltr');\n    });\n  });\n\n  describe('#selectableRelativeToCursor', function() {\n    it('should return selectable delta spots away from cursor', function() {\n      var $first, $second;\n\n      $first = this.view._getSelectables().eq(0);\n      $second = this.view._getSelectables().eq(1);\n\n      this.view.setCursor($first);\n      expect(this.view.selectableRelativeToCursor(+1)).toBe($second);\n    });\n\n    it('should support negative deltas', function() {\n      var $first, $second;\n\n      $first = this.view._getSelectables().eq(0);\n      $second = this.view._getSelectables().eq(1);\n\n      this.view.setCursor($second);\n      expect(this.view.selectableRelativeToCursor(-1)).toBe($first);\n    });\n\n    it('should wrap', function() {\n      var $expected, $actual;\n\n      $expected = this.view._getSelectables().eq(-1);\n      $actual = this.view.selectableRelativeToCursor(-1);\n\n      expect($actual).toBe($expected);\n    });\n\n    it('should return null if delta lands on input', function() {\n      var $first;\n\n      $first = this.view._getSelectables().eq(0);\n\n      this.view.setCursor($first);\n      expect(this.view.selectableRelativeToCursor(-1)).toBeNull();\n    });\n  });\n\n  describe('#setCursor', function() {\n    it('should remove cursor if null is passed in', function() {\n      var $selectable;\n\n      $selectable = this.view._getSelectables().eq(0);\n      this.view.setCursor($selectable);\n      expect(this.view.getActiveSelectable()).toBe($selectable);\n\n      this.view.setCursor(null);\n      expect(this.view.getActiveSelectable()).toBeNull();\n    });\n\n    it('should move cursor to passed in selectable', function() {\n      var $selectable;\n\n      $selectable = this.view._getSelectables().eq(0);\n\n      expect(this.view.getActiveSelectable()).toBeNull();\n      this.view.setCursor($selectable);\n      expect(this.view.getActiveSelectable()).toBe($selectable);\n    });\n  });\n\n  describe('#getSelectableData', function() {\n    it('should extract the data from the selectable element', function() {\n      var $selectable, datum;\n\n      $selectable = $('<div>').data({\n        'tt-selectable-display': 'one',\n        'tt-selectable-object': 'two'\n      });\n\n      data = this.view.getSelectableData($selectable);\n\n      expect(data).toEqual({ val: 'one', obj: 'two' });\n    });\n\n    it('should return null if no element is given', function() {\n      expect(this.view.getSelectableData($('notreal'))).toBeNull();\n    });\n  });\n\n  describe('#getActiveSelectable', function() {\n    it('should return the selectable the cursor is on', function() {\n      var $first;\n\n      $first = this.view._getSelectables().eq(0);\n      this.view.setCursor($first);\n\n      expect(this.view.getActiveSelectable()).toBe($first);\n    });\n\n    it('should return null if the cursor is off', function() {\n      expect(this.view.getActiveSelectable()).toBeNull();\n    });\n  });\n\n  describe('#getTopSelectable', function() {\n    it('should return the selectable at the top of the menu', function() {\n      var $first;\n\n      $first = this.view._getSelectables().eq(0);\n      expect(this.view.getTopSelectable()).toBe($first);\n    });\n  });\n\n  describe('#update', function() {\n    it('should invoke update on each dataset if valid update', function() {\n      this.view.update('fiz');\n      expect(this.dataset.update).toHaveBeenCalled();\n    });\n\n    it('should return true when valid update', function() {\n      expect(this.view.update('fiz')).toBe(true);\n    });\n\n    it('should return false when invalid update', function() {\n      this.view.update('fiz');\n      expect(this.view.update('fiz')).toBe(false);\n    });\n  });\n\n  describe('#empty', function() {\n    it('should set query to null', function() {\n      this.view.query = 'fiz';\n      this.view.empty();\n\n      expect(this.view.query).toBeNull();\n    });\n\n    it('should add empty class to node', function() {\n      this.$node.removeClass(www.classes.empty);\n      this.view.empty();\n\n      expect(this.$node).toHaveClass(www.classes.empty);\n    });\n\n    it('should invoke clear on each dataset', function() {\n      this.view.empty();\n      expect(this.dataset.clear).toHaveBeenCalled();\n    });\n  });\n\n  describe('#destroy', function() {\n    it('should remove event handlers', function() {\n      var $node = this.view.$node;\n\n      spyOn($node, 'off');\n      this.view.destroy();\n      expect($node.off).toHaveBeenCalledWith('.tt');\n    });\n\n    it('should destroy its datasets', function() {\n      this.view.destroy();\n      expect(this.dataset.destroy).toHaveBeenCalled();\n    });\n\n    it('should set node element to dummy element', function() {\n      var $node = this.view.$node;\n\n      this.view.destroy();\n      expect(this.view.$node).not.toBe($node);\n    });\n  });\n});\n"
  },
  {
    "path": "test/typeahead/typeahead_spec.js",
    "content": "describe('Typeahead', function() {\n  var www, testData;\n\n  www = WWW();\n\n  beforeEach(function() {\n    var $fixture, $input;\n\n    jasmine.Input.useMock();\n    jasmine.Dataset.useMock();\n    jasmine.Menu.useMock();\n\n    setFixtures('<input type=\"text\">');\n\n    $fixture = $('#jasmine-fixtures');\n    this.$input = $fixture.find('input');\n\n    testData = { val: 'foo bar', obj: 'fiz' };\n\n    this.view = new Typeahead({\n      input: new Input(),\n      menu: new Menu(),\n      eventBus: new EventBus({ el: this.$input })\n    }, www);\n\n    this.input = this.view.input;\n    this.menu = this.view.menu;\n  });\n\n  describe('on selectableClicked', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'selectableClicked';\n      payload = $('<foo>');\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onSelectableClicked');\n        this.menu.trigger(eventName, payload);\n        expect(this.view._onSelectableClicked).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should select the selectable', function() {\n        spyOn(this.view, 'select');\n        this.menu.trigger(eventName, payload);\n        expect(this.view.select).toHaveBeenCalledWith(payload);\n      });\n    });\n  });\n\n  describe('on asyncRequested', function() {\n    var eventName;\n\n    beforeEach(function() {\n      eventName = 'asyncRequested';\n    });\n\n    it('should trigger typeahead:asyncrequest', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:asyncrequest', spy);\n        this.menu.trigger(eventName);\n        expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('on asyncCanceled', function() {\n    var eventName;\n\n    beforeEach(function() {\n      eventName = 'asyncCanceled';\n    });\n\n    it('should trigger typeahead:asynccancel', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:asynccancel', spy);\n        this.menu.trigger(eventName);\n        expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('on asyncReceived', function() {\n    var eventName;\n\n    beforeEach(function() {\n      eventName = 'asyncReceived';\n    });\n\n    it('should trigger typeahead:asyncreceive', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:asyncreceive', spy);\n        this.menu.trigger(eventName);\n        expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('on datasetRendered', function() {\n    var eventName;\n\n    beforeEach(function() {\n      eventName = 'datasetRendered';\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onDatasetRendered');\n        this.menu.trigger(eventName);\n        expect(this.view._onDatasetRendered).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should update the hint', function() {\n        this.input.hasOverflow.andReturn(false);\n        this.menu.getTopSelectable.andReturn($('<fiz>'));\n        this.menu.getSelectableData.andReturn(testData);\n        this.input.getInputValue.andReturn(testData.val.slice(0, 2));\n\n        this.menu.trigger(eventName);\n\n        expect(this.input.setHint).toHaveBeenCalled();\n      });\n\n      it('should trigger typeahead:render', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:render', spy);\n        this.menu.trigger(eventName);\n        expect(spy).toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('on datasetCleared', function() {\n    var eventName;\n\n    beforeEach(function() {\n      eventName = 'datasetCleared';\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onDatasetCleared');\n        this.menu.trigger(eventName);\n        expect(this.view._onDatasetCleared).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should update the hint', function() {\n        this.input.hasOverflow.andReturn(false);\n        this.menu.getTopSelectable.andReturn($('<fiz>'));\n        this.menu.getSelectableData.andReturn(testData);\n        this.input.getInputValue.andReturn(testData.val.slice(0, 2));\n\n        this.menu.trigger(eventName);\n\n        expect(this.input.setHint).toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('on focused', function() {\n    var eventName;\n\n    beforeEach(function() {\n      eventName = 'focused';\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should activate typeahead', function() {\n        this.input.trigger(eventName);\n        expect(this.view.isActive()).toBe(true);\n      });\n\n      it('should open menu', function() {\n        this.input.trigger(eventName);\n        expect(this.menu.open).toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should open menu', function() {\n        this.input.trigger(eventName);\n        expect(this.menu.open).toHaveBeenCalled();\n      });\n\n      it('should update menu for query if minLength met', function() {\n        this.input.getQuery.andReturn('bar');\n        this.input.trigger(eventName);\n        expect(this.menu.update).toHaveBeenCalledWith('bar');\n      });\n\n      it('should not update menu for query if minLength not met', function() {\n        this.view.minLength = 1;\n        this.input.getQuery.andReturn('');\n        this.input.trigger(eventName);\n        expect(this.menu.update).not.toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('on blurred', function() {\n    var eventName;\n\n    beforeEach(function() {\n      eventName = 'blurred';\n    });\n\n    it('should trigger typeahead:change if query changed since focus', function() {\n      var spy = jasmine.createSpy();\n\n      this.input.hasQueryChangedSinceLastFocus.andReturn(true);\n      this.$input.on('typeahead:change', spy);\n\n      this.input.trigger(eventName);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should not trigger typeahead:change if query has not changed since focus', function() {\n      var spy = jasmine.createSpy();\n\n      this.input.hasQueryChangedSinceLastFocus.andReturn(false);\n      this.$input.on('typeahead:change', spy);\n\n      this.input.trigger(eventName);\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should deactivate typeahead', function() {\n        this.input.trigger(eventName);\n        expect(this.view.isActive()).toBe(false);\n      });\n    });\n  });\n\n  describe('on enterKeyed', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'enterKeyed';\n      payload = jasmine.createSpyObj('event', ['preventDefault']);\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onEnterKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onEnterKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is closed', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(false);\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onEnterKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onEnterKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is open', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(true);\n      });\n\n      it('should select selectable if there is an active one', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select');\n        this.menu.getActiveSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.view.select).toHaveBeenCalledWith($el);\n      });\n\n      it('should prevent default if active selectale ', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select').andReturn(true);\n        this.menu.getActiveSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).toHaveBeenCalled();\n      });\n\n      it('should not select selectable if there is no active one', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select');\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.view.select).not.toHaveBeenCalledWith($el);\n      });\n\n      it('should not prevent default if no active selectale', function() {\n        var $el;\n\n        spyOn(this.view, 'select').andReturn(true);\n        $el = $('<bah>');\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).not.toHaveBeenCalled();\n      });\n\n      it('should not prevent default if selection of active selectable fails', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select').andReturn(false);\n        this.menu.getActiveSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).not.toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('on tabKeyed', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'tabKeyed';\n      payload = jasmine.createSpyObj('event', ['preventDefault']);\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onTabKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onTabKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is closed', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(false);\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onTabKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onTabKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is open', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(true);\n      });\n\n      it('should select selectable if there is an active one', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select');\n        this.menu.getActiveSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.view.select).toHaveBeenCalledWith($el);\n      });\n\n      it('should prevent default if active selectale', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select').andReturn(true);\n        this.menu.getActiveSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).toHaveBeenCalled();\n      });\n\n      it('should not select selectable if there is no active one', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select');\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.view.select).not.toHaveBeenCalledWith($el);\n      });\n\n      it('should not prevent default if no active selectale', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select');\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).not.toHaveBeenCalled();\n      });\n\n      it('should not prevent default if selection of active selectable fails', function() {\n        var $el;\n\n        $el = $('<bah>');\n        spyOn(this.view, 'select').andReturn(false);\n        this.menu.getActiveSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).not.toHaveBeenCalled();\n      });\n\n      it('should autocomplete to top suggestion', function() {\n        var $el;\n\n        $el = $('<foo>');\n        spyOn(this.view, 'autocomplete');\n        this.menu.getTopSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.view.autocomplete).toHaveBeenCalledWith($el);\n      });\n\n      it('should prevent default behavior of DOM event if autocompletion succeeds', function() {\n        var $el;\n\n        $el = $('<foo>');\n        spyOn(this.view, 'autocomplete').andReturn(true);\n        this.menu.getTopSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).toHaveBeenCalled();\n      });\n\n      it('should not prevent default behavior of DOM event if autocompletion fails', function() {\n        var $el;\n\n        $el = $('<foo>');\n        spyOn(this.view, 'autocomplete').andReturn(false);\n        this.menu.getTopSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(payload.preventDefault).not.toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('on escKeyed', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'escKeyed';\n      payload = jasmine.createSpyObj('event', ['preventDefault']);\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onEscKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onEscKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should close', function() {\n        spyOn(this.view, 'close');\n        this.input.trigger(eventName, payload);\n        expect(this.view.close).toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('on upKeyed', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'upKeyed';\n      payload = jasmine.createSpyObj('event', ['preventDefault']);\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onUpKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onUpKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n        spyOn(this.view, 'moveCursor');\n      });\n\n      it('should open menu', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.menu.open).toHaveBeenCalled();\n      });\n\n      it('should move cursor -1', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.view.moveCursor).toHaveBeenCalledWith(-1);\n      });\n    });\n  });\n\n  describe('on downKeyed', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'downKeyed';\n      payload = jasmine.createSpyObj('event', ['preventDefault']);\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onDownKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onDownKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n        spyOn(this.view, 'moveCursor');\n      });\n\n      it('should open menu', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.menu.open).toHaveBeenCalled();\n      });\n\n      it('should move cursor +1', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.view.moveCursor).toHaveBeenCalledWith(1);\n      });\n    });\n  });\n\n  describe('on leftKeyed', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'leftKeyed';\n      payload = jasmine.createSpyObj('event', ['preventDefault']);\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onLeftKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onLeftKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is closed', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(false);\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onLeftKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onLeftKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is open', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(true);\n      });\n\n      it('should autocomplete if language is rtl and text cursor is at end', function() {\n        var $el = $('<foo>');\n\n        spyOn(this.view, 'autocomplete');\n        this.view.dir = 'rtl';\n        this.input.isCursorAtEnd.andReturn(true);\n        this.menu.getTopSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.view.autocomplete).toHaveBeenCalledWith($el);\n      });\n    });\n  });\n\n  describe('on rightKeyed', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'rightKeyed';\n      payload = jasmine.createSpyObj('event', ['preventDefault']);\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onRightKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onRightKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is closed', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(false);\n      });\n\n      it('should do nothing', function() {\n        spyOn(this.view, '_onRightKeyed');\n        this.input.trigger(eventName, payload);\n        expect(this.view._onRightKeyed).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active and menu is open', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.menu.isOpen.andReturn(true);\n      });\n\n      it('should autocomplete if language is rtl and text cursor is at end', function() {\n        var $el = $('<foo>');\n\n        spyOn(this.view, 'autocomplete');\n        this.view.dir = 'ltr';\n        this.input.isCursorAtEnd.andReturn(true);\n        this.menu.getTopSelectable.andReturn($el);\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.view.autocomplete).toHaveBeenCalledWith($el);\n      });\n    });\n  });\n\n  describe('on queryChanged', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'queryChanged';\n      payload = '';\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should not open menu', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.menu.open).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n        this.view.open();\n      });\n\n      it('should open menu', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.menu.open).toHaveBeenCalled();\n      });\n\n      it('should empty menu if minLength is not satisfied', function() {\n        this.view.minLength = 100;\n        this.input.trigger(eventName, payload);\n\n        expect(this.menu.empty).toHaveBeenCalled();\n      });\n\n      it('should update menu if minLength is satisfied', function() {\n        this.input.trigger(eventName, 'fiz');\n        expect(this.menu.update).toHaveBeenCalledWith('fiz');\n      });\n    });\n  });\n\n  describe('on whitespaceChanged', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'whitespaceChanged';\n      payload = '';\n    });\n\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should not open menu', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.menu.open).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should open menu', function() {\n        this.input.trigger(eventName, payload);\n        expect(this.menu.open).toHaveBeenCalled();\n      });\n\n      it('should update the hint', function() {\n        this.input.hasFocus.andReturn(true);\n        this.input.hasOverflow.andReturn(false);\n        this.menu.getTopSelectable.andReturn($('<fiz>'));\n        this.menu.getSelectableData.andReturn(testData);\n\n        this.input.getInputValue.andReturn(testData.val.slice(0, 2));\n\n        this.input.trigger(eventName, payload);\n\n        expect(this.input.setHint).toHaveBeenCalledWith(testData.val);\n      });\n    });\n  });\n\n  describe('on langDirChanged', function() {\n    var eventName, payload;\n\n    beforeEach(function() {\n      eventName = 'langDirChanged';\n      payload = 'rtl';\n    });\n\n    it('should set direction of menu if direction changed', function() {\n      this.view.dir = 'ltr';\n\n      this.input.trigger(eventName, payload);\n\n      expect(this.view.dir).toBe(payload);\n      expect(this.menu.setLanguageDirection).toHaveBeenCalled();\n    });\n\n    it('should do nothing if direction did not changed', function() {\n      this.view.dir = payload;\n\n      this.input.trigger(eventName, payload);\n\n      expect(this.view.dir).toBe(payload);\n      expect(this.menu.setLanguageDirection).not.toHaveBeenCalled();\n    });\n  });\n\n  describe('#isActive', function() {\n    it('should return true if active', function() {\n      this.view.activate();\n      expect(this.view.isActive()).toBe(true);\n    });\n\n    it('should return false if active', function() {\n      this.view.deactivate();\n      expect(this.view.isActive()).toBe(false);\n    });\n  });\n\n  describe('#isEnabled', function() {\n    it('should returned enabled status', function() {\n      this.view.enable();\n      expect(this.view.isEnabled()).toBe(true);\n      this.view.disable();\n      expect(this.view.isEnabled()).toBe(false);\n    });\n  });\n\n  describe('#enable', function() {\n    it('should set enabled to true', function() {\n      this.view.enable();\n      expect(this.view.isEnabled()).toBe(true);\n    });\n  });\n\n  describe('#disable', function() {\n    it('should set enabled to false', function() {\n      this.view.disable();\n      expect(this.view.isEnabled()).toBe(false);\n    });\n  });\n\n  describe('#activate', function() {\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should do nothing', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeactive', spy);\n        this.view.activate();\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when idle and disabled', function() {\n      beforeEach(function() {\n        this.view.disable();\n        this.view.activate();\n      });\n\n      it('should do nothing', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeactive', spy);\n        this.view.activate();\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when idle and enabled', function() {\n      beforeEach(function() {\n        this.view.enable();\n        this.view.deactivate();\n      });\n\n      it('should trigger typeahead:beforeactive', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeactive', spy);\n        this.view.activate();\n        expect(spy).toHaveBeenCalled();\n      });\n\n      it('should support cancellation', function() {\n        var spy1, spy2;\n\n        spy1 = jasmine.createSpy().andCallFake(prevent);\n        spy2 = jasmine.createSpy();\n        this.$input.on('typeahead:beforeactive', spy1);\n        this.$input.on('typeahead:active', spy2);\n\n        this.view.activate();\n\n        expect(spy1).toHaveBeenCalled();\n        expect(spy2).not.toHaveBeenCalled();\n      });\n\n      it('should change state to active', function() {\n        expect(this.view.isActive()).toBe(false);\n        this.view.activate();\n        expect(this.view.isActive()).toBe(true);\n      });\n\n      it('should trigger typeahead:active if not canceled', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:active', spy);\n        this.view.activate();\n        expect(spy).toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('#deactivate', function() {\n    describe('when idle', function() {\n      beforeEach(function() {\n        this.view.deactivate();\n      });\n\n      it('should do nothing', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeidle', spy);\n        this.view.deactivate();\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when active', function() {\n      beforeEach(function() {\n        this.view.activate();\n      });\n\n      it('should trigger typeahead:beforeidle', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeidle', spy);\n        this.view.deactivate();\n        expect(spy).toHaveBeenCalled();\n      });\n\n      it('should support cancellation', function() {\n        var spy1, spy2;\n\n        spy1 = jasmine.createSpy().andCallFake(prevent);\n        spy2 = jasmine.createSpy();\n        this.$input.on('typeahead:beforeidle', spy1);\n        this.$input.on('typeahead:idle', spy2);\n\n        this.view.deactivate();\n\n        expect(spy1).toHaveBeenCalled();\n        expect(spy2).not.toHaveBeenCalled();\n      });\n\n      it('should close', function() {\n        spyOn(this.view, 'close');\n        this.view.deactivate();\n        expect(this.view.close).toHaveBeenCalled();\n      });\n\n      it('should change state to idle', function() {\n        expect(this.view.isActive()).toBe(true);\n        this.view.deactivate();\n        expect(this.view.isActive()).toBe(false);\n      });\n\n      it('should trigger typeahead:idle if not canceled', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:idle', spy);\n        this.view.deactivate();\n        expect(spy).toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('#isOpen', function() {\n    it('should return true if open', function() {\n      this.menu.isOpen.andReturn(true);\n      expect(this.view.isOpen()).toBe(true);\n    });\n\n    it('should return false if closed', function() {\n      this.menu.isOpen.andReturn(false);\n      expect(this.view.isOpen()).toBe(false);\n    });\n  });\n\n  describe('#open', function() {\n    describe('when open', function() {\n      beforeEach(function() {\n        spyOn(this.view, 'isOpen').andReturn(true);\n      });\n\n      it('should do nothing', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeopen', spy);\n        this.view.open();\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when closed', function() {\n      beforeEach(function() {\n        spyOn(this.view, 'isOpen').andReturn(false);\n      });\n\n      it('should trigger typeahead:beforeopen', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeopen', spy);\n        this.view.open();\n        expect(spy).toHaveBeenCalled();\n      });\n\n      it('should support cancellation', function() {\n        var spy1, spy2;\n\n        spy1 = jasmine.createSpy().andCallFake(prevent);\n        spy2 = jasmine.createSpy();\n        this.$input.on('typeahead:beforeopen', spy1);\n        this.$input.on('typeahead:open', spy2);\n\n        this.view.open();\n\n        expect(spy1).toHaveBeenCalled();\n        expect(spy2).not.toHaveBeenCalled();\n      });\n\n      it('should open menu', function() {\n        this.view.open();\n        expect(this.menu.open).toHaveBeenCalled();\n      });\n\n      it('should update hint if active', function() {\n        spyOn(this.view, 'isActive').andReturn(true);\n\n        this.input.hasOverflow.andReturn(false);\n        this.menu.getTopSelectable.andReturn($('<fiz>'));\n        this.menu.getSelectableData.andReturn(testData);\n        this.input.getInputValue.andReturn(testData.val.slice(0, 2));\n\n        this.view.open();\n\n        expect(this.input.setHint).toHaveBeenCalled();\n      });\n\n      it('should trigger typeahead:open if not canceled', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:open', spy);\n        this.view.open();\n        expect(spy).toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('#close', function() {\n    describe('when closed', function() {\n      beforeEach(function() {\n        spyOn(this.view, 'isOpen').andReturn(false);\n      });\n\n      it('should do nothing', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeclose', spy);\n        this.view.open();\n        expect(spy).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('when open', function() {\n      beforeEach(function() {\n        spyOn(this.view, 'isOpen').andReturn(true);\n      });\n\n      it('should trigger typeahead:beforeclose', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:beforeclose', spy);\n        this.view.close();\n        expect(spy).toHaveBeenCalled();\n      });\n\n      it('should support cancellation', function() {\n        var spy1, spy2;\n\n        spy1 = jasmine.createSpy().andCallFake(prevent);\n        spy2 = jasmine.createSpy();\n        this.$input.on('typeahead:beforeclose', spy1);\n        this.$input.on('typeahead:close', spy2);\n\n        this.view.close();\n\n        expect(spy1).toHaveBeenCalled();\n        expect(spy2).not.toHaveBeenCalled();\n      });\n\n      it('should close menu', function() {\n        this.view.close();\n        expect(this.menu.close).toHaveBeenCalled();\n      });\n\n      it('should clear hint', function() {\n        this.view.close();\n        expect(this.input.clearHint).toHaveBeenCalled();\n      });\n\n      it('should trigger typeahead:close if not canceled', function() {\n        var spy = jasmine.createSpy();\n\n        this.$input.on('typeahead:close', spy);\n        this.view.close();\n        expect(spy).toHaveBeenCalled();\n      });\n    });\n  });\n\n  describe('#getVal', function() {\n    it('should return the current query', function() {\n      this.input.getQuery.andReturn('woah');\n      expect(this.view.getVal()).toBe('woah');\n    });\n  });\n\n  describe('#setVal', function() {\n    it('should update query', function() {\n      this.input.hasFocus.andReturn(true);\n      this.view.setVal('woah');\n      expect(this.input.setQuery).toHaveBeenCalledWith('woah');\n    });\n  });\n\n\n\n\n\n  describe('#select', function() {\n    it('should do nothing if element is not a selectable', function() {\n      var spy;\n\n      this.menu.getSelectableData.andReturn(null);\n      this.$input.on('typeahead:beforeselect', spy = jasmine.createSpy());\n\n      this.view.select($('<bah>'));\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n\n    it('should trigger typeahead:beforeselect', function() {\n      var spy;\n\n      this.menu.getSelectableData.andReturn(testData);\n      this.$input.on('typeahead:beforeselect', spy = jasmine.createSpy());\n\n      this.view.select($('<bah>'));\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should support cancellation', function() {\n      var spy1, spy2;\n\n      spy1 = jasmine.createSpy().andCallFake(prevent);\n      spy2 = jasmine.createSpy();\n\n      this.menu.getSelectableData.andReturn(testData);\n      this.$input.on('typeahead:beforeselect', spy1).on('typeahead:select', spy2);\n\n      this.view.select($('<bah>'));\n\n      expect(spy1).toHaveBeenCalled();\n      expect(spy2).not.toHaveBeenCalled();\n    });\n\n    it('should update query', function() {\n      this.menu.getSelectableData.andReturn(testData);\n      this.view.select($('<bah>'));\n      expect(this.input.setQuery).toHaveBeenCalledWith(testData.val, true);\n    });\n\n    it('should trigger typeahead:select', function() {\n      var spy;\n\n      this.menu.getSelectableData.andReturn(testData);\n      this.$input.on('typeahead:select', spy = jasmine.createSpy());\n\n      this.view.select($('<bah>'));\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should close', function() {\n      spyOn(this.view, 'close');\n      this.menu.getSelectableData.andReturn(testData);\n\n      this.view.select($('<bah>'));\n\n      expect(this.view.close).toHaveBeenCalled();\n    });\n  });\n\n  describe('#autocomplete', function() {\n    it('should abort if the query matches the top suggestion', function() {\n      var spy;\n\n      this.input.getQuery.andReturn(testData.val);\n      this.menu.getSelectableData.andReturn(testData);\n      this.$input.on('typeahead:beforeautocomplete', spy = jasmine.createSpy());\n\n      this.view.autocomplete($('<bah>'));\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n\n    it('should trigger typeahead:beforeautocomplete', function() {\n      var spy;\n\n      this.menu.getSelectableData.andReturn(testData);\n      this.$input.on('typeahead:beforeautocomplete', spy = jasmine.createSpy());\n\n      this.view.autocomplete($('<bah>'));\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should support cancellation', function() {\n      var spy1, spy2;\n\n      spy1 = jasmine.createSpy().andCallFake(prevent);\n      spy2 = jasmine.createSpy();\n      this.$input.on('typeahead:beforeautocomplete', spy1);\n      this.$input.on('typeahead:autocomplete', spy2);\n      this.menu.getSelectableData.andReturn(testData);\n\n      this.view.autocomplete($('<bah>'));\n\n      expect(spy1).toHaveBeenCalled();\n      expect(spy2).not.toHaveBeenCalled();\n    });\n\n    it('should update the query', function() {\n      this.menu.getSelectableData.andReturn(testData);\n      this.view.autocomplete($('<bah>'));\n      expect(this.input.setQuery).toHaveBeenCalledWith(testData.val);\n    });\n\n    it('should trigger typeahead:autocomplete', function() {\n      var spy;\n\n      this.menu.getSelectableData.andReturn(testData);\n      this.$input.on('typeahead:autocomplete', spy = jasmine.createSpy());\n\n      this.view.autocomplete($('<bah>'));\n\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('#moveCursor', function() {\n    beforeEach(function() {\n      this.input.getQuery.andReturn('foo');\n    });\n\n    it('should move cursor if minLength is not satisfied', function() {\n      var spy = jasmine.createSpy();\n\n      this.view.minLength = 100;\n      this.menu.update.andReturn(true);\n      this.$input.on('typeahead:beforecursorchange', spy);\n\n      this.view.moveCursor(1);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should move cursor if invalid update', function() {\n      var spy = jasmine.createSpy();\n\n      this.menu.update.andReturn(false);\n      this.$input.on('typeahead:beforecursorchange', spy);\n\n      this.view.moveCursor(1);\n\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should not move cursor if valid update', function() {\n      var spy = jasmine.createSpy();\n\n      this.menu.update.andReturn(true);\n      this.$input.on('typeahead:beforecursorchange', spy);\n\n      this.view.moveCursor(1);\n\n      expect(spy).not.toHaveBeenCalled();\n    });\n\n    it('should trigger typeahead:beforecursorchange', function() {\n      var spy = jasmine.createSpy();\n\n      this.$input.on('typeahead:beforecursorchange', spy);\n      this.view.moveCursor(1);\n      expect(spy).toHaveBeenCalled();\n    });\n\n    it('should support cancellation', function() {\n      var spy = jasmine.createSpy().andCallFake(prevent);\n\n      this.$input.on('typeahead:beforecursorchange', spy);\n      this.view.moveCursor(1);\n      expect(this.menu.setCursor).not.toHaveBeenCalled();\n    });\n\n    it('should update the input value if moved to selectable', function() {\n      this.menu.getSelectableData.andReturn(testData);\n      this.view.moveCursor(1);\n      expect(this.input.setInputValue).toHaveBeenCalledWith(testData.val);\n    });\n\n    it('should reset the input value if moved to input', function() {\n      this.view.moveCursor(1);\n      expect(this.input.resetInputValue).toHaveBeenCalled();\n    });\n\n    it('should update the hint', function() {\n      this.input.hasOverflow.andReturn(false);\n      this.menu.getTopSelectable.andReturn($('<fiz>'));\n      this.menu.getSelectableData.andCallFake(fake);\n      this.input.getInputValue.andReturn(testData.val.slice(0, 1));\n\n      this.view.moveCursor(1);\n\n      expect(this.input.setHint).toHaveBeenCalledWith(testData.val);\n\n      function fake($el) {\n        return ($el && $el.prop('tagName') === 'FIZ') ? testData : null;\n      }\n    });\n\n    it('should trigger cursorchange after setting cursor', function() {\n      var spy = jasmine.createSpy();\n\n      this.$input.on('typeahead:cursorchange', spy);\n      this.view.moveCursor(1);\n      expect(spy).toHaveBeenCalled();\n    });\n  });\n\n  describe('#destroy', function() {\n    it('should destroy input', function() {\n      this.view.destroy();\n\n      expect(this.input.destroy).toHaveBeenCalled();\n    });\n\n    it('should destroy menu', function() {\n      this.view.destroy();\n\n      expect(this.menu.destroy).toHaveBeenCalled();\n    });\n  });\n\n  function prevent($e) { $e.preventDefault(); }\n});\n\n"
  },
  {
    "path": "typeahead.js.jquery.json",
    "content": "{\n  \"licenses\": [\n    {\n      \"url\": \"https://github.com/twitter/typeahead.js/blob/master/LICENSE\"\n    }\n  ],\n  \"dependencies\": {\n    \"jquery\": \">=1.7\"\n  },\n  \"docs\": \"https://github.com/twitter/typeahead.js\",\n  \"demo\": \"http://twitter.github.com/typeahead.js/examples\",\n  \"name\": \"typeahead.js\",\n  \"title\": \"typeahead.js\",\n  \"author\": {\n    \"name\": \"Twitter, Inc.\",\n    \"url\": \"https://twitter.com/twitteross\"\n  },\n  \"description\": \"fast and fully-featured autocomplete library\",\n  \"keywords\": [\n    \"typeahead\",\n    \"autocomplete\"\n  ],\n  \"homepage\": \"http://twitter.github.com/typeahead.js\",\n  \"bugs\": \"https://github.com/twitter/typeahead.js/issues\",\n  \"maintainers\": [\n    {\n      \"name\": \"Jake Harding\",\n      \"url\": \"https://twitter.com/JakeHarding\"\n    },\n    {\n      \"name\": \"Tim Trueman\",\n      \"url\": \"https://twitter.com/timtrueman\"\n    },\n    {\n      \"name\": \"Veljko Skarich\",\n      \"url\": \"https://twitter.com/vskarich\"\n    }\n  ],\n  \"version\": \"0.11.1\"\n}"
  }
]