muck-contents 0.1.12 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. data/README.rdoc +122 -15
  2. data/VERSION +1 -1
  3. data/app/controllers/muck/tiny_mce_controller.rb +47 -0
  4. data/app/helpers/tinymce_helper.rb +23 -8
  5. data/app/views/contents/_form.html.erb +1 -2
  6. data/app/views/contents/new.html.erb +1 -0
  7. data/app/views/layouts/tiny_mce.html.erb +25 -0
  8. data/app/views/tiny_mce/files.html.erb +136 -0
  9. data/app/views/tiny_mce/images.html.erb +312 -0
  10. data/{public/javascripts/tiny_mce/plugins/advlinktoo/link.htm → app/views/tiny_mce/links.html.erb} +328 -344
  11. data/config/muck_contents_routes.rb +6 -0
  12. data/lib/action_controller/acts/muck_content_handler.rb +1 -1
  13. data/locales/en.yml +2 -0
  14. data/muck-contents.gemspec +153 -25
  15. data/public/{javascripts/tiny_mce/plugins/advimagetoo/img → images/tinymce}/sample.gif +0 -0
  16. data/public/{javascripts/tiny_mce/plugins/advfiletoo/img → images/tinymce}/upload.gif +0 -0
  17. data/public/javascripts/tiny_mce/plugins/advfiletoo/editor_plugin.js +9 -22
  18. data/public/javascripts/tiny_mce/plugins/advfiletoo/editor_plugin_src.js +41 -54
  19. data/public/javascripts/tiny_mce/plugins/advfiletoo/js/functions.js +0 -139
  20. data/public/javascripts/tiny_mce/plugins/advimagetoo/editor_plugin.js +9 -17
  21. data/public/javascripts/tiny_mce/plugins/advimagetoo/editor_plugin_src.js +40 -50
  22. data/public/javascripts/tiny_mce/plugins/advimagetoo/js/functions.js +0 -152
  23. data/public/javascripts/tiny_mce/plugins/advlinktoo/editor_plugin.js +3 -14
  24. data/public/javascripts/tiny_mce/plugins/advlinktoo/editor_plugin_src.js +3 -14
  25. data/public/{javascripts/tiny_mce/plugins/advfiletoo/css/advfile.css → stylesheets/tinymce.css} +33 -12
  26. data/test/rails_root/Rakefile +2 -1
  27. data/test/rails_root/app/controllers/uploads_controller.rb +57 -0
  28. data/test/rails_root/app/models/upload.rb +94 -0
  29. data/test/rails_root/app/models/user.rb +5 -0
  30. data/test/rails_root/config/environment.rb +2 -0
  31. data/test/rails_root/config/initializers/mce_options.rb +12 -8
  32. data/test/rails_root/config/initializers/s3_credentials.rb +9 -0
  33. data/test/rails_root/db/migrate/{20090602041838_create_users.rb → 20090327231918_create_users.rb} +11 -10
  34. data/test/rails_root/db/migrate/20090402234137_create_languages.rb +18 -0
  35. data/test/rails_root/db/migrate/20090426041056_create_countries.rb +15 -0
  36. data/test/rails_root/db/migrate/20090426041103_create_states.rb +18 -0
  37. data/test/rails_root/db/migrate/{20090704220120_acts_as_taggable_on_migration.rb → 20090506000404_acts_as_taggable_on_migration.rb} +31 -29
  38. data/test/rails_root/db/migrate/20090517040220_create_uploads.rb +38 -0
  39. data/test/rails_root/db/migrate/20090730044139_add_comment_cache.rb +9 -0
  40. data/test/rails_root/db/migrate/20090818204527_add_activity_indexes.rb +9 -0
  41. data/test/rails_root/db/migrate/20090819030523_add_attachable_to_activities.rb +13 -0
  42. data/test/rails_root/lib/daemons/amazonaws.rb +36 -0
  43. data/test/rails_root/lib/tasks/muck.rake +7 -0
  44. data/test/rails_root/public/images/SWFUploadButton.png +0 -0
  45. data/test/rails_root/public/images/arrow_down.gif +0 -0
  46. data/test/rails_root/public/images/arrow_up.gif +0 -0
  47. data/test/rails_root/public/images/cancelbutton.gif +0 -0
  48. data/test/rails_root/public/images/fancybox/fancy_closebox.png +0 -0
  49. data/test/rails_root/public/images/fancybox/fancy_left.png +0 -0
  50. data/test/rails_root/public/images/fancybox/fancy_progress.png +0 -0
  51. data/test/rails_root/public/images/fancybox/fancy_right.png +0 -0
  52. data/test/rails_root/public/images/fancybox/fancy_shadow_e.png +0 -0
  53. data/test/rails_root/public/images/fancybox/fancy_shadow_n.png +0 -0
  54. data/test/rails_root/public/images/fancybox/fancy_shadow_ne.png +0 -0
  55. data/test/rails_root/public/images/fancybox/fancy_shadow_nw.png +0 -0
  56. data/test/rails_root/public/images/fancybox/fancy_shadow_s.png +0 -0
  57. data/test/rails_root/public/images/fancybox/fancy_shadow_se.png +0 -0
  58. data/test/rails_root/public/images/fancybox/fancy_shadow_sw.png +0 -0
  59. data/test/rails_root/public/images/fancybox/fancy_shadow_w.png +0 -0
  60. data/test/rails_root/public/images/fancybox/fancy_title_left.png +0 -0
  61. data/test/rails_root/public/images/fancybox/fancy_title_main.png +0 -0
  62. data/test/rails_root/public/images/fancybox/fancy_title_right.png +0 -0
  63. data/test/rails_root/public/images/file_icons/excel.gif +0 -0
  64. data/test/rails_root/public/images/file_icons/file.gif +0 -0
  65. data/test/rails_root/public/images/file_icons/file.png +0 -0
  66. data/test/rails_root/public/images/file_icons/file_aac.gif +0 -0
  67. data/test/rails_root/public/images/file_icons/file_ai.gif +0 -0
  68. data/test/rails_root/public/images/file_icons/file_avi.gif +0 -0
  69. data/test/rails_root/public/images/file_icons/file_bin.gif +0 -0
  70. data/test/rails_root/public/images/file_icons/file_bmp.gif +0 -0
  71. data/test/rails_root/public/images/file_icons/file_cue.gif +0 -0
  72. data/test/rails_root/public/images/file_icons/file_divx.gif +0 -0
  73. data/test/rails_root/public/images/file_icons/file_doc.gif +0 -0
  74. data/test/rails_root/public/images/file_icons/file_eps.gif +0 -0
  75. data/test/rails_root/public/images/file_icons/file_flac.gif +0 -0
  76. data/test/rails_root/public/images/file_icons/file_flv.gif +0 -0
  77. data/test/rails_root/public/images/file_icons/file_gif.gif +0 -0
  78. data/test/rails_root/public/images/file_icons/file_html.gif +0 -0
  79. data/test/rails_root/public/images/file_icons/file_ical.gif +0 -0
  80. data/test/rails_root/public/images/file_icons/file_indd.gif +0 -0
  81. data/test/rails_root/public/images/file_icons/file_inx.gif +0 -0
  82. data/test/rails_root/public/images/file_icons/file_iso.gif +0 -0
  83. data/test/rails_root/public/images/file_icons/file_jpg.gif +0 -0
  84. data/test/rails_root/public/images/file_icons/file_mov.gif +0 -0
  85. data/test/rails_root/public/images/file_icons/file_mp3.gif +0 -0
  86. data/test/rails_root/public/images/file_icons/file_mpg.gif +0 -0
  87. data/test/rails_root/public/images/file_icons/file_pdf.gif +0 -0
  88. data/test/rails_root/public/images/file_icons/file_php.gif +0 -0
  89. data/test/rails_root/public/images/file_icons/file_png.gif +0 -0
  90. data/test/rails_root/public/images/file_icons/file_pps.gif +0 -0
  91. data/test/rails_root/public/images/file_icons/file_ppt.gif +0 -0
  92. data/test/rails_root/public/images/file_icons/file_psd.gif +0 -0
  93. data/test/rails_root/public/images/file_icons/file_qxd.gif +0 -0
  94. data/test/rails_root/public/images/file_icons/file_qxp.gif +0 -0
  95. data/test/rails_root/public/images/file_icons/file_raw.gif +0 -0
  96. data/test/rails_root/public/images/file_icons/file_rtf.gif +0 -0
  97. data/test/rails_root/public/images/file_icons/file_svg.gif +0 -0
  98. data/test/rails_root/public/images/file_icons/file_tif.gif +0 -0
  99. data/test/rails_root/public/images/file_icons/file_txt.gif +0 -0
  100. data/test/rails_root/public/images/file_icons/file_vcf.gif +0 -0
  101. data/test/rails_root/public/images/file_icons/file_wav.gif +0 -0
  102. data/test/rails_root/public/images/file_icons/file_wma.gif +0 -0
  103. data/test/rails_root/public/images/file_icons/file_xls.gif +0 -0
  104. data/test/rails_root/public/images/file_icons/file_xml.gif +0 -0
  105. data/test/rails_root/public/images/file_icons/mp3.gif +0 -0
  106. data/test/rails_root/public/images/file_icons/pdf.gif +0 -0
  107. data/test/rails_root/public/images/file_icons/pdf.png +0 -0
  108. data/test/rails_root/public/images/file_icons/text.gif +0 -0
  109. data/test/rails_root/public/images/file_icons/text.png +0 -0
  110. data/test/rails_root/public/images/file_icons/word.gif +0 -0
  111. data/test/rails_root/public/images/icons/blue_guy.png +0 -0
  112. data/test/rails_root/public/images/icons/button_background.png +0 -0
  113. data/test/rails_root/public/images/icons/cancel.png +0 -0
  114. data/test/rails_root/public/images/icons/close.png +0 -0
  115. data/test/rails_root/public/images/icons/comment.png +0 -0
  116. data/test/rails_root/public/images/icons/exclaim.png +0 -0
  117. data/test/rails_root/public/images/icons/external.png +0 -0
  118. data/test/rails_root/public/images/icons/feed.png +0 -0
  119. data/test/rails_root/public/images/icons/grey_guy.png +0 -0
  120. data/test/rails_root/public/images/icons/hide.png +0 -0
  121. data/test/rails_root/public/images/icons/information.png +0 -0
  122. data/test/rails_root/public/images/icons/minus.png +0 -0
  123. data/test/rails_root/public/images/icons/pending.png +0 -0
  124. data/test/rails_root/public/images/icons/question.png +0 -0
  125. data/test/rails_root/public/images/icons/search_box.png +0 -0
  126. data/test/rails_root/public/images/icons/star.png +0 -0
  127. data/test/rails_root/public/images/icons/stop.png +0 -0
  128. data/test/rails_root/public/images/icons/thumb_down.png +0 -0
  129. data/test/rails_root/public/images/icons/thumb_up.png +0 -0
  130. data/test/rails_root/public/images/nothing.png +0 -0
  131. data/test/rails_root/public/{javascripts/tiny_mce/plugins/advimagetoo/img → images/tinymce}/sample.gif +0 -0
  132. data/test/rails_root/public/{javascripts/tiny_mce/plugins/advfiletoo/img → images/tinymce}/upload.gif +0 -0
  133. data/test/rails_root/public/javascripts/jquery/colorpicker.js +450 -0
  134. data/test/rails_root/public/javascripts/jquery/jquery.autocomplete.js.readme +6 -0
  135. data/test/rails_root/public/javascripts/jquery/jquery.autocomplete.min.js +15 -0
  136. data/test/rails_root/public/javascripts/jquery/jquery.autocomplete.pack.js +767 -0
  137. data/test/rails_root/public/javascripts/jquery/jquery.easing.js +1 -0
  138. data/test/rails_root/public/javascripts/jquery/jquery.fancybox.js +9 -0
  139. data/test/rails_root/public/javascripts/jquery/jquery.metadata.min.js +13 -0
  140. data/test/rails_root/public/javascripts/jquery/jquery.queryString.js +33 -0
  141. data/test/rails_root/public/javascripts/jquery/jquery.swapimage.js +66 -0
  142. data/test/rails_root/public/javascripts/jquery/jquery.swapimage.min.js +11 -0
  143. data/test/rails_root/public/javascripts/jquery/jrails.js +1 -0
  144. data/test/rails_root/public/javascripts/muck.js +11 -6
  145. data/test/rails_root/public/javascripts/muck_activities.js +5 -6
  146. data/test/rails_root/public/javascripts/muck_time/en.js +53 -0
  147. data/test/rails_root/public/javascripts/swfupload/fileprogress.js +151 -0
  148. data/test/rails_root/public/javascripts/swfupload/handlers.js +201 -0
  149. data/test/rails_root/public/javascripts/swfupload/swfupload.cookies.js +53 -0
  150. data/test/rails_root/public/javascripts/swfupload/swfupload.js +980 -0
  151. data/test/rails_root/public/javascripts/swfupload/swfupload.queue.js +98 -0
  152. data/test/rails_root/public/javascripts/swfupload/swfupload.speed.js +342 -0
  153. data/test/rails_root/public/javascripts/swfupload/swfupload.swfobject.js +111 -0
  154. data/test/rails_root/public/javascripts/tiny_mce/plugins/advfiletoo/editor_plugin.js +9 -22
  155. data/test/rails_root/public/javascripts/tiny_mce/plugins/advfiletoo/editor_plugin_src.js +41 -54
  156. data/test/rails_root/public/javascripts/tiny_mce/plugins/advfiletoo/js/functions.js +0 -139
  157. data/test/rails_root/public/javascripts/tiny_mce/plugins/advimagetoo/editor_plugin.js +9 -17
  158. data/test/rails_root/public/javascripts/tiny_mce/plugins/advimagetoo/editor_plugin_src.js +40 -50
  159. data/test/rails_root/public/javascripts/tiny_mce/plugins/advimagetoo/image.htm +10 -9
  160. data/test/rails_root/public/javascripts/tiny_mce/plugins/advimagetoo/js/functions.js +0 -152
  161. data/test/rails_root/public/javascripts/tiny_mce/plugins/advlinktoo/editor_plugin.js +3 -14
  162. data/test/rails_root/public/javascripts/tiny_mce/plugins/advlinktoo/editor_plugin_src.js +3 -14
  163. data/test/rails_root/public/javascripts/tiny_mce/templates/example.htm +14 -0
  164. data/test/rails_root/public/javascripts/tree.js +107 -0
  165. data/test/rails_root/public/stylesheets/jquery/jquery.autocomplete.css +15 -0
  166. data/test/rails_root/public/stylesheets/jquery/jquery.fancybox.css +44 -0
  167. data/test/rails_root/public/stylesheets/muck-activities.css +39 -0
  168. data/test/rails_root/public/stylesheets/styles.css +10 -3
  169. data/test/rails_root/public/stylesheets/swfupload.css +26 -0
  170. data/test/rails_root/public/{javascripts/tiny_mce/plugins/advfiletoo/css/advfile.css → stylesheets/tinymce.css} +33 -12
  171. data/test/rails_root/public/system/locals/000/000/026/original/deploy-1.rb +75 -0
  172. metadata +153 -25
  173. data/public/javascripts/tiny_mce/plugins/advfiletoo/file.htm +0 -139
  174. data/public/javascripts/tiny_mce/plugins/advimagetoo/css/advimage.css +0 -32
  175. data/public/javascripts/tiny_mce/plugins/advimagetoo/image.htm +0 -303
  176. data/public/javascripts/tiny_mce/plugins/advlinktoo/css/advlink.css +0 -8
  177. data/test/rails_root/db/migrate/20090608073052_create_friends.rb +0 -16
  178. data/test/rails_root/db/migrate/20090808005918_add_scope_index_to_slugs.rb +0 -9
  179. data/test/rails_root/public/javascripts/tiny_mce/plugins/advfiletoo/file.htm +0 -139
  180. data/test/rails_root/public/javascripts/tiny_mce/plugins/advimagetoo/css/advimage.css +0 -32
  181. data/test/rails_root/public/javascripts/tiny_mce/templates/bio.htm +0 -31
  182. data/test/rails_root/public/javascripts/tiny_mce/templates/country.htm +0 -59
