fetty-generators 1.7.1 → 2.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.
Files changed (167) hide show
  1. data/Gemfile +2 -0
  2. data/README.rdoc +42 -36
  3. data/Rakefile +1 -3
  4. data/lib/generators/fetty.rb +160 -10
  5. data/lib/generators/fetty/authentication/USAGE +4 -0
  6. data/lib/generators/fetty/authentication/authentication_generator.rb +171 -0
  7. data/lib/generators/fetty/authentication/templates/controllers/reset_passwords_controller.rb +45 -0
  8. data/lib/generators/fetty/authentication/templates/controllers/sessions_controller.rb +30 -0
  9. data/lib/generators/fetty/authentication/templates/controllers/users_controller.rb +78 -0
  10. data/lib/generators/fetty/authentication/templates/helpers/reset_passwords_helper.rb +2 -0
  11. data/lib/generators/fetty/authentication/templates/helpers/sessions_helper.rb +2 -0
  12. data/lib/generators/fetty/authentication/templates/helpers/users_helper.rb +2 -0
  13. data/lib/generators/fetty/authentication/templates/lib/sessions_authentication.rb +53 -0
  14. data/lib/generators/fetty/authentication/templates/lib/users_authentication.rb +110 -0
  15. data/lib/generators/fetty/authentication/templates/mailers/setup_mail.rb +11 -0
  16. data/lib/generators/fetty/authentication/templates/mailers/user_mailer.rb +14 -0
  17. data/lib/generators/fetty/authentication/templates/models/active_record/create_users.rb +17 -0
  18. data/lib/generators/fetty/authentication/templates/models/active_record/user.rb +15 -0
  19. data/lib/generators/fetty/authentication/templates/models/mongoid/user.rb +23 -0
  20. data/lib/generators/fetty/authentication/templates/spec/controllers/reset_passwords_controller_spec.rb +93 -0
  21. data/lib/generators/fetty/authentication/templates/spec/controllers/sessions_controller_spec.rb +61 -0
  22. data/lib/generators/fetty/authentication/templates/spec/controllers/users_controller_spec.rb +158 -0
  23. data/lib/generators/fetty/authentication/templates/spec/models/user_spec.rb +108 -0
  24. data/lib/generators/fetty/authentication/templates/spec/routing/reset_passwords_routing_spec.rb +21 -0
  25. data/lib/generators/fetty/authentication/templates/spec/routing/sessions_routing_spec.rb +17 -0
  26. data/lib/generators/fetty/authentication/templates/spec/routing/users_routing_spec.rb +37 -0
  27. data/lib/generators/fetty/authentication/templates/spec/support/user_factories.rb +6 -0
  28. data/lib/generators/fetty/authentication/templates/views/layouts/application.html.erb +33 -0
  29. data/lib/generators/fetty/authentication/templates/views/reset_passwords/edit.html.erb +19 -0
  30. data/lib/generators/fetty/authentication/templates/views/reset_passwords/new.html.erb +14 -0
  31. data/lib/generators/fetty/authentication/templates/views/sessions/new.html.erb +22 -0
  32. data/lib/generators/fetty/authentication/templates/views/user_mailer/user_activation.text.erb +4 -0
  33. data/lib/generators/fetty/authentication/templates/views/user_mailer/user_forgot_password.text.erb +7 -0
  34. data/lib/generators/fetty/authentication/templates/views/users/_table.html.erb +23 -0
  35. data/lib/generators/fetty/authentication/templates/views/users/edit.html.erb +21 -0
  36. data/lib/generators/fetty/authentication/templates/views/users/index.html.erb +7 -0
  37. data/lib/generators/fetty/authentication/templates/views/users/index.js.erb +1 -0
  38. data/lib/generators/fetty/authentication/templates/views/users/new.html.erb +23 -0
  39. data/lib/generators/fetty/authentication/templates/views/users/show.html.erb +19 -0
  40. data/lib/generators/fetty/messages/USAGE +4 -0
  41. data/lib/generators/fetty/messages/messages_generator.rb +136 -0
  42. data/lib/generators/fetty/messages/templates/assets/javascripts/jquery.tokeninput.js +718 -0
  43. data/lib/generators/fetty/messages/templates/assets/javascripts/messages.js +19 -0
  44. data/lib/generators/fetty/messages/templates/assets/stylesheets/messages.css +87 -0
  45. data/lib/generators/fetty/messages/templates/assets/stylesheets/token-input-facebook.css +122 -0
  46. data/lib/generators/fetty/messages/templates/controllers/messages_controller.rb +109 -0
  47. data/lib/generators/fetty/messages/templates/helpers/messages_helper.rb +2 -0
  48. data/lib/generators/fetty/messages/templates/lib/users_messages.rb +67 -0
  49. data/lib/generators/fetty/messages/templates/models/active_record/create_messages.rb +24 -0
  50. data/lib/generators/fetty/messages/templates/models/active_record/message.rb +67 -0
  51. data/lib/generators/fetty/messages/templates/models/mongoid/message.rb +81 -0
  52. data/lib/generators/fetty/messages/templates/spec/controllers/messages_controller_spec.rb +78 -0
  53. data/lib/generators/fetty/messages/templates/spec/models/message_spec.rb +53 -0
  54. data/lib/generators/fetty/messages/templates/spec/routing/messages_routing_spec.rb +61 -0
  55. data/lib/generators/fetty/messages/templates/spec/support/message_factories.rb +8 -0
  56. data/lib/generators/fetty/messages/templates/views/_head.html.erb +20 -0
  57. data/lib/generators/fetty/messages/templates/views/_messages.html.erb +56 -0
  58. data/lib/generators/fetty/messages/templates/views/_tabs_panel.html.erb +11 -0
  59. data/lib/generators/fetty/messages/templates/views/index.html.erb +10 -0
  60. data/lib/generators/fetty/messages/templates/views/index.js.erb +1 -0
  61. data/lib/generators/fetty/messages/templates/views/new.html.erb +26 -0
  62. data/lib/generators/fetty/messages/templates/views/show.html.erb +35 -0
  63. data/lib/generators/fetty/scaffold/USAGE +2 -52
  64. data/lib/generators/fetty/scaffold/scaffold_generator.rb +96 -329
  65. data/lib/generators/fetty/scaffold/templates/controllers/active_record/controller.rb +60 -0
  66. data/lib/generators/fetty/scaffold/templates/controllers/mongoid/controller.rb +60 -0
  67. data/lib/generators/fetty/scaffold/templates/helpers/helper.rb +2 -0
  68. data/lib/generators/fetty/scaffold/templates/models/active_record/migration.rb +16 -0
  69. data/lib/generators/fetty/scaffold/templates/models/active_record/model.rb +10 -0
  70. data/lib/generators/fetty/scaffold/templates/models/mongoid/model.rb +16 -0
  71. data/lib/generators/fetty/scaffold/templates/test/rspec/controller.rb +98 -0
  72. data/lib/generators/fetty/scaffold/templates/test/rspec/factories.rb +26 -0
  73. data/lib/generators/fetty/scaffold/templates/test/rspec/helper.rb +15 -0
  74. data/lib/generators/fetty/scaffold/templates/test/rspec/model.rb +9 -0
  75. data/lib/generators/fetty/scaffold/templates/test/rspec/request.rb +11 -0
  76. data/lib/generators/fetty/scaffold/templates/test/rspec/routing.rb +41 -0
  77. data/lib/generators/fetty/scaffold/templates/test/test_unit/controller.rb +62 -0
  78. data/lib/generators/fetty/scaffold/templates/{fixtures.yml → test/test_unit/fixtures.yml} +2 -0
  79. data/lib/generators/fetty/scaffold/templates/test/test_unit/helper.rb +4 -0
  80. data/lib/generators/fetty/scaffold/templates/test/test_unit/model.rb +7 -0
  81. data/lib/generators/fetty/scaffold/templates/views/_form.html.erb +20 -0
  82. data/lib/generators/fetty/scaffold/templates/views/_table.html.erb +48 -0
  83. data/lib/generators/fetty/scaffold/templates/views/edit.html.erb +13 -0
  84. data/lib/generators/fetty/scaffold/templates/views/index.html.erb +15 -0
  85. data/lib/generators/fetty/scaffold/templates/views/{erb/index.js.erb → index.js.erb} +0 -0
  86. data/lib/generators/fetty/scaffold/templates/views/new.html.erb +9 -0
  87. data/lib/generators/fetty/scaffold/templates/views/show.html.erb +28 -0
  88. data/lib/generators/fetty/setup/USAGE +3 -20
  89. data/lib/generators/fetty/setup/setup_generator.rb +122 -61
  90. data/lib/generators/fetty/setup/templates/ability.rb +0 -0
  91. data/lib/generators/fetty/setup/templates/ckeditor.rb +54 -0
  92. data/lib/generators/fetty/setup/templates/ckeditor.tar.gz +0 -0
  93. data/lib/generators/fetty/setup/templates/escape_utils.rb +7 -0
  94. data/lib/generators/fetty/setup/templates/file_uploader.rb +15 -0
  95. data/lib/generators/fetty/setup/templates/image_uploader.rb +48 -0
  96. data/lib/generators/fetty/setup/templates/spec_helper.rb +44 -0
  97. data/lib/generators/fetty/views/USAGE +5 -0
  98. data/lib/generators/fetty/{layout/templates/stylesheet.css → views/templates/application.css} +184 -166
  99. data/lib/generators/fetty/views/templates/application.html.erb +23 -0
  100. data/lib/generators/fetty/{layout → views}/templates/application.js +0 -0
  101. data/lib/generators/fetty/views/templates/application_helper.rb +10 -0
  102. data/lib/generators/fetty/views/templates/down_arrow.gif +0 -0
  103. data/lib/generators/fetty/{layout → views}/templates/error_messages_helper.rb +0 -0
  104. data/lib/generators/fetty/{layout → views}/templates/layout_helper.rb +0 -0
  105. data/lib/generators/fetty/views/templates/up_arrow.gif +0 -0
  106. data/lib/generators/fetty/views/views_generator.rb +51 -0
  107. data/lib/generators/scaffold.rb +204 -0
  108. metadata +126 -111
  109. data/LICENSE +0 -20
  110. data/lib/generators/fetty/layout/USAGE +0 -26
  111. data/lib/generators/fetty/layout/layout_generator.rb +0 -44
  112. data/lib/generators/fetty/layout/templates/layout.html.erb +0 -32
  113. data/lib/generators/fetty/layout/templates/layout.html.haml +0 -34
  114. data/lib/generators/fetty/layout/templates/no-devise-links-layout.html.erb +0 -23
  115. data/lib/generators/fetty/layout/templates/no-devise-links-layout.html.haml +0 -23
  116. data/lib/generators/fetty/layout/templates/stylesheet.sass +0 -163
  117. data/lib/generators/fetty/scaffold/templates/actions/create.rb +0 -8
  118. data/lib/generators/fetty/scaffold/templates/actions/destroy.rb +0 -5
  119. data/lib/generators/fetty/scaffold/templates/actions/edit.rb +0 -3
  120. data/lib/generators/fetty/scaffold/templates/actions/index.rb +0 -10
  121. data/lib/generators/fetty/scaffold/templates/actions/new.rb +0 -3
  122. data/lib/generators/fetty/scaffold/templates/actions/show.rb +0 -3
  123. data/lib/generators/fetty/scaffold/templates/actions/update.rb +0 -8
  124. data/lib/generators/fetty/scaffold/templates/controller.rb +0 -7
  125. data/lib/generators/fetty/scaffold/templates/helper.rb +0 -2
  126. data/lib/generators/fetty/scaffold/templates/migration.rb +0 -16
  127. data/lib/generators/fetty/scaffold/templates/model.rb +0 -7
  128. data/lib/generators/fetty/scaffold/templates/tests/rspec/actions/create.rb +0 -11
  129. data/lib/generators/fetty/scaffold/templates/tests/rspec/actions/destroy.rb +0 -6
  130. data/lib/generators/fetty/scaffold/templates/tests/rspec/actions/edit.rb +0 -4
  131. data/lib/generators/fetty/scaffold/templates/tests/rspec/actions/index.rb +0 -4
  132. data/lib/generators/fetty/scaffold/templates/tests/rspec/actions/new.rb +0 -4
  133. data/lib/generators/fetty/scaffold/templates/tests/rspec/actions/show.rb +0 -4
  134. data/lib/generators/fetty/scaffold/templates/tests/rspec/actions/update.rb +0 -11
  135. data/lib/generators/fetty/scaffold/templates/tests/rspec/controller.rb +0 -8
  136. data/lib/generators/fetty/scaffold/templates/tests/rspec/model.rb +0 -7
  137. data/lib/generators/fetty/scaffold/templates/tests/shoulda/actions/create.rb +0 -13
  138. data/lib/generators/fetty/scaffold/templates/tests/shoulda/actions/destroy.rb +0 -8
  139. data/lib/generators/fetty/scaffold/templates/tests/shoulda/actions/edit.rb +0 -6
  140. data/lib/generators/fetty/scaffold/templates/tests/shoulda/actions/index.rb +0 -6
  141. data/lib/generators/fetty/scaffold/templates/tests/shoulda/actions/new.rb +0 -6
  142. data/lib/generators/fetty/scaffold/templates/tests/shoulda/actions/show.rb +0 -6
  143. data/lib/generators/fetty/scaffold/templates/tests/shoulda/actions/update.rb +0 -13
  144. data/lib/generators/fetty/scaffold/templates/tests/shoulda/controller.rb +0 -5
  145. data/lib/generators/fetty/scaffold/templates/tests/shoulda/model.rb +0 -7
  146. data/lib/generators/fetty/scaffold/templates/tests/testunit/actions/create.rb +0 -11
  147. data/lib/generators/fetty/scaffold/templates/tests/testunit/actions/destroy.rb +0 -6
  148. data/lib/generators/fetty/scaffold/templates/tests/testunit/actions/edit.rb +0 -4
  149. data/lib/generators/fetty/scaffold/templates/tests/testunit/actions/index.rb +0 -4
  150. data/lib/generators/fetty/scaffold/templates/tests/testunit/actions/new.rb +0 -4
  151. data/lib/generators/fetty/scaffold/templates/tests/testunit/actions/show.rb +0 -4
  152. data/lib/generators/fetty/scaffold/templates/tests/testunit/actions/update.rb +0 -11
  153. data/lib/generators/fetty/scaffold/templates/tests/testunit/controller.rb +0 -5
  154. data/lib/generators/fetty/scaffold/templates/tests/testunit/model.rb +0 -7
  155. data/lib/generators/fetty/scaffold/templates/views/erb/_form.html.erb +0 -22
  156. data/lib/generators/fetty/scaffold/templates/views/erb/_table.html.erb +0 -50
  157. data/lib/generators/fetty/scaffold/templates/views/erb/edit.html.erb +0 -12
  158. data/lib/generators/fetty/scaffold/templates/views/erb/index.html.erb +0 -13
  159. data/lib/generators/fetty/scaffold/templates/views/erb/new.html.erb +0 -9
  160. data/lib/generators/fetty/scaffold/templates/views/erb/show.html.erb +0 -26
  161. data/lib/generators/fetty/scaffold/templates/views/haml/_form.html.haml +0 -8
  162. data/lib/generators/fetty/scaffold/templates/views/haml/_table.html.haml +0 -33
  163. data/lib/generators/fetty/scaffold/templates/views/haml/edit.html.haml +0 -14
  164. data/lib/generators/fetty/scaffold/templates/views/haml/index.html.haml +0 -12
  165. data/lib/generators/fetty/scaffold/templates/views/haml/index.js.haml +0 -1
  166. data/lib/generators/fetty/scaffold/templates/views/haml/new.html.haml +0 -8
  167. data/lib/generators/fetty/scaffold/templates/views/haml/show.html.haml +0 -23
