rails-jquery-autocomplete 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+
6
+ task :default => [:uglify, :test]
7
+
8
+ Rake::TestTask.new(:test) do |test|
9
+ test.libs << 'lib' << 'test'
10
+ test.pattern = 'test/**/*_test.rb'
11
+ test.verbose = true
12
+ end
13
+
14
+ task :uglify do
15
+ require 'uglifier'
16
+ file_folder = "lib/assets/javascripts"
17
+ File.open("#{file_folder}/autocomplete-rails.js", "w") do |f|
18
+ f << Uglifier.compile(File.read("#{file_folder}/autocomplete-rails-uncompressed.js"))
19
+ end
20
+ end
21
+
@@ -0,0 +1,179 @@
1
+ /*
2
+ * Unobtrusive autocomplete
3
+ *
4
+ * To use it, you just have to include the HTML attribute autocomplete
5
+ * with the autocomplete URL as the value
6
+ *
7
+ * Example:
8
+ * <input type="text" data-autocomplete="/url/to/autocomplete">
9
+ *
10
+ * Optionally, you can use a jQuery selector to specify a field that can
11
+ * be updated with the element id whenever you find a matching value
12
+ *
13
+ * Example:
14
+ * <input type="text" data-autocomplete="/url/to/autocomplete" data-id-element="#id_field">
15
+ */
16
+
17
+ (function(jQuery)
18
+ {
19
+ var self = null;
20
+ jQuery.fn.railsAutocomplete = function() {
21
+ var handler = function() {
22
+ if (!this.railsAutoCompleter) {
23
+ this.railsAutoCompleter = new jQuery.railsAutocomplete(this);
24
+ }
25
+ };
26
+ if (jQuery.fn.on !== undefined) {
27
+ return jQuery(document).on('focus',this.selector,handler);
28
+ }
29
+ else {
30
+ return this.live('focus',handler);
31
+ }
32
+ };
33
+
34
+ jQuery.railsAutocomplete = function (e) {
35
+ var _e = e;
36
+ this.init(_e);
37
+ };
38
+
39
+ jQuery.railsAutocomplete.fn = jQuery.railsAutocomplete.prototype = {
40
+ railsAutocomplete: '0.0.1'
41
+ };
42
+
43
+ jQuery.railsAutocomplete.fn.extend = jQuery.railsAutocomplete.extend = jQuery.extend;
44
+ jQuery.railsAutocomplete.fn.extend({
45
+ init: function(e) {
46
+ e.delimiter = jQuery(e).attr('data-delimiter') || null;
47
+ e.min_length = jQuery(e).attr('min-length') || 2;
48
+ e.append_to = jQuery(e).attr('data-append-to') || null;
49
+ e.autoFocus = jQuery(e).attr('data-auto-focus') || false;
50
+ function split( val ) {
51
+ return val.split( e.delimiter );
52
+ }
53
+ function extractLast( term ) {
54
+ return split( term ).pop().replace(/^\s+/,"");
55
+ }
56
+
57
+ jQuery(e).autocomplete({
58
+ appendTo: e.append_to,
59
+ autoFocus: e.autoFocus,
60
+ delay: jQuery(e).attr('delay') || 0,
61
+ source: function( request, response ) {
62
+ var firedFrom = this.element[0];
63
+ var params = {term: extractLast( request.term )};
64
+ if (jQuery(e).attr('data-autocomplete-fields')) {
65
+ jQuery.each(jQuery.parseJSON(jQuery(e).attr('data-autocomplete-fields')), function(field, selector) {
66
+ params[field] = jQuery(selector).val();
67
+ });
68
+ }
69
+ jQuery.getJSON( jQuery(e).attr('data-autocomplete'), params, function() {
70
+ if(arguments[0].length === 0) {
71
+ arguments[0] = [];
72
+ arguments[0][0] = { id: "", label: "no existing match" };
73
+ }
74
+ jQuery(arguments[0]).each(function(i, el) {
75
+ var obj = {};
76
+ obj[el.id] = el;
77
+ jQuery(e).data(obj);
78
+ });
79
+ response.apply(null, arguments);
80
+ jQuery(firedFrom).trigger('railsAutocomplete.source', arguments);
81
+ });
82
+ },
83
+ change: function( event, ui ) {
84
+ if(!jQuery(this).is('[data-id-element]') ||
85
+ jQuery(jQuery(this).attr('data-id-element')).val() === "") {
86
+ return;
87
+ }
88
+ jQuery(jQuery(this).attr('data-id-element')).val(ui.item ? ui.item.id : "");
89
+
90
+ if (jQuery(this).attr('data-update-elements')) {
91
+ var update_elements = jQuery.parseJSON(jQuery(this).attr("data-update-elements"));
92
+ var data = ui.item ? jQuery(this).data(ui.item.id.toString()) : {};
93
+ if(update_elements && jQuery(update_elements['id']).val() === "") {
94
+ return;
95
+ }
96
+ for (var key in update_elements) {
97
+ var element = jQuery(update_elements[key]);
98
+ if (element.is(':checkbox')) {
99
+ if (data[key] != null) {
100
+ element.prop('checked', data[key]);
101
+ }
102
+ } else {
103
+ element.val(ui.item ? data[key] : "");
104
+ }
105
+ }
106
+ }
107
+ },
108
+ search: function() {
109
+ // custom minLength
110
+ var term = extractLast( this.value );
111
+ if ( term.length < e.min_length ) {
112
+ return false;
113
+ }
114
+ },
115
+ focus: function() {
116
+ // prevent value inserted on focus
117
+ return false;
118
+ },
119
+ select: function( event, ui ) {
120
+ if(ui.item.value.toLowerCase().indexOf('no match') != -1 || ui.item.value.toLowerCase().indexOf('too many results') != -1){
121
+ jQuery(this).trigger('railsAutocomplete.noMatch', ui);
122
+ return false;
123
+ }
124
+ var terms = split( this.value );
125
+ // remove the current input
126
+ terms.pop();
127
+ // add the selected item
128
+ terms.push( ui.item.value );
129
+ // add placeholder to get the comma-and-space at the end
130
+ if (e.delimiter != null) {
131
+ terms.push( "" );
132
+ this.value = terms.join( e.delimiter );
133
+ } else {
134
+ this.value = terms.join("");
135
+ if (jQuery(this).attr('data-id-element')) {
136
+ jQuery(jQuery(this).attr('data-id-element')).val(ui.item.id);
137
+ }
138
+ if (jQuery(this).attr('data-update-elements')) {
139
+ var data = ui.item;
140
+ var new_record = ui.item.value.indexOf('Create New') != -1 ? true : false;
141
+ var update_elements = jQuery.parseJSON(jQuery(this).attr("data-update-elements"));
142
+ for (var key in update_elements) {
143
+ if(jQuery(update_elements[key]).attr("type") === "checkbox"){
144
+ if(data[key] === true || data[key] === 1) {
145
+ jQuery(update_elements[key]).attr("checked","checked");
146
+ }
147
+ else {
148
+ jQuery(update_elements[key]).removeAttr("checked");
149
+ }
150
+ }
151
+ else{
152
+ if((new_record && data[key] && data[key].indexOf('Create New') == -1) || !new_record){
153
+ jQuery(update_elements[key]).val(data[key]);
154
+ }else{
155
+ jQuery(update_elements[key]).val('');
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ var remember_string = this.value;
162
+ jQuery(this).bind('keyup.clearId', function(){
163
+ if(jQuery.trim(jQuery(this).val()) != jQuery.trim(remember_string)){
164
+ jQuery(jQuery(this).attr('data-id-element')).val("");
165
+ jQuery(this).unbind('keyup.clearId');
166
+ }
167
+ });
168
+ jQuery(e).trigger('railsAutocomplete.select', ui);
169
+
170
+ return false;
171
+ }
172
+ });
173
+ }
174
+ });
175
+
176
+ jQuery(document).ready(function(){
177
+ jQuery('input[data-autocomplete]').railsAutocomplete();
178
+ });
179
+ })(jQuery);
@@ -0,0 +1 @@
1
+ !function(t){t.fn.railsAutocomplete=function(){var e=function(){this.railsAutoCompleter||(this.railsAutoCompleter=new t.railsAutocomplete(this))};return void 0!==t.fn.on?t(document).on("focus",this.selector,e):this.live("focus",e)},t.railsAutocomplete=function(t){var e=t;this.init(e)},t.railsAutocomplete.fn=t.railsAutocomplete.prototype={railsAutocomplete:"0.0.1"},t.railsAutocomplete.fn.extend=t.railsAutocomplete.extend=t.extend,t.railsAutocomplete.fn.extend({init:function(e){function a(t){return t.split(e.delimiter)}function i(t){return a(t).pop().replace(/^\s+/,"")}e.delimiter=t(e).attr("data-delimiter")||null,e.min_length=t(e).attr("min-length")||2,e.append_to=t(e).attr("data-append-to")||null,e.autoFocus=t(e).attr("data-auto-focus")||!1,t(e).autocomplete({appendTo:e.append_to,autoFocus:e.autoFocus,delay:t(e).attr("delay")||0,source:function(a,n){var r=this.element[0],o={term:i(a.term)};t(e).attr("data-autocomplete-fields")&&t.each(t.parseJSON(t(e).attr("data-autocomplete-fields")),function(e,a){o[e]=t(a).val()}),t.getJSON(t(e).attr("data-autocomplete"),o,function(){0===arguments[0].length&&(arguments[0]=[],arguments[0][0]={id:"",label:"no existing match"}),t(arguments[0]).each(function(a,i){var n={};n[i.id]=i,t(e).data(n)}),n.apply(null,arguments),t(r).trigger("railsAutocomplete.source",arguments)})},change:function(e,a){if(t(this).is("[data-id-element]")&&""!==t(t(this).attr("data-id-element")).val()&&(t(t(this).attr("data-id-element")).val(a.item?a.item.id:""),t(this).attr("data-update-elements"))){var i=t.parseJSON(t(this).attr("data-update-elements")),n=a.item?t(this).data(a.item.id.toString()):{};if(i&&""===t(i.id).val())return;for(var r in i){var o=t(i[r]);o.is(":checkbox")?null!=n[r]&&o.prop("checked",n[r]):o.val(a.item?n[r]:"")}}},search:function(){var t=i(this.value);return t.length<e.min_length?!1:void 0},focus:function(){return!1},select:function(i,n){if(-1!=n.item.value.toLowerCase().indexOf("no match")||-1!=n.item.value.toLowerCase().indexOf("too many results"))return t(this).trigger("railsAutocomplete.noMatch",n),!1;var r=a(this.value);if(r.pop(),r.push(n.item.value),null!=e.delimiter)r.push(""),this.value=r.join(e.delimiter);else if(this.value=r.join(""),t(this).attr("data-id-element")&&t(t(this).attr("data-id-element")).val(n.item.id),t(this).attr("data-update-elements")){var o=n.item,l=-1!=n.item.value.indexOf("Create New")?!0:!1,u=t.parseJSON(t(this).attr("data-update-elements"));for(var s in u)"checkbox"===t(u[s]).attr("type")?o[s]===!0||1===o[s]?t(u[s]).attr("checked","checked"):t(u[s]).removeAttr("checked"):t(u[s]).val(l&&o[s]&&-1==o[s].indexOf("Create New")||!l?o[s]:"")}var c=this.value;return t(this).bind("keyup.clearId",function(){t.trim(t(this).val())!=t.trim(c)&&(t(t(this).attr("data-id-element")).val(""),t(this).unbind("keyup.clearId"))}),t(e).trigger("railsAutocomplete.select",n),!1}})}}),t(document).ready(function(){t("input[data-autocomplete]").railsAutocomplete()})}(jQuery);
@@ -0,0 +1,6 @@
1
+ Given /^I choose "([^"]*)" in the autocomplete list$/ do |text|
2
+ page.execute_script %Q{ $('input[data-autocomplete]').trigger("focus") }
3
+ page.execute_script %Q{ $('input[data-autocomplete]').trigger("keydown") }
4
+ sleep 1
5
+ page.execute_script %Q{ $('.ui-menu-item a:contains("#{text}")').trigger("mouseenter").trigger("click"); }
6
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Autocomplete
4
+ class InstallGenerator < Rails::Generators::Base
5
+ def install
6
+ # Copy the unobtrusive JS file
7
+ copy_file('autocomplete-rails.js', 'public/javascripts/autocomplete-rails.js')
8
+ end
9
+
10
+ def self.source_root
11
+ File.join(File.dirname(__FILE__), '..', '..', 'assets', 'javascripts')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Autocomplete
4
+ class UncompressedGenerator < Rails::Generators::Base
5
+ def install
6
+ # Copy the unobtrusive JS file
7
+ copy_file('autocomplete-rails-uncompressed.js', 'public/javascripts/autocomplete-rails.js')
8
+ end
9
+
10
+ def self.source_root
11
+ File.join(File.dirname(__FILE__), '..', '..', 'assets', 'javascripts')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails-jquery-autocomplete/form_helper'
2
+ require 'rails-jquery-autocomplete/autocomplete'
3
+
4
+ module RailsJQueryAutocomplete
5
+ autoload :Orm , 'rails-jquery-autocomplete/orm'
6
+ autoload :FormtasticPlugin , 'rails-jquery-autocomplete/formtastic_plugin'
7
+
8
+ unless ::Rails.version < "3.1"
9
+ require 'rails-jquery-autocomplete/rails/engine'
10
+ end
11
+ end
12
+
13
+ class ActionController::Base
14
+ include RailsJQueryAutocomplete::Autocomplete
15
+ end
16
+
17
+ require 'rails-jquery-autocomplete/formtastic'
18
+
19
+ begin
20
+ require 'simple_form'
21
+ require 'rails-jquery-autocomplete/simple_form_plugin'
22
+ rescue LoadError
23
+ end
@@ -0,0 +1,116 @@
1
+ module RailsJQueryAutocomplete
2
+ module Autocomplete
3
+ def self.included(target)
4
+ target.extend RailsJQueryAutocomplete::Autocomplete::ClassMethods
5
+
6
+ target.send :include, RailsJQueryAutocomplete::Orm::Mongoid if defined?(Mongoid::Document)
7
+ target.send :include, RailsJQueryAutocomplete::Orm::MongoMapper if defined?(MongoMapper::Document)
8
+ target.send :include, RailsJQueryAutocomplete::Orm::ActiveRecord
9
+
10
+ end
11
+
12
+ #
13
+ # Usage:
14
+ #
15
+ # class ProductsController < Admin::BaseController
16
+ # autocomplete :brand, :name
17
+ # end
18
+ #
19
+ # This will magically generate an action autocomplete_brand_name, so,
20
+ # don't forget to add it on your routes file
21
+ #
22
+ # resources :products do
23
+ # get :autocomplete_brand_name, :on => :collection
24
+ # end
25
+ #
26
+ # Now, on your view, all you have to do is have a text field like:
27
+ #
28
+ # f.text_field :brand_name, :autocomplete => autocomplete_brand_name_products_path
29
+ #
30
+ #
31
+ # Yajl is used by default to encode results, if you want to use a different encoder
32
+ # you can specify your custom encoder via block
33
+ #
34
+ # class ProductsController < Admin::BaseController
35
+ # autocomplete :brand, :name do |items|
36
+ # CustomJSONEncoder.encode(items)
37
+ # end
38
+ # end
39
+ #
40
+ module ClassMethods
41
+ def autocomplete(object, method, options = {}, &block)
42
+
43
+ define_method("get_prefix") do |model|
44
+ if defined?(Mongoid::Document) && model.include?(Mongoid::Document)
45
+ 'mongoid'
46
+ elsif defined?(MongoMapper::Document) && model.include?(MongoMapper::Document)
47
+ 'mongo_mapper'
48
+ else
49
+ 'active_record'
50
+ end
51
+ end
52
+ define_method("get_autocomplete_order") do |method, options, model=nil|
53
+ method("#{get_prefix(get_object(options[:class_name] || object))}_get_autocomplete_order").call(method, options, model)
54
+ end
55
+
56
+ define_method("get_autocomplete_items") do |parameters|
57
+ method("#{get_prefix(get_object(options[:class_name] || object))}_get_autocomplete_items").call(parameters)
58
+ end
59
+
60
+ define_method("autocomplete_#{object}_#{method}") do
61
+
62
+ method = options[:column_name] if options.has_key?(:column_name)
63
+
64
+ term = params[:term]
65
+
66
+ if term && !term.blank?
67
+ #allow specifying fully qualified class name for model object
68
+ class_name = options[:class_name] || object
69
+ items = get_autocomplete_items(:model => get_object(class_name), \
70
+ :options => options, :term => term, :method => method)
71
+ else
72
+ items = {}
73
+ end
74
+
75
+ render :json => json_for_autocomplete(items, options[:display_value] ||= method, options[:extra_data], &block), root: false
76
+ end
77
+ end
78
+ end
79
+
80
+ # Returns a limit that will be used on the query
81
+ def get_autocomplete_limit(options)
82
+ options[:limit] ||= 10
83
+ end
84
+
85
+ # Returns parameter model_sym as a constant
86
+ #
87
+ # get_object(:actor)
88
+ # # returns a Actor constant supposing it is already defined
89
+ #
90
+ def get_object(model_sym)
91
+ object = model_sym.to_s.camelize.constantize
92
+ end
93
+
94
+ #
95
+ # Returns a hash with three keys actually used by the Autocomplete jQuery-ui
96
+ # Can be overriden to show whatever you like
97
+ # Hash also includes a key/value pair for each method in extra_data
98
+ #
99
+ def json_for_autocomplete(items, method, extra_data=[])
100
+ items = items.collect do |item|
101
+ hash = {"id" => item.id.to_s, "label" => item.send(method), "value" => item.send(method)}
102
+ extra_data.each do |datum|
103
+ hash[datum] = item.send(datum)
104
+ end if extra_data
105
+ # TODO: Come back to remove this if clause when test suite is better
106
+ hash
107
+ end
108
+ if block_given?
109
+ yield(items)
110
+ else
111
+ items
112
+ end
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,49 @@
1
+ module ActionView
2
+ module Helpers
3
+ module FormHelper
4
+ # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) and
5
+ # that is populated with jQuery's autocomplete plugin.
6
+ #
7
+ # ==== Examples
8
+ # autocomplete_field(:post, :title, author_autocomplete_path, :size => 20)
9
+ # # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" data-autocomplete="author/autocomplete"/>
10
+ #
11
+ def autocomplete_field(object_name, method, source, options ={})
12
+ options["data-autocomplete"] = source
13
+ text_field(object_name, method, rewrite_autocomplete_option(options))
14
+ end
15
+ end
16
+
17
+ module FormTagHelper
18
+ # Creates a standard text field that can be populated with jQuery's autocomplete plugin
19
+ #
20
+ # ==== Examples
21
+ # autocomplete_field_tag 'address', '', address_autocomplete_path, :size => 75
22
+ # # => <input id="address" name="address" size="75" type="text" value="" data-autocomplete="address/autocomplete"/>
23
+ #
24
+ def autocomplete_field_tag(name, value, source, options ={})
25
+ options["data-autocomplete"] = source
26
+ text_field_tag(name, value, rewrite_autocomplete_option(options))
27
+ end
28
+ end
29
+
30
+ #
31
+ # Method used to rename the autocomplete key to a more standard
32
+ # data-autocomplete key
33
+ #
34
+ private
35
+ def rewrite_autocomplete_option(options)
36
+ options["data-autocomplete-fields"] = JSON.generate(options.delete :fields) if options[:fields]
37
+ options["data-update-elements"] = JSON.generate(options.delete :update_elements) if options[:update_elements]
38
+ options["data-id-element"] = options.delete :id_element if options[:id_element]
39
+ options["data-append-to"] = options.delete :append_to if options[:append_to]
40
+ options
41
+ end
42
+ end
43
+ end
44
+
45
+ class ActionView::Helpers::FormBuilder #:nodoc:
46
+ def autocomplete_field(method, source, options = {})
47
+ @template.autocomplete_field(@object_name, method, source, objectify_options(options))
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ #
2
+ # Load the formtastic plugin if using Formtastic
3
+ #
4
+ begin
5
+ require 'formtastic'
6
+ begin
7
+ require "formtastic/version"
8
+ rescue LoadError
9
+ end
10
+
11
+ if defined?(Formtastic::VERSION)
12
+ #
13
+ # Formtastic 2.x
14
+ #
15
+
16
+ module Formtastic
17
+ module Inputs
18
+ class AutocompleteInput
19
+ include Base
20
+ include Base::Stringish
21
+
22
+ def to_html
23
+ input_wrapping do
24
+ label_html <<
25
+ builder.autocomplete_field(method, options.delete(:url), input_html_options)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ else
32
+
33
+ #
34
+ # Formtastic 1.x
35
+ #
36
+ class Formtastic::SemanticFormBuilder < ActionView::Helpers::FormBuilder
37
+ include RailsJQueryAutocomplete::FormtasticPlugin
38
+ end
39
+ end
40
+ rescue LoadError
41
+ end