diff --git a/.gitmodules b/.gitmodules
index dce419409..1bc8388c0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,9 +4,6 @@
[submodule "src/thirdparty/path-utils"]
path = src/thirdparty/path-utils
url = https://github.com/jblas/path-utils.git
-[submodule "src/thirdparty/smart-auto-complete"]
- path = src/thirdparty/smart-auto-complete
- url = https://github.com/laktek/jQuery-Smart-Auto-Complete.git
[submodule "src/thirdparty/mustache"]
path = src/thirdparty/mustache
url = https://github.com/janl/mustache.js.git
diff --git a/src/brackets.js b/src/brackets.js
index e00dcef67..0ea802129 100644
--- a/src/brackets.js
+++ b/src/brackets.js
@@ -55,7 +55,7 @@ define(function (require, exports, module) {
require("widgets/bootstrap-modal");
require("widgets/bootstrap-twipsy-mod");
require("thirdparty/path-utils/path-utils.min");
- require("thirdparty/smart-auto-complete/jquery.smart_autocomplete");
+ require("thirdparty/smart-auto-complete-local/jquery.smart_autocomplete");
// Load dependent modules
var Global = require("utils/Global"),
diff --git a/src/thirdparty/smart-auto-complete b/src/thirdparty/smart-auto-complete
deleted file mode 160000
index d5135c04b..000000000
--- a/src/thirdparty/smart-auto-complete
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d5135c04bd34a6a04d3fd3fdb4238293c067e58c
diff --git a/src/thirdparty/smart-auto-complete-local/README.markdown b/src/thirdparty/smart-auto-complete-local/README.markdown
new file mode 100644
index 000000000..b8f7b4570
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/README.markdown
@@ -0,0 +1,179 @@
+Note: this is a modified copy of Smart Auto Complete, forked from
+https://github.com/laktek/jQuery-Smart-Auto-Complete/commit/7c1236d781a6a5af4c5575767dcda0864dd60df5
+
+jQuery Smart Auto Complete plugin
+=================================
+
+## Requirements
+
+jQuery 1.5 or above
+
+## Usage
+
+In the header section of your page, add two script tags referencing to core jQuery and smart autocomplete plugin. It should look similar to code below:
+
+
+
+
+To activate the plugin call `smartAutoComplete` method with options on target jQuery object.
+
+
+
+## Options
+
+### minCharLimit (integer)
+
+**default**: 1
+
+Sets the minimum characters user have to type before invoking the autocomplete
+
+### maxCharLimit (integer)
+
+**default**: unlimited
+
+Sets the maximum character range auto-complete will offer suggestions. Useful in free-form fields.
+
+### maxResults (integer)
+
+**default**: null (means unlimited)
+
+Sets the maximum number of results to return.
+
+### delay (integer)
+
+**default**: 0
+
+Sets the number of miliseconds plugin should wait, before calling the filter function.
+
+### disabled (boolean)
+
+**default**: false
+
+Sets whether autocomplete is disabled on the field.
+
+### forceSelect (boolean)
+
+**default**: false
+
+If set to true, field will be always filled with best matching result, without leaving the custom input.
+Better to enable this option, if you want autocomplete field to behave similar to a HTML select field. (Check Example 2 in the demo)
+
+### typeAhead (boolean)
+
+**default**: false
+
+If set to true, it will offer the best matching result in grey within the field; that can be auto-completed by pressing the right arrow-key or enter.
+This is similar to behaviour in Google Instant Search's query field (Check Example 3 in the demo)
+
+
+### source (string/array)
+
+Defines the list of items to be filtered. You can give a hardcoded array or a string containing a URL, which will return a JSON array, as the source.
+
+**Note**: Your can omit this option or provide a different object, if you are defining your own filter method.
+
+### filter (function)
+
+**parameters available**: term, source
+
+Define a custom function that would return the matching items for the term (this will override the default filtering algorithm)
+Function should return an array or a Deferred object (ajax call)
+
+### resultFormatter (function)
+
+**parameters available**: result
+
+The function you supply here will be called to format the output of an individual result.
+Function should return a string
+
+### resultsContainer (selector)
+
+Define to which element(s) the results should be appended.
+
+### resultElement (selector)
+
+References the result elements collection. It should be a jQuery selector which could capture all result elements within the results container (e.g. li, div.result).
+
+## Events
+
+Following custom events will be available to the element which has `smartAutoComplete` activated. You can bind handlers to these events like other jQuery events and also cancel the default handler by calling `event.preventDefault()`.
+
+*Note:* Make sure you bind the handlers, only after `smartAutoComplete` is activated on the field. Otherwise context data could get overriden.
+
+To learn more about the default behaviour of the events, please refer to the specs at `spec/core/jquery.smart_autocomplete_spec.js`.
+
+### keyIn
+
+**parameters**: query
+
+Fires when user type something in the field
+
+### resultsReady
+
+**parameters**: results
+
+Fires when results are ready (returned from the filter function)
+
+### showResults
+
+**parameters**: results
+
+Fires after results are added to the results container
+
+### noResults
+
+Fires if filter function returned no results
+
+### lostFocus
+
+Fires when autocomplete field loses focus by user clicking outside of the field or focusing on another field. Also, this event is fired when a value is selected
+
+### itemSelect
+
+**paramters**: item
+
+Fires when user selects an item from the result list
+
+### itemFocus
+
+**paramters**: item
+
+Fires when user focuses on an item in results list with mouse or arrow keys
+
+### itemUnfocus
+
+**paramters**: item
+
+Fires when an item in results list looses focus
+
+## Styling
+
+Following classes will be used to reference elements added by jQuery Smart Autocomplete plugin.
+
+**smart_autocomplete_container** - A *ul* element containing the results.
+
+**smart_autocomplete_highlight** - This class will be added to a result element when it's focused.
+
+**smart_autocomplete_type_ahead_field** - If *typeAhead* option is enabled, additional field will be added behind the autocomplete enabled text field.
+
+## Learn More
+
+[Introducing jQuery Smart AutoComplete](http://laktek.com/2011/03/03/introducing-jquery-smart-autocomplete/)
+
+## Demo
+
+[Basic Examples](http://laktek.github.com/jQuery-Smart-Auto-Complete/demo/index.html)
+
+[GitHub Instant Search Example](http://laktek.github.com/jQuery-Smart-Auto-Complete/demo/github_instant)
+
+
+Copyright (c) 2011 Lakshan Perera (laktek.com)
+
+Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
+
diff --git a/src/thirdparty/smart-auto-complete-local/demo/countries.json b/src/thirdparty/smart-auto-complete-local/demo/countries.json
new file mode 100644
index 000000000..38dfc6dbc
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/demo/countries.json
@@ -0,0 +1 @@
+["United States", "Canada", "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and/or Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British lndian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", "Cook Islands", "Costa Rica", "Croatia (Hrvatska)", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "East Timor", "Ecudaor", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "France, Metropolitan", "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard and Mc Donald Islands", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran (Islamic Republic of)", "Iraq", "Ireland", "Israel", "Italy", "Ivory Coast", "Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", "Liechtenstein", "Lithuania", "Luxembourg", "Macau", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfork Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saint Kitts and Nevis", "Saint Lucia", "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia South Sandwich Islands", "Spain", "Sri Lanka", "St. Helena", "St. Pierre and Miquelon", "Sudan", "Suriname", "Svalbarn and Jan Mayen Islands", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", "Taiwan", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States minor outlying islands", "Uruguay", "Uzbekistan", "Vanuatu", "Vatican City State", "Venezuela", "Vietnam", "Virigan Islands (British)", "Virgin Islands (U.S.)", "Wallis and Futuna Islands", "Western Sahara", "Yemen", "Yugoslavia", "Zaire", "Zambia", "Zimbabwe"]
diff --git a/src/thirdparty/smart-auto-complete-local/demo/github_instant.html b/src/thirdparty/smart-auto-complete-local/demo/github_instant.html
new file mode 100644
index 000000000..d1843746a
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/demo/github_instant.html
@@ -0,0 +1,87 @@
+
+
+
Please check the source code of this page to learn how these examples were implemented
+
+
+
+
+
+
+
+
diff --git a/src/thirdparty/smart-auto-complete-local/demo/qs_score.js b/src/thirdparty/smart-auto-complete-local/demo/qs_score.js
new file mode 100644
index 000000000..af29f732b
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/demo/qs_score.js
@@ -0,0 +1,95 @@
+// qs_score - Quicksilver Score
+//
+// A port of the Quicksilver string ranking algorithm
+//
+// "hello world".score("axl") //=> 0.0
+// "hello world".score("ow") //=> 0.6
+// "hello world".score("hello world") //=> 1.0
+//
+// Tested in Firefox 2 and Safari 3
+//
+// The Quicksilver code is available here
+// http://code.google.com/p/blacktree-alchemy/
+// http://blacktree-alchemy.googlecode.com/svn/trunk/Crucible/Code/NSString+BLTRRanking.m
+//
+// The MIT License
+//
+// Copyright (c) 2008 Lachie Cox
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+String.prototype.score = function(abbreviation,offset) {
+ offset = offset || 0 // TODO: I think this is unused... remove
+
+ if(abbreviation.length == 0) return 0.9
+ if(abbreviation.length > this.length) return 0.0
+
+ for (var i = abbreviation.length; i > 0; i--) {
+ var sub_abbreviation = abbreviation.substring(0,i)
+ var index = this.indexOf(sub_abbreviation)
+
+
+ if(index < 0) continue;
+ if(index + abbreviation.length > this.length + offset) continue;
+
+ var next_string = this.substring(index+sub_abbreviation.length)
+ var next_abbreviation = null
+
+ if(i >= abbreviation.length)
+ next_abbreviation = ''
+ else
+ next_abbreviation = abbreviation.substring(i)
+
+ var remaining_score = next_string.score(next_abbreviation,offset+index)
+
+ if (remaining_score > 0) {
+ var score = this.length-next_string.length;
+
+ if(index != 0) {
+ var j = 0;
+
+ var c = this.charCodeAt(index-1)
+ if(c==32 || c == 9) {
+ for(var j=(index-2); j >= 0; j--) {
+ c = this.charCodeAt(j)
+ score -= ((c == 32 || c == 9) ? 1 : 0.15)
+ }
+
+ // XXX maybe not port this heuristic
+ //
+ // } else if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[self characterAtIndex:matchedRange.location]]) {
+ // for (j = matchedRange.location-1; j >= (int) searchRange.location; j--) {
+ // if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[self characterAtIndex:j]])
+ // score--;
+ // else
+ // score -= 0.15;
+ // }
+ } else {
+ score -= index
+ }
+ }
+
+ score += remaining_score * next_string.length
+ score /= this.length;
+ return score
+ }
+ }
+ return 0.0
+}
\ No newline at end of file
diff --git a/src/thirdparty/smart-auto-complete-local/jquery.smart_autocomplete.js b/src/thirdparty/smart-auto-complete-local/jquery.smart_autocomplete.js
new file mode 100644
index 000000000..d47d20f2b
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/jquery.smart_autocomplete.js
@@ -0,0 +1,546 @@
+/**
+ * Smart Auto Complete plugin
+ *
+ * Copyright (c) 2011 Lakshan Perera (laktek.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
+ *
+*/
+
+/*
+ Requirements: jQuery 1.5 or above
+
+ Usage:
+ $(target).smartAutoComplete({options})
+
+ Options:
+ minCharLimit: (integer) minimum characters user have to type before invoking the autocomplete (default: 1)
+ maxCharLimit: (integer) maximum characters user can type while invoking the autocomplete (default: null (unlimited))
+ maxResults: (integer) maximum number of results to return (default: null (unlimited))
+ delay: (integer) delay before autocomplete starts (default: 0)
+ disabled: (boolean) whether autocomplete disabled on the field (default: false)
+ forceSelect: (boolean) If set to true, field will be always filled with best matching result, without leaving the custom input.
+ Better to enable this option, if you want autocomplete field to behave similar to a HTML select field. (Check Example 2 in the demo)
+ (default: false)
+ typeAhead: (boolean) If set to true, it will offer the best matching result in grey within the field; that can be auto-completed by pressing the right arrow-key or enter.
+ This is similar to behaviour in Google Instant Search's query field (Check Example 3 in the demo)
+ (default: false)
+ source: (string/array) you can supply an array with items or a string containing a URL to fetch items for the source
+ this is optional if you prefer to have your own filter method
+ filter: (function) define a custom function that would return matching items to the entered text (this will override the default filtering algorithm)
+ should return an array or a Deferred object (ajax call)
+ parameters available: term, source
+ resultFormatter: (function) the function you supply here will be called to format the output of an individual result.
+ should return a string
+ parameters available: result
+ resultsContainer: (selector) to which element(s) the result should be appended.
+ resultElement: (selector) references to the result elements collection (e.g. li, div.result)
+
+ Events:
+ keyIn: fires when user types into the field (parameters: query)
+ resultsReady: fires when the filter function returns (parameters: results)
+ showResults: fires when results are shown (parameters: results)
+ noResults: fires when filter returns an empty array
+ itemSelect: fires when user selects an item from the result list (paramters: item)
+ itemFocus: fires when user highlights an item with mouse or arrow keys (paramters: item)
+ itemUnfocus: fires when user moves out from an highlighted item (paramters: item)
+ lostFocus: fires when autocomplete field loses focus by user clicking outside of the field or focusing on another field. Also, this event is fired when a value is selected
+
+ })
+*/
+
+(function($){
+ $.fn.smartAutoComplete = function(){
+
+ if(arguments.length < 1){
+ // get the smart autocomplete object of the first element and return
+ var first_element = this[0];
+ return $(first_element).data("smart-autocomplete")
+ }
+
+ var default_filter_matcher = function(term, source, context){
+ var matcher = new RegExp(term.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), "i" );
+
+ return $.grep(source, function(value) {
+ return matcher.test( value );
+ });
+
+ }
+
+ var default_options = {
+ minCharLimit: 1,
+ maxCharLimit: null,
+ maxResults: null,
+ delay: 0,
+ disabled: false,
+ forceSelect: false,
+ typeAhead: false,
+ resultElement: "li",
+ resultFormatter: function(r){ return ("
" + r + "
"); },
+ filter: function(term, source){
+ var context = this;
+ var options = $(context).data("smart-autocomplete");
+
+
+ //when source is an array
+ if($.type(source) === "array") {
+ // directly map
+ var results = default_filter_matcher(term, source, context);
+ return results;
+ }
+ //when source is a string
+ else if($.type(source) === "string"){
+ // treat the string as a URL endpoint
+ // pass the query as 'term'
+
+ return $.Deferred(function(dfd){
+ $.ajax({
+ url: source,
+ data: {"term": term},
+ dataType: "json"
+ }).done( function(data){
+ dfd.resolve( default_filter_matcher(term, data, context) );
+ });
+ }).promise();
+
+ }
+
+ },
+
+ alignResultsContainer: false,
+
+ clearResults: function(){
+ //remove type ahead field
+ var type_ahead_field = $(this.context).prev(".smart_autocomplete_type_ahead_field");
+ $(this.context).css({ background: type_ahead_field.css("background") });
+ type_ahead_field.remove();
+
+ //clear results div
+ $(this.resultsContainer).html("");
+ },
+
+ setCurrentSelectionToContext: function(){
+ if(this.rawResults.length > 0 && this.currentSelection >= 0)
+ $(this.context).val(this.rawResults[(this.currentSelection)]);
+ },
+
+ setItemSelected: function(val){
+ this.itemSelected = val;
+ },
+
+ autocompleteFocused: false,
+
+ setAutocompleteFocused: function(val){
+ this.autocompleteFocused = val;
+ }
+
+ };
+
+ //define the default events
+ $.event.special.keyIn = {
+ setup: function(){ return false; },
+
+ _default: function(ev){
+ var context = ev.target;
+ var options = $(context).data("smart-autocomplete");
+ var source = options.source || null;
+ var filter = options.filter;
+ var maxChars = (options.maxCharLimit > 0 ? options.maxCharLimit : Number.POSITIVE_INFINITY)
+
+ //event specific data
+ var query = ev.smartAutocompleteData.query;
+
+ if(options.disabled || (query.length > maxChars)){
+ $(context).trigger("lostFocus");
+ return false;
+ }
+
+ //set item selected property
+ options.setItemSelected(false);
+
+ //set autocomplete focused
+ options.setAutocompleteFocused(true);
+
+ //call the filter function with delay
+ setTimeout(function(){
+ $.when( filter.apply(options, [query, options.source]) ).done(function( results ){
+ //do the trimming
+ var trimmed_results = (options.maxResults > 0 ? results.splice(0, options.maxResults) : results);
+
+ $(context).trigger("resultsReady", [trimmed_results]);
+ });
+ }, options.delay);
+ }
+ };
+
+ $.event.special.resultsReady = {
+ setup: function(){ return false },
+
+ _default: function(ev){
+ var context = ev.target;
+ var options = $(context).data("smart-autocomplete");
+
+ //event specific data
+ var results = ev.smartAutocompleteData.results;
+
+ //exit if smart complete is disabled
+ if(options.disabled)
+ return false;
+
+ //clear all previous results
+ $(context).smartAutoComplete().clearResults();
+
+ //save the raw results
+ options.rawResults = results;
+
+ //fire the no match event and exit if no matching results
+ if(results.length < 1){
+ $(context).trigger("noResults");
+ return false
+ }
+
+ //call the results formatter function
+ var formatted_results = $.map(results, function(result){
+ return options.resultFormatter.apply(options, [result]);
+ });
+
+ var formatted_results_html = formatted_results.join("");
+
+ //append the results to the container
+ if(options.resultsContainer)
+ $(options.resultsContainer).append(formatted_results_html);
+
+ //trigger results ready event
+ $(context).trigger("showResults", [results]);
+ }
+ };
+
+ $.event.special.showResults = {
+ setup: function(){ return false },
+
+ _default: function(ev){
+ var context = ev.target;
+ var options = $(context).data("smart-autocomplete");
+ var results_container = $(options.resultsContainer);
+
+ //event specific data
+ var raw_results = ev.smartAutocompleteData.results;
+
+ //type ahead
+ if(options.typeAhead && (raw_results[0].substr(0, $(context).val().length) == $(context).val()) ){
+ var suggestion = raw_results[0]; //options.typeAheadExtractor($(context).val(), raw_results[0]);
+
+ //add new typeAhead field
+ $(context).before("");
+
+ $(context).css({
+ position: "relative",
+ zIndex: 2,
+ background: "transparent"
+ });
+
+ var typeAheadField = $(context).prev("input");
+ typeAheadField.css({
+ position: "absolute",
+ zIndex: 1,
+ overflow: "hidden",
+ background: $(context).css("background"),
+ borderColor: "transparent",
+ width: $(context).width(),
+ color: "silver"
+ });
+
+ //trigger item over for first item
+ options.currentSelection = 0;
+ if(results_container)
+ $(context).trigger("itemFocus", results_container.children()[options.currentSelection]);
+ }
+
+ //show the results container after aligning it with the field
+ if(results_container){
+ if(options.alignResultsContainer){
+ results_container.css({
+ position: "absolute",
+ top: function(){ return $(context).offset().top + $(context).height(); },
+ left: function(){ return $(context).offset().left; },
+ width: function(){ return $(context).width(); },
+ zIndex: 1000
+ })
+ }
+ results_container.show();
+ }
+
+ }
+ };
+
+ $.event.special.noResults = {
+ setup: function(){ return false },
+
+ _default: function(ev){
+ var context = ev.target;
+ var options = $(context).data("smart-autocomplete");
+ var result_container = $(options.resultsContainer);
+
+ if(result_container){
+ //clear previous results
+ options.clearResults();
+ }
+
+ }
+ };
+
+ $.event.special.itemSelect = {
+ setup: function(){ return false },
+
+ _default: function(ev){
+ var context = ev.target;
+ var options = $(context).data("smart-autocomplete");
+
+ //event specific data
+ var selected_item = ev.smartAutocompleteData.item;
+
+ //get the text from selected item
+ var selected_value = $(selected_item).text() || $(selected_item).val();
+ //set it as the value of the autocomplete field
+ $(context).val(selected_value);
+
+ //set item selected property
+ options.setItemSelected(true);
+
+ //set number of current chars in field
+ options.originalCharCount = $(context).val().length;
+
+ //trigger lost focus
+ $(context).trigger('lostFocus');
+ }
+ };
+
+ $.event.special.itemFocus = {
+ setup: function(){ return false },
+
+ _default: function(ev){
+
+ //event specific data
+ var item = ev.smartAutocompleteData.item;
+
+ $(item).addClass("smart_autocomplete_highlight");
+ }
+ };
+
+ $.event.special.itemUnfocus = {
+ setup: function(){ return false },
+
+ _default: function(ev){
+
+ //event specific data
+ var item = ev.smartAutocompleteData.item;
+
+ $(item).removeClass("smart_autocomplete_highlight");
+ }
+ }
+
+ $.event.special.lostFocus = {
+ setup: function(){ return false },
+
+ _default: function(ev){
+ var context = ev.target;
+ var options = $(context).data("smart-autocomplete");
+
+ //if force select is selected and no item is selected, clear currently entered text
+ if(options.forceSelect && !options.itemSelected)
+ $(options.context).val("");
+
+ //unset autocomplete focused
+ options.setAutocompleteFocused(false);
+
+ //clear results
+ options.clearResults();
+
+ //hide the results container
+ if(options.resultsContainer)
+ $(options.resultsContainer).hide();
+
+ //set current selection to null
+ options.currentSelection = null;
+ }
+ };
+
+ var passed_options = arguments[0];
+
+ return this.each(function(i) {
+ //set the options
+ var options = $.extend(default_options, $(this).data("smart-autocomplete"), passed_options);
+ //set the context
+ options["context"] = this;
+
+ //if a result container is not defined
+ if($.type(options.resultsContainer) === "undefined" ){
+ //define the default result container if it is already not defined
+ var default_container = $("
");
+ default_container.appendTo("body");
+
+ options.resultsContainer = default_container;
+ options.alignResultsContainer = true;
+ }
+
+ $(this).data("smart-autocomplete", options);
+
+ // bind user events
+ $(this).on("keydown", function(ev){
+ //get the options
+ var options = $(this).data("smart-autocomplete");
+
+ //up arrow
+ if(ev.keyCode === 38){
+
+ if(options.resultsContainer){
+ var current_selection = options.currentSelection || 0;
+ var result_suggestions = $(options.resultsContainer).children();
+
+ if(current_selection > 0) {
+ $(options.context).trigger("itemUnfocus", result_suggestions[current_selection] );
+ current_selection--;
+ } else if((current_selection-1) < 0) {
+ $(options.context).trigger("itemUnfocus", result_suggestions[current_selection] );
+ current_selection = result_suggestions.length-1;
+ }
+
+ options.currentSelection = current_selection;
+
+ $(options.context).trigger("itemFocus", [ result_suggestions[current_selection] ] );
+ }
+ }
+
+ //down arrow
+ else if(ev.keyCode === 40){
+
+ if(options.resultsContainer && options.resultsContainer.is(':visible')){
+ var current_selection = options.currentSelection;
+ var result_suggestions = $(options.resultsContainer).children();
+
+ if(current_selection >= 0)
+ $(options.context).trigger("itemUnfocus", result_suggestions[current_selection] );
+
+ if(isNaN(current_selection) || null == current_selection || (++current_selection >= result_suggestions.length) )
+ current_selection = 0;
+
+ options["currentSelection"] = current_selection;
+
+ $(options.context).trigger("itemFocus", [ result_suggestions[current_selection] ] );
+ }
+ //trigger keyIn event on down key
+ else {
+ $(options.context).trigger("keyIn", [$(this).val()]);
+ }
+
+ }
+
+ //right arrow & enter key
+ else if(ev.keyCode === 39 || ev.keyCode === 13){
+ var type_ahead_field = $(options.context).prev(".smart_autocomplete_type_ahead_field");
+ if(options.resultsContainer && $(options.resultsContainer).is(':visible')){
+ var current_selection = options.currentSelection;
+ var result_suggestions = $(options.resultsContainer).children();
+
+ $(options.context).trigger("itemSelect", [ result_suggestions[current_selection] ] );
+ }
+ else if(options.typeAhead && type_ahead_field.is(":visible"))
+ $(options.context).trigger("itemSelect", [ type_ahead_field ] );
+
+ return false;
+ }
+
+ else if(ev.keyCode !== 255) {
+ var current_char_count = $(options.context).val().length;
+ //check whether the string has modified
+ if(options.originalCharCount == current_char_count)
+ return;
+
+ //check minimum and maximum number of characters are typed
+ if(current_char_count >= options.minCharLimit){
+ $(options.context).trigger("keyIn", [$(this).val()]);
+ }
+ else{
+ if(options.autocompleteFocused){
+ options.currentSelection = null;
+ $(options.context).trigger("lostFocus");
+ }
+ }
+
+ }
+ });
+
+ $(this).focus(function(){
+ //if the field is in a form capture the return key event
+ $(this).closest("form").bind("keydown.block_for_smart_autocomplete", function(ev){
+ var type_ahead_field = $(options.context).prev(".smart_autocomplete_type_ahead_field");
+ if(ev.keyCode === 13){
+ if(options.resultsContainer && $(options.resultsContainer).is(":visible")){
+ var current_selection = options.currentSelection;
+ var result_suggestions = $(options.resultsContainer).children();
+
+ $(options.context).trigger("itemSelect", [ result_suggestions[current_selection] ] );
+ return false;
+ }
+ else if(options.typeAhead && type_ahead_field.is(":visible") ){
+ $(options.context).trigger("itemSelect", [ type_ahead_field ] );
+ return false;
+ }
+ }
+ });
+
+ if(options.forceSelect){
+ $(this).select();
+ }
+ });
+
+ //check for loosing focus on smart complete field and results container
+ //$(this).blur(function(ev){
+ $(document).bind("focusin click", function(ev){
+ if(options.autocompleteFocused){
+ var elemIsParent = $.contains(options.resultsContainer[0], ev.target);
+ if(ev.target == options.resultsContainer[0] || ev.target == options.context || elemIsParent) return
+
+ $(options.context).closest("form").unbind("keydown.block_for_smart_autocomplete");
+ $(options.context).trigger("lostFocus");
+ }
+ });
+
+ //bind events to results container
+ $(options.resultsContainer).delegate(options.resultElement, "mouseenter.smart_autocomplete", function(){
+ var old_selection = options.currentSelection || 0;
+ var result_suggestions = $(options.resultsContainer).children();
+
+ options["currentSelection"] = $(this).prevAll().length;
+
+ if (old_selection != options.currentSelection) {
+ $(options.context).trigger("itemUnfocus", result_suggestions[old_selection]);
+ }
+
+ $(options.context).trigger("itemFocus", [this] );
+
+ });
+
+ $(options.resultsContainer).delegate(options.resultElement, "mouseleave.smart_autocomplete", function(){
+ $(options.context).trigger("itemUnfocus", [this] );
+ });
+
+ $(options.resultsContainer).delegate(options.resultElement, "mousedown.smart_autocomplete", function(){
+ $(options.context).trigger("itemSelect", [this]);
+ return false
+ });
+
+ //bind plugin specific events
+ $(this).bind({
+ keyIn: function(ev, query){ ev.smartAutocompleteData = {"query": query }; },
+ resultsReady: function(ev, results){ ev.smartAutocompleteData = {"results": results }; },
+ showResults: function(ev, results){ ev.smartAutocompleteData = {"results": results } },
+ noResults: function(){},
+ lostFocus: function(){},
+ itemSelect: function(ev, item){ ev.smartAutocompleteData = {"item": item }; },
+ itemFocus: function(ev, item){ ev.smartAutocompleteData = {"item": item }; },
+ itemUnfocus: function(ev, item){ ev.smartAutocompleteData = {"item": item }; }
+ });
+ });
+
+ }
+})(jQuery);
diff --git a/src/thirdparty/smart-auto-complete-local/spec/SpecRunner.html b/src/thirdparty/smart-auto-complete-local/spec/SpecRunner.html
new file mode 100644
index 000000000..60c86fa3e
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/spec/SpecRunner.html
@@ -0,0 +1,30 @@
+
+
+
+ Jasmine Test Runner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/thirdparty/smart-auto-complete-local/spec/core/SpecHelper.js b/src/thirdparty/smart-auto-complete-local/spec/core/SpecHelper.js
new file mode 100644
index 000000000..b75d4f84e
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/spec/core/SpecHelper.js
@@ -0,0 +1,2 @@
+beforeEach(function() {
+});
diff --git a/src/thirdparty/smart-auto-complete-local/spec/core/jquery.smart_autocomplete_spec.js b/src/thirdparty/smart-auto-complete-local/spec/core/jquery.smart_autocomplete_spec.js
new file mode 100644
index 000000000..6575b4128
--- /dev/null
+++ b/src/thirdparty/smart-auto-complete-local/spec/core/jquery.smart_autocomplete_spec.js
@@ -0,0 +1,433 @@
+describe('Smart AutoComplete', function () {
+
+ beforeEach(function () {
+ setFixtures("");
+ });
+
+ describe('initializing with default values', function(){
+
+ var smart_autocomplete_options;
+
+ beforeEach(function(){
+ $("#autoCompleteField").smartAutoComplete({});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+ });
+
+ it('minimum character length should be 1', function () {
+ expect(smart_autocomplete_options.minCharLimit).toEqual(1);
+ });
+
+ it('maximum results should be unlimited', function () {
+ expect(smart_autocomplete_options.maxResults).toEqual(null);
+ });
+
+ it('delay should be 0ms', function () {
+ expect(smart_autocomplete_options.delay).toEqual(0);
+ });
+
+ it('disabled should be false', function () {
+ expect(smart_autocomplete_options.disabled).toEqual(false);
+ });
+
+ it('force select should be false', function () {
+ expect(smart_autocomplete_options.forceSelect).toEqual(false);
+ });
+
+ it('result element should be a li', function () {
+ expect(smart_autocomplete_options.resultElement).toEqual("li");
+ });
+
+ it('type ahead should be a false', function () {
+ expect(smart_autocomplete_options.typeAhead).toEqual(false);
+ });
+
+ });
+
+ describe('overriding default values', function(){
+
+ it('takes hardcoded options', function(){
+ setFixtures("");
+ $("#autoCompleteFieldHardCoded").smartAutoComplete({});
+ smart_autocomplete_options = $("#autoCompleteFieldHardCoded").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.minCharLimit).toEqual(4);
+ });
+
+ it('minimum character limit set to 4', function () {
+ $("#autoCompleteField").smartAutoComplete({minCharLimit: 4});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.minCharLimit).toEqual(4);
+ });
+
+ it('maximum results set to 10', function () {
+ $("#autoCompleteField").smartAutoComplete({maxResults: 10});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.maxResults).toEqual(10);
+ });
+
+ it('delay set to 10ms', function () {
+ $("#autoCompleteField").smartAutoComplete({delay: 10});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.delay).toEqual(10);
+ });
+
+ it('disabled set to true', function () {
+ $("#autoCompleteField").smartAutoComplete({disabled: true});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.disabled).toEqual(true);
+ });
+
+ it('force select set to true', function () {
+ $("#autoCompleteField").smartAutoComplete({forceSelect: true});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.forceSelect).toEqual(true);
+ });
+
+ it('result element set to be a div', function () {
+ $("#autoCompleteField").smartAutoComplete({resultElement: "div"});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.resultElement).toEqual("div");
+ });
+
+ it('type ahead is set to true', function () {
+ $("#autoCompleteField").smartAutoComplete({typeAhead: true});
+ smart_autocomplete_options = $("#autoCompleteField").data("smart-autocomplete");
+
+ expect(smart_autocomplete_options.typeAhead).toEqual(true);
+ });
+
+ });
+
+ describe('keyIn event', function(){
+
+ it("should be fired if the maximum char limit is reached", function(){
+ $("#autoCompleteField").smartAutoComplete({minCharLimit: 2 });
+
+ var output_buffer = "";
+ $("#autoCompleteField").bind('keyIn', function(ev){ output_buffer = "keyin called"; ev.preventDefault(); });
+
+ $("#autoCompleteField").val("test");
+ $("#autoCompleteField").trigger('keyup');
+
+ expect(output_buffer).toEqual("keyin called");
+ });
+
+ it("should not be fired if the maximum char limit is not reached", function(){
+ $("#autoCompleteField").smartAutoComplete({minCharLimit: 4});
+
+ var output_buffer = "";
+ $("#autoCompleteField").bind('keyIn', function(ev){ output_buffer = "keyin called"; ev.preventDefault(); });
+
+ $("#autoCompleteField").val("te");
+ $("#autoCompleteField").trigger('keyup');
+
+ expect(output_buffer).not.toEqual("keyin called");
+ });
+
+ it("performs no action if disabled", function(){
+ var mock_autocomplete_obj = {filter: function(){}, source: 'test', disabled: true};
+ spyOn(mock_autocomplete_obj, 'filter');
+
+ $("#autoCompleteField").smartAutoComplete({});
+ $("#autoCompleteField").data("smart-autocomplete", mock_autocomplete_obj); //replace with the mock
+ $("#autoCompleteField").trigger("keyIn", "t");
+
+ expect(mock_autocomplete_obj.filter).not.toHaveBeenCalledWith('t', 'test');
+ });
+
+ it("should set the item selected property to false", function(){
+ $("#autoCompleteField").smartAutoComplete({});
+ spyOn(window, 'setTimeout').andReturn(true);
+
+ $("#autoCompleteField").trigger('keyIn', "t");
+ expect($("#autoCompleteField").smartAutoComplete().itemSelected).toBeFalsy();
+ });
+
+ it("waits for the miliseconds set as the delay before running the filter", function(){
+ var output_buffer;
+ $("#autoCompleteField").smartAutoComplete({filter: function(q, s){ output_buffer = "received " + q + " & " + s; return [] }, source: "test", delay: 10});
+ $("#autoCompleteField").bind('resultsReady', function(ev){ ev.preventDefault(); });
+ $("#autoCompleteField").trigger("keyIn", "t");
+
+ waits(10); //this is deprecated
+ runs(function(){
+ expect(output_buffer).toEqual("received t & test");
+ });
+ });
+
+ it("if custom filter function is defined, call it with query and source", function(){
+ var output_buffer;
+ $("#autoCompleteField").smartAutoComplete({filter: function(q, s){ output_buffer = "received " + q + " & " + s; return [] }, source: "test"});
+ $("#autoCompleteField").bind('resultsReady', function(ev){ ev.preventDefault(); });
+ $("#autoCompleteField").trigger("keyIn", "t");
+
+ waits(0); //this is deprecated
+ runs(function(){
+ expect(output_buffer).toEqual("received t & test");
+ });
+ });
+
+ it("if custom filter function is not defined, call default filter with query and source", function(){
+ var mock_autocomplete_obj = {filter: function(){}, source: 'test', clearResults: function(){}, setItemSelected: function(){} };
+ spyOn(mock_autocomplete_obj, 'filter').andReturn([]);
+
+ $("#autoCompleteField").smartAutoComplete({});
+ $("#autoCompleteField").data("smart-autocomplete", mock_autocomplete_obj); //replace with the mock
+ $("#autoCompleteField").bind('resultsReady', function(ev){ ev.preventDefault(); });
+ $("#autoCompleteField").trigger("keyIn", "t");
+
+ waits(0); //this is deprecated
+ runs(function(){
+ expect(mock_autocomplete_obj.filter).toHaveBeenCalledWith('t', 'test');
+ });
+ });
+
+ });
+
+ describe('resultsReady event', function(){
+
+ var result_formatter_called = false;
+ var result_formatter_function = function(r){ result_formatter_called = true; return r };
+
+ it("persists the raw results", function(){
+ $("#autoCompleteField").smartAutoComplete({});
+ $("#autoCompleteField").bind('showResults', function(ev){ ev.preventDefault(); });
+ $("#autoCompleteField").trigger('resultsReady', [["a", "b", "c"]]);
+
+ expect($("#autoCompleteField").data("smart-autocomplete").rawResults).toEqual(["a", "b", "c"]);
+
+ });
+
+ it("format the results using the result formatter function", function(){
+ $("#autoCompleteField").smartAutoComplete({resultFormatter: result_formatter_function });
+ $("#autoCompleteField").bind('showResults', function(ev){ ev.preventDefault(); });
+ $("#autoCompleteField").trigger('resultsReady', [["a", "b", "c"]]);
+
+ expect(result_formatter_called).toBeTruthy();
+ });
+
+ it("should call clearResults method", function(){
+
+ var mock_autocomplete_obj = {clearResults: function(){}, resultFormatter: function(){} };
+ spyOn(mock_autocomplete_obj, 'clearResults');
+
+ $("#autoCompleteField").smartAutoComplete({});
+ $("#autoCompleteField").data("smart-autocomplete", mock_autocomplete_obj); //replace with the mock
+ $("#autoCompleteField").trigger('resultsReady', [["a", "b", "c"]]);
+
+ expect(mock_autocomplete_obj.clearResults).toHaveBeenCalled();
+ });
+
+ it("should append the results to given result container", function(){
+ setFixtures("");
+ $("#autoCompleteField").smartAutoComplete({resultFormatter: result_formatter_function, resultsContainer: "#autoCompleteAppendToBlock" });
+ $("#autoCompleteField").bind('showResults', function(ev){ ev.preventDefault(); });
+ $("#autoCompleteField").trigger('resultsReady', [["a", "b", "c"]]);
+
+ expect($("#autoCompleteAppendToBlock")).toHaveHtml("abc");
+ });
+
+ it("fires the show results event", function(){
+ var event_output = "";
+ $("#autoCompleteField").bind('showResults', function(ev){ event_output = "show results"; ev.preventDefault(); });
+
+ $("#autoCompleteField").smartAutoComplete({resultFormatter: result_formatter_function });
+ $("#autoCompleteField").trigger('resultsReady', [["a", "b", "c"]]);
+
+ expect(event_output).toEqual("show results");
+ });
+
+ it("fires the no match event if filter returns empty", function(){
+ var event_output = "";
+ $("#autoCompleteField").bind('noResults', function(ev){ event_output = "no match"; ev.preventDefault(); });
+
+ $("#autoCompleteField").smartAutoComplete({resultFormatter: result_formatter_function });
+ $("#autoCompleteField").trigger('resultsReady', [[]]);
+
+ expect(event_output).toEqual("no match");
+ });
+
+ });
+
+ describe('no results event', function(){
+
+ it("should call clearResults method", function(){
+
+ var mock_autocomplete_obj = {clearResults: function(){}, resultFormatter: function(){} };
+ spyOn(mock_autocomplete_obj, 'clearResults');
+
+ $("#autoCompleteField").smartAutoComplete({});
+ $("#autoCompleteField").data("smart-autocomplete", mock_autocomplete_obj); //replace with the mock
+ $("#autoCompleteField").trigger('noResults');
+
+ expect(mock_autocomplete_obj.clearResults).toHaveBeenCalled();
+ });
+
+ });
+
+ describe('show results event', function(){
+
+ it("should show type ahead field if the type ahead option is enabled", function(){
+ setFixtures("");
+
+ $("#autoCompleteField").smartAutoComplete({ typeAhead: true });
+ $("#autoCompleteField").val("te");
+ $("#autoCompleteField").trigger('showResults', [["test"]]);
+
+ expect($("#autoCompleteField").prev('.smart_autocomplete_type_ahead_field').length).toEqual(1);
+ });
+
+ it("should apply styles to container relative to field", function(){
+ setFixtures("");
+
+ $("#autoCompleteField").smartAutoComplete({ resultsContainer: "#resultsContainer" });
+ $("#autoCompleteField").trigger('showResults', [[]]);
+
+ expect($("#resultsContainer").attr('style')).not.toBeEmpty();
+
+ });
+
+ it("should make result container visible", function(){
+ setFixtures("");
+ $("#autoCompleteField").smartAutoComplete({ resultsContainer: "#resultsContainer" });
+ $("#autoCompleteField").trigger('showResults', [[]]);
+
+ expect($("#resultsContainer")).toBeVisible();
+
+ });
+
+ });
+
+ describe('lost focus event', function(){
+
+ it("should call clearResults method", function(){
+ var mock_autocomplete_obj = {clearResults: function(){}, resultFormatter: function(){} };
+ spyOn(mock_autocomplete_obj, 'clearResults');
+
+ $("#autoCompleteField").smartAutoComplete({});
+ $("#autoCompleteField").data("smart-autocomplete", mock_autocomplete_obj); //replace with the mock
+ $("#autoCompleteField").trigger('noResults');
+
+ expect(mock_autocomplete_obj.clearResults).toHaveBeenCalled();
+ });
+
+ it("should make result container hidden", function(){
+ setFixtures("");
+ $("#autoCompleteField").smartAutoComplete({ resultsContainer: "#resultsContainer" });
+ $("#autoCompleteField").trigger('lostFocus');
+
+ expect($("#resultsContainer")).not.toBeVisible();
+
+ });
+
+ it("fill in the field with best matching value if force select is enabled and no item is selected", function(){
+ $("#autoCompleteField").smartAutoComplete({ resultsContainer: "#resultsContainer", forceSelect: true, rawResults: ['Apple','Banana', 'Orange'], itemSelected: false, currentSelection: 0 });
+ $("#autoCompleteField").trigger('lostFocus');
+
+ expect($("#autoCompleteField")).toHaveValue('');
+ });
+
+ });
+
+ describe('item select event', function(){
+
+ it('should set the text of selected item as the value of field', function(){
+ setFixtures("
I was selected!
");
+ $("#autoCompleteField").smartAutoComplete({});
+
+ $("#autoCompleteField").trigger('itemSelect', [$("#selectedField")]);
+ expect($("#autoCompleteField")).toHaveValue('I was selected!');
+ });
+
+ it("should set the item selected property to true", function(){
+ setFixtures("
I was selected!
");
+ $("#autoCompleteField").smartAutoComplete({});
+
+ $("#autoCompleteField").trigger('itemSelect', [$("#selectedField")]);
+ expect($("#autoCompleteField").smartAutoComplete().itemSelected).toBeTruthy();
+
+ });
+
+ it("should trigger the lost focus event after a value is selected", function(){
+ setFixtures("