@@ -0,0 +1,767 @@
1
+ /**********************************************************************************************
2
+ * NOTE: this version is modified from the original to work with luvfoo. Specifically line
3
+ * 314 was changed from
4
+ * if ( data && data.length && hasFocus ) {
5
+ * to
6
+ * if ( data && data.length ) {
7
+ * For some reason the hasFocus value was always 0 which prevented the autocomplete from ever showing up.
8
+ **********************************************************************************************
9
+ *
10
+ * Autocomplete - jQuery plugin 1.0.2
11
+ *
12
+ * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
13
+ *
14
+ * Dual licensed under the MIT and GPL licenses:
15
+ * http://www.opensource.org/licenses/mit-license.php
16
+ * http://www.gnu.org/licenses/gpl.html
17
+ *
18
+ * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
19
+ *
20
+ */
21
+
22
+ ;(function($) {
23
+
24
+ $.fn.extend({
25
+ autocomplete: function(urlOrData, options) {
26
+ var isUrl = typeof urlOrData == "string";
27
+ options = $.extend({}, $.Autocompleter.defaults, {
28
+ url: isUrl ? urlOrData : null,
29
+ data: isUrl ? null : urlOrData,
30
+ delay: isUrl ? $.Autocompleter.defaults.delay : 10,
31
+ max: options && !options.scroll ? 10 : 150
32
+ }, options);
33
+
34
+ // if highlight is set to false, replace it with a do-nothing function
35
+ options.highlight = options.highlight || function(value) { return value; };
36
+
37
+ // if the formatMatch option is not specified, then use formatItem for backwards compatibility
38
+ options.formatMatch = options.formatMatch || options.formatItem;
39
+
40
+ return this.each(function() {
41
+ new $.Autocompleter(this, options);
42
+ });
43
+ },
44
+ result: function(handler) {
45
+ return this.bind("result", handler);
46
+ },
47
+ search: function(handler) {
48
+ return this.trigger("search", [handler]);
49
+ },
50
+ flushCache: function() {
51
+ return this.trigger("flushCache");
52
+ },
53
+ setOptions: function(options){
54
+ return this.trigger("setOptions", [options]);
55
+ },
56
+ unautocomplete: function() {
57
+ return this.trigger("unautocomplete");
58
+ }
59
+ });
60
+
61
+ $.Autocompleter = function(input, options) {
62
+
63
+ var KEY = {
64
+ UP: 38,
65
+ DOWN: 40,
66
+ DEL: 46,
67
+ TAB: 9,
68
+ RETURN: 13,
69
+ ESC: 27,
70
+ COMMA: 188,
71
+ PAGEUP: 33,
72
+ PAGEDOWN: 34,
73
+ BACKSPACE: 8
74
+ };
75
+
76
+ // Create $ object for input element
77
+ var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
78
+
79
+ var timeout;
80
+ var previousValue = "";
81
+ var cache = $.Autocompleter.Cache(options);
82
+ var hasFocus = 0;
83
+ var lastKeyPressCode;
84
+ var config = {
85
+ mouseDownOnSelect: false
86
+ };
87
+ var select = $.Autocompleter.Select(options, input, selectCurrent, config);
88
+
89
+ var blockSubmit;
90
+
91
+ // prevent form submit in opera when selecting with return key
92
+ $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
93
+ if (blockSubmit) {
94
+ blockSubmit = false;
95
+ return false;
96
+ }
97
+ });
98
+
99
+ // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
100
+ $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
101
+ // track last key pressed
102
+ lastKeyPressCode = event.keyCode;
103
+ switch(event.keyCode) {
104
+
105
+ case KEY.UP:
106
+ event.preventDefault();
107
+ if ( select.visible() ) {
108
+ select.prev();
109
+ } else {
110
+ onChange(0, true);
111
+ }
112
+ break;
113
+
114
+ case KEY.DOWN:
115
+ event.preventDefault();
116
+ if ( select.visible() ) {
117
+ select.next();
118
+ } else {
119
+ onChange(0, true);
120
+ }
121
+ break;
122
+
123
+ case KEY.PAGEUP:
124
+ event.preventDefault();
125
+ if ( select.visible() ) {
126
+ select.pageUp();
127
+ } else {
128
+ onChange(0, true);
129
+ }
130
+ break;
131
+
132
+ case KEY.PAGEDOWN:
133
+ event.preventDefault();
134
+ if ( select.visible() ) {
135
+ select.pageDown();
136
+ } else {
137
+ onChange(0, true);
138
+ }
139
+ break;
140
+
141
+ // matches also semicolon
142
+ case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
143
+ case KEY.TAB:
144
+ case KEY.RETURN:
145
+ if( selectCurrent() ) {
146
+ // stop default to prevent a form submit, Opera needs special handling
147
+ event.preventDefault();
148
+ blockSubmit = true;
149
+ return false;
150
+ }
151
+ break;
152
+
153
+ case KEY.ESC:
154
+ select.hide();
155
+ break;
156
+
157
+ default:
158
+ clearTimeout(timeout);
159
+ timeout = setTimeout(onChange, options.delay);
160
+ break;
161
+ }
162
+ }).focus(function(){
163
+ // track whether the field has focus, we shouldn't process any
164
+ // results if the field no longer has focus
165
+ hasFocus++;
166
+ }).blur(function() {
167
+ hasFocus = 0;
168
+ if (!config.mouseDownOnSelect) {
169
+ hideResults();
170
+ }
171
+ }).click(function() {
172
+ // show select when clicking in a focused field
173
+ if ( hasFocus++ > 1 && !select.visible() ) {
174
+ onChange(0, true);
175
+ }
176
+ }).bind("search", function() {
177
+ // TODO why not just specifying both arguments?
178
+ var fn = (arguments.length > 1) ? arguments[1] : null;
179
+ function findValueCallback(q, data) {
180
+ var result;
181
+ if( data && data.length ) {
182
+ for (var i=0; i < data.length; i++) {
183
+ if( data[i].result.toLowerCase() == q.toLowerCase() ) {
184
+ result = data[i];
185
+ break;
186
+ }
187
+ }
188
+ }
189
+ if( typeof fn == "function" ) fn(result);
190
+ else $input.trigger("result", result && [result.data, result.value]);
191
+ }
192
+ $.each(trimWords($input.val()), function(i, value) {
193
+ request(value, findValueCallback, findValueCallback);
194
+ });
195
+ }).bind("flushCache", function() {
196
+ cache.flush();
197
+ }).bind("setOptions", function() {
198
+ $.extend(options, arguments[1]);
199
+ // if we've updated the data, repopulate
200
+ if ( "data" in arguments[1] )
201
+ cache.populate();
202
+ }).bind("unautocomplete", function() {
203
+ select.unbind();
204
+ $input.unbind();
205
+ $(input.form).unbind(".autocomplete");
206
+ });
207
+
208
+
209
+ function selectCurrent() {
210
+ var selected = select.selected();
211
+ if( !selected )
212
+ return false;
213
+
214
+ var v = selected.result;
215
+ previousValue = v;
216
+
217
+ if ( options.multiple ) {
218
+ var words = trimWords($input.val());
219
+ if ( words.length > 1 ) {
220
+ v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
221
+ }
222
+ v += options.multipleSeparator;
223
+ }
224
+
225
+ $input.val(v);
226
+ hideResultsNow();
227
+ $input.trigger("result", [selected.data, selected.value]);
228
+ return true;
229
+ }
230
+
231
+ function onChange(crap, skipPrevCheck) {
232
+ if( lastKeyPressCode == KEY.DEL ) {
233
+ select.hide();
234
+ return;
235
+ }
236
+
237
+ var currentValue = $input.val();
238
+
239
+ if ( !skipPrevCheck && currentValue == previousValue )
240
+ return;
241
+
242
+ previousValue = currentValue;
243
+
244
+ currentValue = lastWord(currentValue);
245
+ if ( currentValue.length >= options.minChars) {
246
+ $input.addClass(options.loadingClass);
247
+ if (!options.matchCase)
248
+ currentValue = currentValue.toLowerCase();
249
+ request(currentValue, receiveData, hideResultsNow);
250
+ } else {
251
+ stopLoading();
252
+ select.hide();
253
+ }
254
+ };
255
+
256
+ function trimWords(value) {
257
+ if ( !value ) {
258
+ return [""];
259
+ }
260
+ var words = value.split( options.multipleSeparator );
261
+ var result = [];
262
+ $.each(words, function(i, value) {
263
+ if ( $.trim(value) )
264
+ result[i] = $.trim(value);
265
+ });
266
+ return result;
267
+ }
268
+
269
+ function lastWord(value) {
270
+ if ( !options.multiple )
271
+ return value;
272
+ var words = trimWords(value);
273
+ return words[words.length - 1];
274
+ }
275
+
276
+ // fills in the input box w/the first match (assumed to be the best match)
277
+ // q: the term entered
278
+ // sValue: the first matching result
279
+ function autoFill(q, sValue){
280
+ // autofill in the complete box w/the first match as long as the user hasn't entered in more data
281
+ // if the last user key pressed was backspace, don't autofill
282
+ if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
283
+ // fill in the value (keep the case the user has typed)
284
+ $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
285
+ // select the portion of the value not typed by the user (so the next character will erase)
286
+ $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
287
+ }
288
+ };
289
+
290
+ function hideResults() {
291
+ clearTimeout(timeout);
292
+ timeout = setTimeout(hideResultsNow, 200);
293
+ };
294
+
295
+ function hideResultsNow() {
296
+ var wasVisible = select.visible();
297
+ select.hide();
298
+ clearTimeout(timeout);
299
+ stopLoading();
300
+ if (options.mustMatch) {
301
+ // call search and run callback
302
+ $input.search(
303
+ function (result){
304
+ // if no value found, clear the input box
305
+ if( !result ) {
306
+ if (options.multiple) {
307
+ var words = trimWords($input.val()).slice(0, -1);
308
+ $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
309
+ }
310
+ else
311
+ $input.val( "" );
312
+ }
313
+ }
314
+ );
315
+ }
316
+ if (wasVisible)
317
+ // position cursor at end of input field
318
+ $.Autocompleter.Selection(input, input.value.length, input.value.length);
319
+ };
320
+
321
+ function receiveData(q, data) {
322
+ if ( data && data.length ) {
323
+ stopLoading();
324
+ select.display(data, q);
325
+ autoFill(q, data[0].value);
326
+ select.show();
327
+ } else {
328
+ hideResultsNow();
329
+ }
330
+ };
331
+
332
+ function request(term, success, failure) {
333
+ if (!options.matchCase)
334
+ term = term.toLowerCase();
335
+ var data = cache.load(term);
336
+ // recieve the cached data
337
+ if (data && data.length) {
338
+ success(term, data);
339
+ // if an AJAX url has been supplied, try loading the data now
340
+ } else if( (typeof options.url == "string") && (options.url.length > 0) ){
341
+
342
+ var extraParams = {
343
+ timestamp: +new Date()
344
+ };
345
+ $.each(options.extraParams, function(key, param) {
346
+ extraParams[key] = typeof param == "function" ? param() : param;
347
+ });
348
+
349
+ $.ajax({
350
+ // try to leverage ajaxQueue plugin to abort previous requests
351
+ mode: "abort",
352
+ // limit abortion to this input
353
+ port: "autocomplete" + input.name,
354
+ dataType: options.dataType,
355
+ url: options.url,
356
+ data: $.extend({
357
+ q: lastWord(term),
358
+ limit: options.max
359
+ }, extraParams),
360
+ success: function(data) {
361
+ var parsed = options.parse && options.parse(data) || parse(data);
362
+ cache.add(term, parsed);
363
+ success(term, parsed);
364
+ }
365
+ });
366
+ } else {
367
+ // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
368
+ select.emptyList();
369
+ failure(term);
370
+ }
371
+ };
372
+
373
+ function parse(data) {
374
+ var parsed = [];
375
+ var rows = data.split("\n");
376
+ for (var i=0; i < rows.length; i++) {
377
+ var row = $.trim(rows[i]);
378
+ if (row) {
379
+ row = row.split("|");
380
+ parsed[parsed.length] = {
381
+ data: row,
382
+ value: row[0],
383
+ result: options.formatResult && options.formatResult(row, row[0]) || row[0]
384
+ };
385
+ }
386
+ }
387
+ return parsed;
388
+ };
389
+
390
+ function stopLoading() {
391
+ $input.removeClass(options.loadingClass);
392
+ };
393
+
394
+ };
395
+
396
+ $.Autocompleter.defaults = {
397
+ inputClass: "ac_input",
398
+ resultsClass: "ac_results",
399
+ loadingClass: "ac_loading",
400
+ minChars: 1,
401
+ delay: 400,
402
+ matchCase: false,
403
+ matchSubset: true,
404
+ matchContains: false,
405
+ cacheLength: 10,
406
+ max: 100,
407
+ mustMatch: false,
408
+ extraParams: {},
409
+ selectFirst: true,
410
+ formatItem: function(row) { return row[0]; },
411
+ formatMatch: null,
412
+ autoFill: false,
413
+ width: 0,
414
+ multiple: false,
415
+ multipleSeparator: ", ",
416
+ highlight: function(value, term) {
417
+ return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
418
+ },
419
+ scroll: true,
420
+ scrollHeight: 180
421
+ };
422
+
423
+ $.Autocompleter.Cache = function(options) {
424
+
425
+ var data = {};
426
+ var length = 0;
427
+
428
+ function matchSubset(s, sub) {
429
+ if (!options.matchCase)
430
+ s = s.toLowerCase();
431
+ var i = s.indexOf(sub);
432
+ if (i == -1) return false;
433
+ return i == 0 || options.matchContains;
434
+ };
435
+
436
+ function add(q, value) {
437
+ if (length > options.cacheLength){
438
+ flush();
439
+ }
440
+ if (!data[q]){
441
+ length++;
442
+ }
443
+ data[q] = value;
444
+ }
445
+
446
+ function populate(){
447
+ if( !options.data ) return false;
448
+ // track the matches
449
+ var stMatchSets = {},
450
+ nullData = 0;
451
+
452
+ // no url was specified, we need to adjust the cache length to make sure it fits the local data store
453
+ if( !options.url ) options.cacheLength = 1;
454
+
455
+ // track all options for minChars = 0
456
+ stMatchSets[""] = [];
457
+
458
+ // loop through the array and create a lookup structure
459
+ for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
460
+ var rawValue = options.data[i];
461
+ // if rawValue is a string, make an array otherwise just reference the array
462
+ rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
463
+
464
+ var value = options.formatMatch(rawValue, i+1, options.data.length);
465
+ if ( value === false )
466
+ continue;
467
+
468
+ var firstChar = value.charAt(0).toLowerCase();
469
+ // if no lookup array for this character exists, look it up now
470
+ if( !stMatchSets[firstChar] )
471
+ stMatchSets[firstChar] = [];
472
+
473
+ // if the match is a string
474
+ var row = {
475
+ value: value,
476
+ data: rawValue,
477
+ result: options.formatResult && options.formatResult(rawValue) || value
478
+ };
479
+
480
+ // push the current match into the set list
481
+ stMatchSets[firstChar].push(row);
482
+
483
+ // keep track of minChars zero items
484
+ if ( nullData++ < options.max ) {
485
+ stMatchSets[""].push(row);
486
+ }
487
+ };
488
+
489
+ // add the data items to the cache
490
+ $.each(stMatchSets, function(i, value) {
491
+ // increase the cache size
492
+ options.cacheLength++;
493
+ // add to the cache
494
+ add(i, value);
495
+ });
496
+ }
497
+
498
+ // populate any existing data
499
+ setTimeout(populate, 25);
500
+
501
+ function flush(){
502
+ data = {};
503
+ length = 0;
504
+ }
505
+
506
+ return {
507
+ flush: flush,
508
+ add: add,
509
+ populate: populate,
510
+ load: function(q) {
511
+ if (!options.cacheLength || !length)
512
+ return null;
513
+ /*
514
+ * if dealing w/local data and matchContains than we must make sure
515
+ * to loop through all the data collections looking for matches
516
+ */
517
+ if( !options.url && options.matchContains ){
518
+ // track all matches
519
+ var csub = [];
520
+ // loop through all the data grids for matches
521
+ for( var k in data ){
522
+ // don't search through the stMatchSets[""] (minChars: 0) cache
523
+ // this prevents duplicates
524
+ if( k.length > 0 ){
525
+ var c = data[k];
526
+ $.each(c, function(i, x) {
527
+ // if we've got a match, add it to the array
528
+ if (matchSubset(x.value, q)) {
529
+ csub.push(x);
530
+ }
531
+ });
532
+ }
533
+ }
534
+ return csub;
535
+ } else
536
+ // if the exact item exists, use it
537
+ if (data[q]){
538
+ return data[q];
539
+ } else
540
+ if (options.matchSubset) {
541
+ for (var i = q.length - 1; i >= options.minChars; i--) {
542
+ var c = data[q.substr(0, i)];
543
+ if (c) {
544
+ var csub = [];
545
+ $.each(c, function(i, x) {
546
+ if (matchSubset(x.value, q)) {
547
+ csub[csub.length] = x;
548
+ }
549
+ });
550
+ return csub;
551
+ }
552
+ }
553
+ }
554
+ return null;
555
+ }
556
+ };
557
+ };
558
+
559
+ $.Autocompleter.Select = function (options, input, select, config) {
560
+ var CLASSES = {
561
+ ACTIVE: "ac_over"
562
+ };
563
+
564
+ var listItems,
565
+ active = -1,
566
+ data,
567
+ term = "",
568
+ needsInit = true,
569
+ element,
570
+ list;
571
+
572
+ // Create results
573
+ function init() {
574
+ if (!needsInit)
575
+ return;
576
+ element = $("<div/>")
577
+ .hide()
578
+ .addClass(options.resultsClass)
579
+ .css("position", "absolute")
580
+ .appendTo(document.body);
581
+
582
+ list = $("<ul/>").appendTo(element).mouseover( function(event) {
583
+ if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
584
+ active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
585
+ $(target(event)).addClass(CLASSES.ACTIVE);
586
+ }
587
+ }).click(function(event) {
588
+ $(target(event)).addClass(CLASSES.ACTIVE);
589
+ select();
590
+ // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
591
+ input.focus();
592
+ return false;
593
+ }).mousedown(function() {
594
+ config.mouseDownOnSelect = true;
595
+ }).mouseup(function() {
596
+ config.mouseDownOnSelect = false;
597
+ });
598
+
599
+ if( options.width > 0 )
600
+ element.css("width", options.width);
601
+
602
+ needsInit = false;
603
+ }
604
+
605
+ function target(event) {
606
+ var element = event.target;
607
+ while(element && element.tagName != "LI")
608
+ element = element.parentNode;
609
+ // more fun with IE, sometimes event.target is empty, just ignore it then
610
+ if(!element)
611
+ return [];
612
+ return element;
613
+ }
614
+
615
+ function moveSelect(step) {
616
+ listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
617
+ movePosition(step);
618
+ var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
619
+ if(options.scroll) {
620
+ var offset = 0;
621
+ listItems.slice(0, active).each(function() {
622
+ offset += this.offsetHeight;
623
+ });
624
+ if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
625
+ list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
626
+ } else if(offset < list.scrollTop()) {
627
+ list.scrollTop(offset);
628
+ }
629
+ }
630
+ };
631
+
632
+ function movePosition(step) {
633
+ active += step;
634
+ if (active < 0) {
635
+ active = listItems.size() - 1;
636
+ } else if (active >= listItems.size()) {
637
+ active = 0;
638
+ }
639
+ }
640
+
641
+ function limitNumberOfItems(available) {
642
+ return options.max && options.max < available
643
+ ? options.max
644
+ : available;
645
+ }
646
+
647
+ function fillList() {
648
+ list.empty();
649
+ var max = limitNumberOfItems(data.length);
650
+ for (var i=0; i < max; i++) {
651
+ if (!data[i])
652
+ continue;
653
+ var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
654
+ if ( formatted === false )
655
+ continue;
656
+ var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
657
+ $.data(li, "ac_data", data[i]);
658
+ }
659
+ listItems = list.find("li");
660
+ if ( options.selectFirst ) {
661
+ listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
662
+ active = 0;
663
+ }
664
+ // apply bgiframe if available
665
+ if ( $.fn.bgiframe )
666
+ list.bgiframe();
667
+ }
668
+
669
+ return {
670
+ display: function(d, q) {
671
+ init();
672
+ data = d;
673
+ term = q;
674
+ fillList();
675
+ },
676
+ next: function() {
677
+ moveSelect(1);
678
+ },
679
+ prev: function() {
680
+ moveSelect(-1);
681
+ },
682
+ pageUp: function() {
683
+ if (active != 0 && active - 8 < 0) {
684
+ moveSelect( -active );
685
+ } else {
686
+ moveSelect(-8);
687
+ }
688
+ },
689
+ pageDown: function() {
690
+ if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
691
+ moveSelect( listItems.size() - 1 - active );
692
+ } else {
693
+ moveSelect(8);
694
+ }
695
+ },
696
+ hide: function() {
697
+ element && element.hide();
698
+ listItems && listItems.removeClass(CLASSES.ACTIVE);
699
+ active = -1;
700
+ },
701
+ visible : function() {
702
+ return element && element.is(":visible");
703
+ },
704
+ current: function() {
705
+ return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
706
+ },
707
+ show: function() {
708
+ var offset = $(input).offset();
709
+ element.css({
710
+ width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
711
+ top: offset.top + input.offsetHeight,
712
+ left: offset.left
713
+ }).show();
714
+ if(options.scroll) {
715
+ list.scrollTop(0);
716
+ list.css({
717
+ maxHeight: options.scrollHeight,
718
+ overflow: 'auto'
719
+ });
720
+
721
+ if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
722
+ var listHeight = 0;
723
+ listItems.each(function() {
724
+ listHeight += this.offsetHeight;
725
+ });
726
+ var scrollbarsVisible = listHeight > options.scrollHeight;
727
+ list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
728
+ if (!scrollbarsVisible) {
729
+ // IE doesn't recalculate width when scrollbar disappears
730
+ listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
731
+ }
732
+ }
733
+
734
+ }
735
+ },
736
+ selected: function() {
737
+ var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
738
+ return selected && selected.length && $.data(selected[0], "ac_data");
739
+ },
740
+ emptyList: function (){
741
+ list && list.empty();
742
+ },
743
+ unbind: function() {
744
+ element && element.remove();
745
+ }
746
+ };
747
+ };
748
+
749
+ $.Autocompleter.Selection = function(field, start, end) {
750
+ if( field.createTextRange ){
751
+ var selRange = field.createTextRange();
752
+ selRange.collapse(true);
753
+ selRange.moveStart("character", start);
754
+ selRange.moveEnd("character", end);
755
+ selRange.select();
756
+ } else if( field.setSelectionRange ){
757
+ field.setSelectionRange(start, end);
758
+ } else {
759
+ if( field.selectionStart ){
760
+ field.selectionStart = start;
761
+ field.selectionEnd = end;
762
+ }
763
+ }
764
+ field.focus();
765
+ };
766
+
767
+ })(jQuery);