@@ -0,0 +1,19 @@
1
+ <% title "Reset your Password" %>
2
+
3
+ <%= form_tag reset_password_path, :method => :put do %>
4
+ <%= hidden_field_tag :id, @user.id %>
5
+ <%= hidden_field_tag :token, @user.token %>
6
+ <p>
7
+ <%= label_tag :password %><br />
8
+ <%= password_field_tag :password %>
9
+ </p>
10
+ <p>
11
+ <%= label_tag :password_confirmation %><br />
12
+ <%= password_field_tag :password_confirmation %>
13
+ </p>
14
+ <p><%= submit_tag "Update my password" %></p>
15
+ <% end %>
16
+
17
+ <p>
18
+ I already remember my password! <%= link_to "Sign in", new_session_path %>
19
+ </p>
@@ -0,0 +1,14 @@
1
+ <% title "Reset Password" %>
2
+
3
+ <%= form_tag reset_password_path, :method => :post do %>
4
+ <p>
5
+ <%= label_tag :login, "Username or Email Address" %>
6
+ <%= text_field_tag :login %>
7
+ </p>
8
+ <p><%= submit_tag "Send me the password reset instructions" %></p>
9
+ <% end %>
10
+
11
+ <p>
12
+ Don't have an account? <%= link_to "Sign up", new_user_path %><br/>
13
+ Already have an account? <%= link_to "Sign in", new_session_path %>
14
+ </p>
@@ -0,0 +1,22 @@
1
+ <% title "Sign in" %>
2
+
3
+ <%= form_tag session_path do %>
4
+ <p>
5
+ <%= label_tag :login, "Username or Email Address" %><br />
6
+ <%= text_field_tag :login, params[:login] %>
7
+ </p>
8
+ <p>
9
+ <%= label_tag :password %><br />
10
+ <%= password_field_tag :password %>
11
+ </p>
12
+ <p>
13
+ <%= check_box_tag :remember_me %>
14
+ <%= label_tag :remember_me %>
15
+ </p>
16
+ <p><%= submit_tag "Sign in" %></p>
17
+ <% end %>
18
+
19
+ <p>
20
+ Don't have an account? <%= link_to "Sign up", new_user_path %><br/>
21
+ Can't access your account? <%= link_to "Forgot my password", new_reset_password_path %>
22
+ </p>
@@ -0,0 +1,4 @@
1
+ Hi <%= @user.username %>, Thank you for sign up!
2
+
3
+ Please activate your account by clicking this link below :
4
+ <%= activate_users_url(@user.id,@user.token) %>
@@ -0,0 +1,7 @@
1
+ You have requested to reset your password. If you haven't requested to reset your password please let us know at support@example.com.
2
+
3
+ Visit this url to enter a new password:
4
+
5
+ <%= edit_reset_password_url(@user.id,@user.token) %>
6
+
7
+ Note: If you don't visit this link and update your password, it will remain unchanged.
@@ -0,0 +1,23 @@
1
+ <table class="pretty">
2
+ <tr>
3
+ <th>Username</th>
4
+ <th>Email</th>
5
+ <th>Activated at</th>
6
+ <th></th>
7
+ <th></th>
8
+ </tr>
9
+ <% for user in @users %>
10
+ <tr>
11
+ <td><%= user.username %></td>
12
+ <td><%= user.email %></td>
13
+ <td><%= user.activated_at %></td>
14
+ <td>
15
+ <%= link_to 'Edit', edit_user_path(user) %>
16
+ </td>
17
+ <td>
18
+ <%= link_to 'Destroy', user_path(user), :confirm => 'Are you sure want to close this account?', :method => :delete %>
19
+ </td>
20
+ </tr>
21
+ <% end %>
22
+ </table>
23
+
@@ -0,0 +1,21 @@
1
+ <% title "Edit Profile" %>
2
+
3
+ <%= form_tag user_path(@user), :method => :put do %>
4
+ <p>
5
+ <%= label_tag :username %><br />
6
+ <%= text_field_tag :username, @user.username %>
7
+ </p>
8
+ <p>
9
+ <%= label_tag :email %><br />
10
+ <%= text_field_tag :email, @user.email %>
11
+ </p>
12
+ <p>
13
+ <%= label_tag :password %><br />
14
+ <%= password_field_tag :password, @user.password %>
15
+ </p>
16
+ <p>
17
+ <%= label_tag :password_confirmation %><br />
18
+ <%= password_field_tag :password_confirmation, @user.password_confirmation %>
19
+ </p>
20
+ <p><%= submit_tag "Update" %></p>
21
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% title "List of Users" %>
2
+
3
+ <div id="index_table"><%= render 'table' %></div>
4
+
5
+ <p>
6
+ <%= link_to 'New user', new_user_path %>
7
+ </p>
@@ -0,0 +1 @@
1
+ $("#index_table").html("<%= escape_javascript(render("table")) %>");
@@ -0,0 +1,23 @@
1
+ <% title "Sign up" %>
2
+
3
+ <%= form_tag users_path do %>
4
+ <p>
5
+ <%= label_tag :username %><br />
6
+ <%= text_field_tag :username %>
7
+ </p>
8
+ <p>
9
+ <%= label_tag :email %><br />
10
+ <%= text_field_tag :email %>
11
+ </p>
12
+ <p>
13
+ <%= label_tag :password %><br />
14
+ <%= password_field_tag :password %>
15
+ </p>
16
+ <p>
17
+ <%= label_tag :password_confirmation %><br />
18
+ <%= password_field_tag :password_confirmation %>
19
+ </p>
20
+ <p><%= submit_tag "Sign up" %></p>
21
+ <% end %>
22
+
23
+ <p>Already have an account? <%= link_to "Sign in", new_session_path %>.</p>
@@ -0,0 +1,19 @@
1
+ <% title "User Profile" %>
2
+
3
+ <p>
4
+ <strong>Username:</strong>
5
+ <%= @user.username %>
6
+ </p>
7
+ <p>
8
+ <strong>Email:</strong>
9
+ <%= @user.email %>
10
+ </p>
11
+ <p>
12
+ <strong>Password:</strong>
13
+ <%= @user.password %>
14
+ </p>
15
+
16
+ <p>
17
+ <%= link_to "Edit", edit_user_path(@user) %> |
18
+ <%= link_to 'Close Account', @user, :confirm => 'Are you sure want to close this account?', :method => :delete %>
19
+ </p>
@@ -0,0 +1,4 @@
1
+ Description:
2
+ The fetty:messages generator creates a basic messaging system
3
+ between user in your app.
4
+
@@ -0,0 +1,136 @@
1
+ require 'generators/fetty'
2
+ require 'rails/generators/active_record'
3
+ require 'rails/generators/migration'
4
+ require 'rails/generators/generated_attribute'
5
+
6
+ module Fetty
7
+ module Generators
8
+ class MessagesGenerator < Base
9
+ include Rails::Generators::Migration
10
+
11
+ class_option :destroy, :desc => 'Destroy fetty:messages', :type => :boolean, :default => false
12
+
13
+ # have to inject this tag on application.html.erb layout
14
+ # <%= link_to "inbox(#{current_user.inbox(:opened => false).count})", messages_path(:inbox), :id => "inbox-link" %> |
15
+
16
+ def generate_messages
17
+ if options.destroy?
18
+ destroy_messages
19
+ else
20
+ @model_path = "app/models/user.rb"
21
+ if file_exists?(@model_path)
22
+ unless using_mongoid?
23
+ @orm = using_mongoid? ? 'mongoid' : 'active_record'
24
+ using_mongoid? ? add_gem("mongoid-ancestry") : add_gem("ancestry")
25
+ copy_models_and_migrations
26
+ copy_controller_and_helper
27
+ copy_views
28
+ copy_assets
29
+ must_load_lib_directory
30
+ add_routes
31
+ generate_test_unit if using_test_unit?
32
+ generate_specs if using_rspec?
33
+ else
34
+ raise "Sorry, fetty:messages only works with ActiveRecord !!"
35
+ end
36
+ else
37
+ raise "You don't have User model, please install some authentication first!"
38
+ end
39
+ end
40
+ rescue Exception => e
41
+ print_notes(e.message,"error",:red)
42
+ end
43
+
44
+ private
45
+
46
+ def copy_models_and_migrations
47
+ copy_file "models/#{@orm}/message.rb", "app/models/message.rb"
48
+ migration_template "models/active_record/create_messages.rb", "db/migrate/create_messages.rb" unless using_mongoid?
49
+ copy_file "lib/users_messages.rb", "lib/users_messages.rb"
50
+
51
+ inject_into_class @model_path, User do
52
+ "\n has_many :messages" +
53
+ "\n include UsersMessages\n"
54
+ end
55
+ end
56
+
57
+ def copy_controller_and_helper
58
+ copy_file 'controllers/messages_controller.rb', 'app/controllers/messages_controller.rb'
59
+ copy_file 'helpers/messages_helper.rb', 'app/helpers/messages_helper.rb'
60
+ end
61
+
62
+ def copy_views
63
+ copy_file "views/_head.html.erb", "app/views/messages/_head.html.erb"
64
+ copy_file "views/_messages.html.erb", "app/views/messages/_messages.html.erb"
65
+ copy_file "views/_tabs_panel.html.erb", "app/views/messages/_tabs_panel.html.erb"
66
+ copy_file "views/index.html.erb", "app/views/messages/index.html.erb"
67
+ copy_file "views/index.js.erb", "app/views/messages/index.js.erb"
68
+ copy_file "views/new.html.erb", "app/views/messages/new.html.erb"
69
+ copy_file "views/show.html.erb", "app/views/messages/show.html.erb"
70
+ end
71
+
72
+ def copy_assets
73
+ copy_file 'assets/stylesheets/messages.css', 'public/stylesheets/messages.css'
74
+ copy_file 'assets/stylesheets/token-input-facebook.css', 'public/stylesheets/token-input-facebook.css'
75
+ copy_file 'assets/javascripts/messages.js', 'public/javascripts/messages.js'
76
+ copy_file 'assets/javascripts/jquery.tokeninput.js', 'public/javascripts/jquery.tokeninput.js'
77
+ end
78
+
79
+ def add_routes
80
+ inject_into_file "config/routes.rb", :after => "Application.routes.draw do" do
81
+ "\n\n\t resources :messages, :only => [:new, :create] do" +
82
+ "\n\t collection do" +
83
+ "\n\t get 'token' => 'messages#token', :as => 'token'" +
84
+ "\n\t post 'empty/:messagebox' => 'messages#empty', :as => 'empty'" +
85
+ "\n\t put 'update' => 'messages#update'" +
86
+ "\n\t get ':messagebox/show/:id' => 'messages#show', :as => 'show', :constraints => { :messagebox => /inbox|outbox|trash/ }" +
87
+ "\n\t get '(/:messagebox)' => 'messages#index', :as => 'box', :constraints => { :messagebox => /inbox|outbox|trash/ }" +
88
+ "\n\t end" +
89
+ "\n\t end\n"
90
+ end
91
+ end
92
+
93
+ def generate_test_unit
94
+
95
+ end
96
+
97
+ def generate_specs
98
+ copy_file "spec/controllers/messages_controller_spec.rb", "spec/controllers/messages_controller_spec.rb"
99
+ copy_file "spec/models/message_spec.rb", "spec/models/message_spec.rb"
100
+ copy_file "spec/routing/messages_routing_spec.rb", "spec/routing/messages_routing_spec.rb"
101
+ copy_file "spec/support/message_factories.rb", "spec/support/message_factories.rb"
102
+ end
103
+
104
+ def destroy_messages
105
+ asking "Are you sure want to destroy fetty:messages?" do
106
+ remove_file "app/models/message.rb"
107
+ run('rm db/migrate/*_create_messages.rb') unless using_mongoid?
108
+ remove_file "lib/users_messages.rb"
109
+ gsub_file 'app/models/user.rb', /has_many :messages/, ''
110
+ gsub_file 'app/models/user.rb', /include UsersMessages/, ''
111
+ remove_file "app/controllers/messages_controller.rb"
112
+ remove_file "app/helpers/messages_helper.rb"
113
+ remove_dir "app/views/messages"
114
+ remove_file 'public/stylesheets/messages.css'
115
+ remove_file 'public/stylesheets/token-input-facebook.css'
116
+ remove_file 'public/javascripts/messages.js'
117
+ remove_file 'public/javascripts/jquery.tokeninput.js'
118
+ gsub_file 'config/routes.rb', /resources :messages.*:constraints => { :messagebox => \/inbox|outbox|trash\/ }(\s*end){2}/m, ''
119
+
120
+ if using_rspec?
121
+ remove_file "spec/controllers/messages_controller_spec.rb"
122
+ remove_file "spec/models/message_spec.rb"
123
+ remove_file "spec/routing/messages_routing_spec.rb"
124
+ remove_file "spec/support/message_factories.rb"
125
+ end
126
+ end
127
+ end
128
+
129
+ # FIXME: Should be proxied to ActiveRecord::Generators::Base
130
+ # Implement the required interface for Rails::Generators::Migration.
131
+ def self.next_migration_number(dirname) #:nodoc:
132
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,718 @@
1
+ /*
2
+ * jQuery Plugin: Tokenizing Autocomplete Text Entry
3
+ * Version 1.4.2
4
+ *
5
+ * Copyright (c) 2009 James Smith (http://loopj.com)
6
+ * Licensed jointly under the GPL and MIT licenses,
7
+ * choose which one suits your project best!
8
+ *
9
+ */
10
+
11
+ (function ($) {
12
+ // Default settings
13
+ var DEFAULT_SETTINGS = {
14
+ hintText: "Type in a search term",
15
+ noResultsText: "No results",
16
+ searchingText: "Searching...",
17
+ deleteText: "&times;",
18
+ searchDelay: 300,
19
+ minChars: 1,
20
+ tokenLimit: null,
21
+ jsonContainer: null,
22
+ method: "GET",
23
+ contentType: "json",
24
+ queryParam: "q",
25
+ tokenDelimiter: ",",
26
+ preventDuplicates: false,
27
+ prePopulate: null,
28
+ animateDropdown: true,
29
+ onResult: null,
30
+ onAdd: null,
31
+ onDelete: null
32
+ };
33
+
34
+ // Default classes to use when theming
35
+ var DEFAULT_CLASSES = {
36
+ tokenList: "token-input-list",
37
+ token: "token-input-token",
38
+ tokenDelete: "token-input-delete-token",
39
+ selectedToken: "token-input-selected-token",
40
+ highlightedToken: "token-input-highlighted-token",
41
+ dropdown: "token-input-dropdown",
42
+ dropdownItem: "token-input-dropdown-item",
43
+ dropdownItem2: "token-input-dropdown-item2",
44
+ selectedDropdownItem: "token-input-selected-dropdown-item",
45
+ inputToken: "token-input-input-token"
46
+ };
47
+
48
+ // Input box position "enum"
49
+ var POSITION = {
50
+ BEFORE: 0,
51
+ AFTER: 1,
52
+ END: 2
53
+ };
54
+
55
+ // Keys "enum"
56
+ var KEY = {
57
+ BACKSPACE: 8,
58
+ TAB: 9,
59
+ ENTER: 13,
60
+ ESCAPE: 27,
61
+ SPACE: 32,
62
+ PAGE_UP: 33,
63
+ PAGE_DOWN: 34,
64
+ END: 35,
65
+ HOME: 36,
66
+ LEFT: 37,
67
+ UP: 38,
68
+ RIGHT: 39,
69
+ DOWN: 40,
70
+ NUMPAD_ENTER: 108,
71
+ COMMA: 188
72
+ };
73
+
74
+
75
+ // Expose the .tokenInput function to jQuery as a plugin
76
+ $.fn.tokenInput = function (url_or_data, options) {
77
+ var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
78
+
79
+ return this.each(function () {
80
+ new $.TokenList(this, url_or_data, settings);
81
+ });
82
+ };
83
+
84
+
85
+ // TokenList class for each input
86
+ $.TokenList = function (input, url_or_data, settings) {
87
+ //
88
+ // Initialization
89
+ //
90
+
91
+ // Configure the data source
92
+ if($.type(url_or_data) === "string") {
93
+ // Set the url to query against
94
+ settings.url = url_or_data;
95
+
96
+ // Make a smart guess about cross-domain if it wasn't explicitly specified
97
+ if(settings.crossDomain === undefined) {
98
+ if(settings.url.indexOf("://") === -1) {
99
+ settings.crossDomain = false;
100
+ } else {
101
+ settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]);
102
+ }
103
+ }
104
+ } else if($.type(url_or_data) === "array") {
105
+ // Set the local data to search through
106
+ settings.local_data = url_or_data;
107
+ }
108
+
109
+ // Build class names
110
+ if(settings.classes) {
111
+ // Use custom class names
112
+ settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
113
+ } else if(settings.theme) {
114
+ // Use theme-suffixed default class names
115
+ settings.classes = {};
116
+ $.each(DEFAULT_CLASSES, function(key, value) {
117
+ settings.classes[key] = value + "-" + settings.theme;
118
+ });
119
+ } else {
120
+ settings.classes = DEFAULT_CLASSES;
121
+ }
122
+
123
+
124
+ // Save the tokens
125
+ var saved_tokens = [];
126
+
127
+ // Keep track of the number of tokens in the list
128
+ var token_count = 0;
129
+
130
+ // Basic cache to save on db hits
131
+ var cache = new $.TokenList.Cache();
132
+
133
+ // Keep track of the timeout, old vals
134
+ var timeout;
135
+ var input_val;
136
+
137
+ // Create a new text input an attach keyup events
138
+ var input_box = $("<input type=\"text\" autocomplete=\"off\">")
139
+ .css({
140
+ outline: "none"
141
+ })
142
+ .focus(function () {
143
+ if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
144
+ show_dropdown_hint();
145
+ }
146
+ })
147
+ .blur(function () {
148
+ hide_dropdown();
149
+ })
150
+ .bind("keyup keydown blur update", resize_input)
151
+ .keydown(function (event) {
152
+ var previous_token;
153
+ var next_token;
154
+
155
+ switch(event.keyCode) {
156
+ case KEY.LEFT:
157
+ case KEY.RIGHT:
158
+ case KEY.UP:
159
+ case KEY.DOWN:
160
+ if(!$(this).val()) {
161
+ previous_token = input_token.prev();
162
+ next_token = input_token.next();
163
+
164
+ if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
165
+ // Check if there is a previous/next token and it is selected
166
+ if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
167
+ deselect_token($(selected_token), POSITION.BEFORE);
168
+ } else {
169
+ deselect_token($(selected_token), POSITION.AFTER);
170
+ }
171
+ } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
172
+ // We are moving left, select the previous token if it exists
173
+ select_token($(previous_token.get(0)));
174
+ } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
175
+ // We are moving right, select the next token if it exists
176
+ select_token($(next_token.get(0)));
177
+ }
178
+ } else {
179
+ var dropdown_item = null;
180
+
181
+ if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
182
+ dropdown_item = $(selected_dropdown_item).next();
183
+ } else {
184
+ dropdown_item = $(selected_dropdown_item).prev();
185
+ }
186
+
187
+ if(dropdown_item.length) {
188
+ select_dropdown_item(dropdown_item);
189
+ }
190
+ return false;
191
+ }
192
+ break;
193
+
194
+ case KEY.BACKSPACE:
195
+ previous_token = input_token.prev();
196
+
197
+ if(!$(this).val().length) {
198
+ if(selected_token) {
199
+ delete_token($(selected_token));
200
+ } else if(previous_token.length) {
201
+ select_token($(previous_token.get(0)));
202
+ }
203
+
204
+ return false;
205
+ } else if($(this).val().length === 1) {
206
+ hide_dropdown();
207
+ } else {
208
+ // set a timeout just long enough to let this function finish.
209
+ setTimeout(function(){do_search();}, 5);
210
+ }
211
+ break;
212
+
213
+ case KEY.TAB:
214
+ case KEY.ENTER:
215
+ case KEY.NUMPAD_ENTER:
216
+ case KEY.COMMA:
217
+ if(selected_dropdown_item) {
218
+ add_token($(selected_dropdown_item));
219
+ return false;
220
+ }
221
+ break;
222
+
223
+ case KEY.ESCAPE:
224
+ hide_dropdown();
225
+ return true;
226
+
227
+ default:
228
+ if(String.fromCharCode(event.which)) {
229
+ // set a timeout just long enough to let this function finish.
230
+ setTimeout(function(){do_search();}, 5);
231
+ }
232
+ break;
233
+ }
234
+ });
235
+
236
+ // Keep a reference to the original input box
237
+ var hidden_input = $(input)
238
+ .hide()
239
+ .val("")
240
+ .focus(function () {
241
+ input_box.focus();
242
+ })
243
+ .blur(function () {
244
+ input_box.blur();
245
+ });
246
+
247
+ // Keep a reference to the selected token and dropdown item
248
+ var selected_token = null;
249
+ var selected_dropdown_item = null;
250
+
251
+ // The list to store the token items in
252
+ var token_list = $("<ul />")
253
+ .addClass(settings.classes.tokenList)
254
+ .click(function (event) {
255
+ var li = $(event.target).closest("li");
256
+ if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
257
+ toggle_select_token(li);
258
+ } else {
259
+ // Deselect selected token
260
+ if(selected_token) {
261
+ deselect_token($(selected_token), POSITION.END);
262
+ }
263
+
264
+ // Focus input box
265
+ input_box.focus();
266
+ }
267
+ })
268
+ .mouseover(function (event) {
269
+ var li = $(event.target).closest("li");
270
+ if(li && selected_token !== this) {
271
+ li.addClass(settings.classes.highlightedToken);
272
+ }
273
+ })
274
+ .mouseout(function (event) {
275
+ var li = $(event.target).closest("li");
276
+ if(li && selected_token !== this) {
277
+ li.removeClass(settings.classes.highlightedToken);
278
+ }
279
+ })
280
+ .insertBefore(hidden_input);
281
+
282
+ // The token holding the input box
283
+ var input_token = $("<li />")
284
+ .addClass(settings.classes.inputToken)
285
+ .appendTo(token_list)
286
+ .append(input_box);
287
+
288
+ // The list to store the dropdown items in
289
+ var dropdown = $("<div>")
290
+ .addClass(settings.classes.dropdown)
291
+ .appendTo("body")
292
+ .hide();
293
+
294
+ // Magic element to help us resize the text input
295
+ var input_resizer = $("<tester/>")
296
+ .insertAfter(input_box)
297
+ .css({
298
+ position: "absolute",
299
+ top: -9999,
300
+ left: -9999,
301
+ width: "auto",
302
+ fontSize: input_box.css("fontSize"),
303
+ fontFamily: input_box.css("fontFamily"),
304
+ fontWeight: input_box.css("fontWeight"),
305
+ letterSpacing: input_box.css("letterSpacing"),
306
+ whiteSpace: "nowrap"
307
+ });
308
+
309
+ // Pre-populate list if items exist
310
+ hidden_input.val("");
311
+ li_data = settings.prePopulate || hidden_input.data("pre");
312
+ if(li_data && li_data.length) {
313
+ $.each(li_data, function (index, value) {
314
+ insert_token(value.id, value.name);
315
+ });
316
+ }
317
+
318
+
319
+
320
+ //
321
+ // Private functions
322
+ //
323
+
324
+ function resize_input() {
325
+ if(input_val === (input_val = input_box.val())) {return;}
326
+
327
+ // Enter new content into resizer and resize input accordingly
328
+ var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
329
+ input_resizer.html(escaped);
330
+ input_box.width(input_resizer.width() + 30);
331
+ }
332
+
333
+ function is_printable_character(keycode) {
334
+ return ((keycode >= 48 && keycode <= 90) || // 0-1a-z
335
+ (keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * .
336
+ (keycode >= 186 && keycode <= 192) || // ; = , - . / ^
337
+ (keycode >= 219 && keycode <= 222)); // ( \ ) '
338
+ }
339
+
340
+ // Inner function to a token to the list
341
+ function insert_token(id, value) {
342
+ var this_token = $("<li><p>"+ value +"</p> </li>")
343
+ .addClass(settings.classes.token)
344
+ .insertBefore(input_token);
345
+
346
+ // The 'delete token' button
347
+ $("<span>" + settings.deleteText + "</span>")
348
+ .addClass(settings.classes.tokenDelete)
349
+ .appendTo(this_token)
350
+ .click(function () {
351
+ delete_token($(this).parent());
352
+ return false;
353
+ });
354
+
355
+ // Store data on the token
356
+ var token_data = {"id": id, "name": value};
357
+ $.data(this_token.get(0), "tokeninput", token_data);
358
+
359
+ // Save this token for duplicate checking
360
+ saved_tokens.push(token_data);
361
+
362
+ // Update the hidden input
363
+ var token_ids = $.map(saved_tokens, function (el) {
364
+ return el.id;
365
+ });
366
+ hidden_input.val(token_ids.join(settings.tokenDelimiter));
367
+
368
+ token_count += 1;
369
+
370
+ return this_token;
371
+ }
372
+
373
+ // Add a token to the token list based on user input
374
+ function add_token (item) {
375
+ var li_data = $.data(item.get(0), "tokeninput");
376
+ var callback = settings.onAdd;
377
+
378
+ // See if the token already exists and select it if we don't want duplicates
379
+ if(token_count > 0 && settings.preventDuplicates) {
380
+ var found_existing_token = null;
381
+ token_list.children().each(function () {
382
+ var existing_token = $(this);
383
+ var existing_data = $.data(existing_token.get(0), "tokeninput");
384
+ if(existing_data && existing_data.id === li_data.id) {
385
+ found_existing_token = existing_token;
386
+ return false;
387
+ }
388
+ });
389
+
390
+ if(found_existing_token) {
391
+ select_token(found_existing_token);
392
+ input_token.insertAfter(found_existing_token);
393
+ input_box.focus();
394
+ return;
395
+ }
396
+ }
397
+
398
+ // Insert the new tokens
399
+ insert_token(li_data.id, li_data.name);
400
+
401
+ // Check the token limit
402
+ if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
403
+ input_box.hide();
404
+ hide_dropdown();
405
+ return;
406
+ } else {
407
+ input_box.focus();
408
+ }
409
+
410
+ // Clear input box
411
+ input_box.val("");
412
+
413
+ // Don't show the help dropdown, they've got the idea
414
+ hide_dropdown();
415
+
416
+ // Execute the onAdd callback if defined
417
+ if($.isFunction(callback)) {
418
+ callback(li_data);
419
+ }
420
+ }
421
+
422
+ // Select a token in the token list
423
+ function select_token (token) {
424
+ token.addClass(settings.classes.selectedToken);
425
+ selected_token = token.get(0);
426
+
427
+ // Hide input box
428
+ input_box.val("");
429
+
430
+ // Hide dropdown if it is visible (eg if we clicked to select token)
431
+ hide_dropdown();
432
+ }
433
+
434
+ // Deselect a token in the token list
435
+ function deselect_token (token, position) {
436
+ token.removeClass(settings.classes.selectedToken);
437
+ selected_token = null;
438
+
439
+ if(position === POSITION.BEFORE) {
440
+ input_token.insertBefore(token);
441
+ } else if(position === POSITION.AFTER) {
442
+ input_token.insertAfter(token);
443
+ } else {
444
+ input_token.appendTo(token_list);
445
+ }
446
+
447
+ // Show the input box and give it focus again
448
+ input_box.focus();
449
+ }
450
+
451
+ // Toggle selection of a token in the token list
452
+ function toggle_select_token(token) {
453
+ var previous_selected_token = selected_token;
454
+
455
+ if(selected_token) {
456
+ deselect_token($(selected_token), POSITION.END);
457
+ }
458
+
459
+ if(previous_selected_token === token.get(0)) {
460
+ deselect_token(token, POSITION.END);
461
+ } else {
462
+ select_token(token);
463
+ }
464
+ }
465
+
466
+ // Delete a token from the token list
467
+ function delete_token (token) {
468
+ // Remove the id from the saved list
469
+ var token_data = $.data(token.get(0), "tokeninput");
470
+ var callback = settings.onDelete;
471
+
472
+ // Delete the token
473
+ token.remove();
474
+ selected_token = null;
475
+
476
+ // Show the input box and give it focus again
477
+ input_box.focus();
478
+
479
+ // Remove this token from the saved list
480
+ saved_tokens = $.grep(saved_tokens, function (val) {
481
+ return (val.id !== token_data.id);
482
+ });
483
+
484
+ // Update the hidden input
485
+ var token_ids = $.map(saved_tokens, function (el) {
486
+ return el.id;
487
+ });
488
+ hidden_input.val(token_ids.join(settings.tokenDelimiter));
489
+
490
+ token_count -= 1;
491
+
492
+ if(settings.tokenLimit !== null) {
493
+ input_box
494
+ .show()
495
+ .val("")
496
+ .focus();
497
+ }
498
+
499
+ // Execute the onDelete callback if defined
500
+ if($.isFunction(callback)) {
501
+ callback(token_data);
502
+ }
503
+ }
504
+
505
+ // Hide and clear the results dropdown
506
+ function hide_dropdown () {
507
+ dropdown.hide().empty();
508
+ selected_dropdown_item = null;
509
+ }
510
+
511
+ function show_dropdown() {
512
+ dropdown
513
+ .css({
514
+ position: "absolute",
515
+ top: $(token_list).offset().top + $(token_list).outerHeight(),
516
+ left: $(token_list).offset().left,
517
+ zindex: 999
518
+ })
519
+ .show();
520
+ }
521
+
522
+ function show_dropdown_searching () {
523
+ if(settings.searchingText) {
524
+ dropdown.html("<p>"+settings.searchingText+"</p>");
525
+ show_dropdown();
526
+ }
527
+ }
528
+
529
+ function show_dropdown_hint () {
530
+ if(settings.hintText) {
531
+ dropdown.html("<p>"+settings.hintText+"</p>");
532
+ show_dropdown();
533
+ }
534
+ }
535
+
536
+ // Highlight the query part of the search term
537
+ function highlight_term(value, term) {
538
+ return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
539
+ }
540
+
541
+ // Populate the results dropdown with some results
542
+ function populate_dropdown (query, results) {
543
+ if(results && results.length) {
544
+ dropdown.empty();
545
+ var dropdown_ul = $("<ul>")
546
+ .appendTo(dropdown)
547
+ .mouseover(function (event) {
548
+ select_dropdown_item($(event.target).closest("li"));
549
+ })
550
+ .mousedown(function (event) {
551
+ add_token($(event.target).closest("li"));
552
+ return false;
553
+ })
554
+ .hide();
555
+
556
+ $.each(results, function(index, value) {
557
+ var this_li = $("<li>" + highlight_term(value.name, query) + "</li>")
558
+ .appendTo(dropdown_ul);
559
+
560
+ if(index % 2) {
561
+ this_li.addClass(settings.classes.dropdownItem);
562
+ } else {
563
+ this_li.addClass(settings.classes.dropdownItem2);
564
+ }
565
+
566
+ if(index === 0) {
567
+ select_dropdown_item(this_li);
568
+ }
569
+
570
+ $.data(this_li.get(0), "tokeninput", {"id": value.id, "name": value.name});
571
+ });
572
+
573
+ show_dropdown();
574
+
575
+ if(settings.animateDropdown) {
576
+ dropdown_ul.slideDown("fast");
577
+ } else {
578
+ dropdown_ul.show();
579
+ }
580
+ } else {
581
+ if(settings.noResultsText) {
582
+ dropdown.html("<p>"+settings.noResultsText+"</p>");
583
+ show_dropdown();
584
+ }
585
+ }
586
+ }
587
+
588
+ // Highlight an item in the results dropdown
589
+ function select_dropdown_item (item) {
590
+ if(item) {
591
+ if(selected_dropdown_item) {
592
+ deselect_dropdown_item($(selected_dropdown_item));
593
+ }
594
+
595
+ item.addClass(settings.classes.selectedDropdownItem);
596
+ selected_dropdown_item = item.get(0);
597
+ }
598
+ }
599
+
600
+ // Remove highlighting from an item in the results dropdown
601
+ function deselect_dropdown_item (item) {
602
+ item.removeClass(settings.classes.selectedDropdownItem);
603
+ selected_dropdown_item = null;
604
+ }
605
+
606
+ // Do a search and show the "searching" dropdown if the input is longer
607
+ // than settings.minChars
608
+ function do_search() {
609
+ var query = input_box.val().toLowerCase();
610
+
611
+ if(query && query.length) {
612
+ if(selected_token) {
613
+ deselect_token($(selected_token), POSITION.AFTER);
614
+ }
615
+
616
+ if(query.length >= settings.minChars) {
617
+ show_dropdown_searching();
618
+ clearTimeout(timeout);
619
+
620
+ timeout = setTimeout(function(){
621
+ run_search(query);
622
+ }, settings.searchDelay);
623
+ } else {
624
+ hide_dropdown();
625
+ }
626
+ }
627
+ }
628
+
629
+ // Do the actual search
630
+ function run_search(query) {
631
+ var cached_results = cache.get(query);
632
+ if(cached_results) {
633
+ populate_dropdown(query, cached_results);
634
+ } else {
635
+ // Are we doing an ajax search or local data search?
636
+ if(settings.url) {
637
+ // Extract exisiting get params
638
+ var ajax_params = {};
639
+ ajax_params.data = {};
640
+ if(settings.url.indexOf("?") > -1) {
641
+ var parts = settings.url.split("?");
642
+ ajax_params.url = parts[0];
643
+
644
+ var param_array = parts[1].split("&");
645
+ $.each(param_array, function (index, value) {
646
+ var kv = value.split("=");
647
+ ajax_params.data[kv[0]] = kv[1];
648
+ });
649
+ } else {
650
+ ajax_params.url = settings.url;
651
+ }
652
+
653
+ // Prepare the request
654
+ ajax_params.data[settings.queryParam] = query;
655
+ ajax_params.type = settings.method;
656
+ ajax_params.dataType = settings.contentType;
657
+ if(settings.crossDomain) {
658
+ ajax_params.dataType = "jsonp";
659
+ }
660
+
661
+ // Attach the success callback
662
+ ajax_params.success = function(results) {
663
+ if($.isFunction(settings.onResult)) {
664
+ results = settings.onResult.call(this, results);
665
+ }
666
+ cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
667
+
668
+ // only populate the dropdown if the results are associated with the active search query
669
+ if(input_box.val().toLowerCase() === query) {
670
+ populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
671
+ }
672
+ };
673
+
674
+ // Make the request
675
+ $.ajax(ajax_params);
676
+ } else if(settings.local_data) {
677
+ // Do the search through local data
678
+ var results = $.grep(settings.local_data, function (row) {
679
+ return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
680
+ });
681
+
682
+ populate_dropdown(query, results);
683
+ }
684
+ }
685
+ }
686
+ };
687
+
688
+ // Really basic cache for the results
689
+ $.TokenList.Cache = function (options) {
690
+ var settings = $.extend({
691
+ max_size: 500
692
+ }, options);
693
+
694
+ var data = {};
695
+ var size = 0;
696
+
697
+ var flush = function () {
698
+ data = {};
699
+ size = 0;
700
+ };
701
+
702
+ this.add = function (query, results) {
703
+ if(size > settings.max_size) {
704
+ flush();
705
+ }
706
+
707
+ if(!data[query]) {
708
+ size += 1;
709
+ }
710
+
711
+ data[query] = results;
712
+ };
713
+
714
+ this.get = function (query) {
715
+ return data[query];
716
+ };
717
+ };
718
+ }(jQuery));