Skip to content
This repository
Browse code

Update script.aculo.us to 1.5_rc2, and Prototype to 1.4.0_pre7

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2386 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 516dc2c0f16cf187f981b5e8648a7f7f1b31d190 1 parent dd21e9a
Thomas Fuchs authored September 28, 2005
7  actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -51,7 +51,12 @@ def javascript_path(source)
51 51
       #     <script type="text/javascript" src="/javascripts/dragdrop.js"></script>      
52 52
       def javascript_include_tag(*sources)
53 53
         options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
54  
-        sources = ['prototype', 'effects', 'controls', 'dragdrop'] if sources.first == :defaults
  54
+        if sources.first == :defaults
  55
+          sources = ['prototype', 'scriptaculous']
  56
+          if defined?(RAILS_ROOT) and File.exists?("#{RAILS_ROOT}/public/javascripts/application.js")
  57
+            sources << 'application' 
  58
+          end
  59
+        end
55 60
         sources.collect { |source|
56 61
           source = javascript_path(source)        
57 62
           content_tag("script", "", { "type" => "text/javascript", "src" => source }.merge(options))
38  actionpack/lib/action_view/helpers/java_script_macros_helper.rb
@@ -74,9 +74,8 @@ def in_place_editor_field(object, method, tag_options = {}, in_place_editor_opti
74 74
       # input field.
75 75
       # 
76 76
       # Required +options+ are:
77  
-      # <tt>:url</tt>::       Specifies the DOM ID of the element whose
78  
-      #                       innerHTML should be updated with the autocomplete
79  
-      #                       entries returned by XMLHttpRequest.
  77
+      # <tt>:url</tt>::       URL to call for autocompletion results
  78
+      #                       in url_for format.
80 79
       # 
81 80
       # Addtional +options+ are:
82 81
       # <tt>:update</tt>::    Specifies the DOM ID of the element whose 
@@ -130,10 +129,41 @@ def auto_complete_result(entries, field, phrase = nil)
130 129
       # See the RDoc on ActionController::AutoComplete to learn more about this.
131 130
       def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})
132 131
         (completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
133  
-        text_field(object, method, { :autocomplete => "off" }.merge!(tag_options)) +
  132
+        text_field(object, method, tag_options) +
134 133
         content_tag("div", "", :id => "#{object}_#{method}_auto_complete", :class => "auto_complete") +
135 134
         auto_complete_field("#{object}_#{method}", { :url => { :action => "auto_complete_for_#{object}_#{method}" } }.update(completion_options))
136 135
       end
  136
+      
  137
+      private
  138
+        def auto_complete_stylesheet
  139
+          content_tag("style", <<-EOT
  140
+            div.auto_complete {
  141
+              width: 350px;
  142
+              background: #fff;
  143
+            }
  144
+            div.auto_complete ul {
  145
+              border:1px solid #888;
  146
+              margin:0;
  147
+              padding:0;
  148
+              width:100%;
  149
+              list-style-type:none;
  150
+            }
  151
+            div.auto_complete ul li {
  152
+              margin:0;
  153
+              padding:3px;
  154
+            }
  155
+            div.auto_complete ul li.selected { 
  156
+              background-color: #ffb; 
  157
+            }
  158
+            div.auto_complete ul strong.highlight { 
  159
+              color: #800; 
  160
+              margin:0;
  161
+              padding:0;
  162
+            }
  163
+          EOT
  164
+          )
  165
+        end
  166
+      
137 167
     end
138 168
   end
139 169
 end
28  actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -498,34 +498,6 @@ def build_callbacks(options)
498 498
         callbacks
499 499
       end
500 500
       
501  
-      def auto_complete_stylesheet
502  
-        content_tag("style", <<-EOT
503  
-          div.auto_complete {
504  
-            width: 350px;
505  
-            background: #fff;
506  
-          }
507  
-          div.auto_complete ul {
508  
-            border:1px solid #888;
509  
-            margin:0;
510  
-            padding:0;
511  
-            width:100%;
512  
-            list-style-type:none;
513  
-          }
514  
-          div.auto_complete ul li {
515  
-            margin:0;
516  
-            padding:3px;
517  
-          }
518  
-          div.auto_complete ul li.selected { 
519  
-            background-color: #ffb; 
520  
-          }
521  
-          div.auto_complete ul strong.highlight { 
522  
-            color: #800; 
523  
-            margin:0;
524  
-            padding:0;
525  
-          }
526  
-        EOT
527  
-        )
528  
-      end
529 501
     end
530 502
     
531 503
     JavascriptHelper = JavaScriptHelper unless const_defined? :JavascriptHelper
575  actionpack/lib/action_view/helpers/javascripts/controls.js
... ...
@@ -1,41 +1,12 @@
1 1
 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 2
 //           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
  3
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
  4
+// Contributors:
  5
+//  Richard Livsey
  6
+//  Rahul Bhargava
  7
+//  Rob Wills
3 8
 // 
4  
-// Permission is hereby granted, free of charge, to any person obtaining
5  
-// a copy of this software and associated documentation files (the
6  
-// "Software"), to deal in the Software without restriction, including
7  
-// without limitation the rights to use, copy, modify, merge, publish,
8  
-// distribute, sublicense, and/or sell copies of the Software, and to
9  
-// permit persons to whom the Software is furnished to do so, subject to
10  
-// the following conditions:
11  
-// 
12  
-// The above copyright notice and this permission notice shall be
13  
-// included in all copies or substantial portions of the Software.
14  
-// 
15  
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  
-
23  
-Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
24  
-  var children = $(element).childNodes;
25  
-  var text     = "";
26  
-  var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
27  
-  
28  
-  for (var i = 0; i < children.length; i++) {
29  
-    if(children[i].nodeType==3) {
30  
-      text+=children[i].nodeValue;
31  
-    } else {
32  
-      if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
33  
-        text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
34  
-    }
35  
-  }
36  
-  
37  
-  return text;
38  
-}
  9
+// See scriptaculous.js for full license.
39 10
 
40 11
 // Autocompleter.Base handles all the autocompletion functionality 
41 12
 // that's independent of the data source for autocompletion. This
@@ -46,7 +17,7 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
46 17
 // a getUpdatedChoices function that will be invoked every time
47 18
 // the text inside the monitored textbox changes. This method 
48 19
 // should get the text for which to provide autocompletion by
49  
-// invoking this.getEntry(), NOT by directly accessing
  20
+// invoking this.getToken(), NOT by directly accessing
50 21
 // this.element.value. This is to allow incremental tokenized
51 22
 // autocompletion. Specific auto-completion logic (AJAX, etc)
52 23
 // belongs in getUpdatedChoices.
@@ -57,7 +28,7 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
57 28
 // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
58 29
 // will incrementally autocomplete with a comma as the token.
59 30
 // Additionally, ',' in the above example can be replaced with
60  
-// a token array, e.g. { tokens: new Array (',', '\n') } which
  31
+// a token array, e.g. { tokens: [',', '\n'] } which
61 32
 // enables autocompletion on multiple tokens. This is most 
62 33
 // useful when one of the tokens is \n (a newline), as it 
63 34
 // allows smart autocompletion after linebreaks.
@@ -65,57 +36,54 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
65 36
 var Autocompleter = {}
66 37
 Autocompleter.Base = function() {};
67 38
 Autocompleter.Base.prototype = {
68  
-  base_initialize: function(element, update, options) {
  39
+  baseInitialize: function(element, update, options) {
69 40
     this.element     = $(element); 
70 41
     this.update      = $(update);  
71  
-    this.has_focus   = false; 
  42
+    this.hasFocus    = false; 
72 43
     this.changed     = false; 
73 44
     this.active      = false; 
74 45
     this.index       = 0;     
75  
-    this.entry_count = 0;
  46
+    this.entryCount  = 0;
76 47
 
77 48
     if (this.setOptions)
78 49
       this.setOptions(options);
79 50
     else
80  
-      this.options = {}
81  
-     
82  
-    this.options.tokens       = this.options.tokens || new Array();
  51
+      this.options = options || {};
  52
+
  53
+    this.options.paramName    = this.options.paramName || this.element.name;
  54
+    this.options.tokens       = this.options.tokens || [];
83 55
     this.options.frequency    = this.options.frequency || 0.4;
84  
-    this.options.min_chars    = this.options.min_chars || 1;
  56
+    this.options.minChars     = this.options.minChars || 1;
85 57
     this.options.onShow       = this.options.onShow || 
86 58
     function(element, update){ 
87 59
       if(!update.style.position || update.style.position=='absolute') {
88 60
         update.style.position = 'absolute';
89  
-          var offsets = Position.cumulativeOffset(element);
90  
-          update.style.left = offsets[0] + 'px';
91  
-          update.style.top  = (offsets[1] + element.offsetHeight) + 'px';
92  
-          update.style.width = element.offsetWidth + 'px';
  61
+        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
93 62
       }
94 63
       new Effect.Appear(update,{duration:0.15});
95 64
     };
96 65
     this.options.onHide = this.options.onHide || 
97 66
     function(element, update){ new Effect.Fade(update,{duration:0.15}) };
98  
-    
99  
-    if(this.options.indicator)
100  
-      this.indicator = $(this.options.indicator);
101 67
 
102 68
     if (typeof(this.options.tokens) == 'string') 
103 69
       this.options.tokens = new Array(this.options.tokens);
104  
-       
  70
+
105 71
     this.observer = null;
106 72
     
  73
+    this.element.setAttribute('autocomplete','off');
  74
+
107 75
     Element.hide(this.update);
108  
-    
  76
+
109 77
     Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
110 78
     Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
111 79
   },
112 80
 
113 81
   show: function() {
114  
-    if(this.update.style.display=='none') this.options.onShow(this.element, this.update);
115  
-    if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && this.update.style.position=='absolute') {
  82
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
  83
+    if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
116 84
       new Insertion.After(this.update, 
117 85
        '<iframe id="' + this.update.id + '_iefix" '+
118  
-       'style="display:none;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
  86
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
119 87
        'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
120 88
       this.iefix = $(this.update.id+'_iefix');
121 89
     }
@@ -126,18 +94,18 @@ Autocompleter.Base.prototype = {
126 94
       Element.show(this.iefix);
127 95
     }
128 96
   },
129  
-  
  97
+
130 98
   hide: function() {
131  
-    if(this.update.style.display=='') this.options.onHide(this.element, this.update);
  99
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
132 100
     if(this.iefix) Element.hide(this.iefix);
133 101
   },
134  
-  
  102
+
135 103
   startIndicator: function() {
136  
-    if(this.indicator) Element.show(this.indicator);
  104
+    if(this.options.indicator) Element.show(this.options.indicator);
137 105
   },
138  
-  
  106
+
139 107
   stopIndicator: function() {
140  
-    if(this.indicator) Element.hide(this.indicator);
  108
+    if(this.options.indicator) Element.hide(this.options.indicator);
141 109
   },
142 110
 
143 111
   onKeyPress: function(event) {
@@ -145,22 +113,23 @@ Autocompleter.Base.prototype = {
145 113
       switch(event.keyCode) {
146 114
        case Event.KEY_TAB:
147 115
        case Event.KEY_RETURN:
148  
-         this.select_entry();
  116
+         this.selectEntry();
149 117
          Event.stop(event);
150 118
        case Event.KEY_ESC:
151 119
          this.hide();
152 120
          this.active = false;
  121
+         Event.stop(event);
153 122
          return;
154 123
        case Event.KEY_LEFT:
155 124
        case Event.KEY_RIGHT:
156 125
          return;
157 126
        case Event.KEY_UP:
158  
-         this.mark_previous();
  127
+         this.markPrevious();
159 128
          this.render();
160 129
          if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
161 130
          return;
162 131
        case Event.KEY_DOWN:
163  
-         this.mark_next();
  132
+         this.markNext();
164 133
          this.render();
165 134
          if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
166 135
          return;
@@ -168,15 +137,15 @@ Autocompleter.Base.prototype = {
168 137
      else 
169 138
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN) 
170 139
         return;
171  
-    
  140
+
172 141
     this.changed = true;
173  
-    this.has_focus = true;
174  
-    
  142
+    this.hasFocus = true;
  143
+
175 144
     if(this.observer) clearTimeout(this.observer);
176 145
       this.observer = 
177 146
         setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
178 147
   },
179  
-  
  148
+
180 149
   onHover: function(event) {
181 150
     var element = Event.findElement(event, 'LI');
182 151
     if(this.index != element.autocompleteIndex) 
@@ -190,92 +159,97 @@ Autocompleter.Base.prototype = {
190 159
   onClick: function(event) {
191 160
     var element = Event.findElement(event, 'LI');
192 161
     this.index = element.autocompleteIndex;
193  
-    this.select_entry();
194  
-    Event.stop(event);
  162
+    this.selectEntry();
  163
+    this.hide();
195 164
   },
196 165
   
197 166
   onBlur: function(event) {
198 167
     // needed to make click events working
199 168
     setTimeout(this.hide.bind(this), 250);
200  
-    this.has_focus = false;
  169
+    this.hasFocus = false;
201 170
     this.active = false;     
202 171
   }, 
203 172
   
204 173
   render: function() {
205  
-    if(this.entry_count > 0) {
206  
-      for (var i = 0; i < this.entry_count; i++)
  174
+    if(this.entryCount > 0) {
  175
+      for (var i = 0; i < this.entryCount; i++)
207 176
         this.index==i ? 
208  
-          Element.addClassName(this.get_entry(i),"selected") : 
209  
-          Element.removeClassName(this.get_entry(i),"selected");
210  
-        
211  
-      if(this.has_focus) { 
212  
-        if(this.get_current_entry().scrollIntoView) 
213  
-          this.get_current_entry().scrollIntoView(false);
  177
+          Element.addClassName(this.getEntry(i),"selected") : 
  178
+          Element.removeClassName(this.getEntry(i),"selected");
214 179
         
  180
+      if(this.hasFocus) { 
215 181
         this.show();
216 182
         this.active = true;
217 183
       }
218 184
     } else this.hide();
219 185
   },
220 186
   
221  
-  mark_previous: function() {
  187
+  markPrevious: function() {
222 188
     if(this.index > 0) this.index--
223  
-      else this.index = this.entry_count-1;
  189
+      else this.index = this.entryCcount-1;
224 190
   },
225 191
   
226  
-  mark_next: function() {
227  
-    if(this.index < this.entry_count-1) this.index++
  192
+  markNext: function() {
  193
+    if(this.index < this.entryCount-1) this.index++
228 194
       else this.index = 0;
229 195
   },
230 196
   
231  
-  get_entry: function(index) {
  197
+  getEntry: function(index) {
232 198
     return this.update.firstChild.childNodes[index];
233 199
   },
234 200
   
235  
-  get_current_entry: function() {
236  
-    return this.get_entry(this.index);
  201
+  getCurrentEntry: function() {
  202
+    return this.getEntry(this.index);
237 203
   },
238 204
   
239  
-  select_entry: function() {
  205
+  selectEntry: function() {
240 206
     this.active = false;
241  
-    value = Element.collectTextNodesIgnoreClass(this.get_current_entry(), 'informal').unescapeHTML();
242  
-    this.updateElement(value);
243  
-    this.element.focus();
  207
+    this.updateElement(this.getCurrentEntry());
244 208
   },
245 209
 
246  
-  updateElement: function(value) {
247  
-    var last_token_pos = this.findLastToken();
248  
-    if (last_token_pos != -1) {
249  
-      var new_value = this.element.value.substr(0, last_token_pos + 1);
250  
-      var whitespace = this.element.value.substr(last_token_pos + 1).match(/^\s+/);
  210
+  updateElement: function(selectedElement) {
  211
+    if (this.options.updateElement) {
  212
+      this.options.updateElement(selectedElement);
  213
+      return;
  214
+    }
  215
+
  216
+    var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
  217
+    var lastTokenPos = this.findLastToken();
  218
+    if (lastTokenPos != -1) {
  219
+      var newValue = this.element.value.substr(0, lastTokenPos + 1);
  220
+      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
251 221
       if (whitespace)
252  
-        new_value += whitespace[0];
253  
-      this.element.value = new_value + value;
  222
+        newValue += whitespace[0];
  223
+      this.element.value = newValue + value;
254 224
     } else {
255 225
       this.element.value = value;
256  
-    } 
  226
+    }
  227
+    this.element.focus();
  228
+    
  229
+    if (this.options.afterUpdateElement)
  230
+      this.options.afterUpdateElement(this.element, selectedElement);
257 231
   },
258  
-  
  232
+
259 233
   updateChoices: function(choices) {
260  
-    if(!this.changed && this.has_focus) {
  234
+    if(!this.changed && this.hasFocus) {
261 235
       this.update.innerHTML = choices;
262 236
       Element.cleanWhitespace(this.update);
263 237
       Element.cleanWhitespace(this.update.firstChild);
264 238
 
265 239
       if(this.update.firstChild && this.update.firstChild.childNodes) {
266  
-        this.entry_count = 
  240
+        this.entryCount = 
267 241
           this.update.firstChild.childNodes.length;
268  
-        for (var i = 0; i < this.entry_count; i++) {
269  
-          entry = this.get_entry(i);
  242
+        for (var i = 0; i < this.entryCount; i++) {
  243
+          var entry = this.getEntry(i);
270 244
           entry.autocompleteIndex = i;
271 245
           this.addObservers(entry);
272 246
         }
273 247
       } else { 
274  
-        this.entry_count = 0;
  248
+        this.entryCount = 0;
275 249
       }
276  
-      
  250
+
277 251
       this.stopIndicator();
278  
-      
  252
+
279 253
       this.index = 0;
280 254
       this.render();
281 255
     }
@@ -288,7 +262,7 @@ Autocompleter.Base.prototype = {
288 262
 
289 263
   onObserverEvent: function() {
290 264
     this.changed = false;   
291  
-    if(this.getEntry().length>=this.options.min_chars) {
  265
+    if(this.getToken().length>=this.options.minChars) {
292 266
       this.startIndicator();
293 267
       this.getUpdatedChoices();
294 268
     } else {
@@ -297,58 +271,56 @@ Autocompleter.Base.prototype = {
297 271
     }
298 272
   },
299 273
 
300  
-  getEntry: function() {
301  
-    var token_pos = this.findLastToken();
302  
-    if (token_pos != -1)
303  
-      var ret = this.element.value.substr(token_pos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
  274
+  getToken: function() {
  275
+    var tokenPos = this.findLastToken();
  276
+    if (tokenPos != -1)
  277
+      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
304 278
     else
305 279
       var ret = this.element.value;
306  
-    
  280
+
307 281
     return /\n/.test(ret) ? '' : ret;
308 282
   },
309 283
 
310 284
   findLastToken: function() {
311  
-    var last_token_pos = -1;
  285
+    var lastTokenPos = -1;
312 286
 
313 287
     for (var i=0; i<this.options.tokens.length; i++) {
314  
-      var this_token_pos = this.element.value.lastIndexOf(this.options.tokens[i]);
315  
-      if (this_token_pos > last_token_pos)
316  
-        last_token_pos = this_token_pos;
  288
+      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
  289
+      if (thisTokenPos > lastTokenPos)
  290
+        lastTokenPos = thisTokenPos;
317 291
     }
318  
-    return last_token_pos;
  292
+    return lastTokenPos;
319 293
   }
320 294
 }
321 295
 
322 296
 Ajax.Autocompleter = Class.create();
323  
-Ajax.Autocompleter.prototype = Object.extend(new Autocompleter.Base(), 
324  
-Object.extend(new Ajax.Base(), {
  297
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
325 298
   initialize: function(element, update, url, options) {
326  
-	  this.base_initialize(element, update, options);
  299
+	  this.baseInitialize(element, update, options);
327 300
     this.options.asynchronous  = true;
328  
-    this.options.onComplete    = this.onComplete.bind(this)
329  
-    this.options.method        = 'post';
  301
+    this.options.onComplete    = this.onComplete.bind(this);
330 302
     this.options.defaultParams = this.options.parameters || null;
331 303
     this.url                   = url;
332 304
   },
333  
-  
  305
+
334 306
   getUpdatedChoices: function() {
335  
-    entry = encodeURIComponent(this.element.name) + '=' + 
336  
-      encodeURIComponent(this.getEntry());
337  
-      
  307
+    entry = encodeURIComponent(this.options.paramName) + '=' + 
  308
+      encodeURIComponent(this.getToken());
  309
+
338 310
     this.options.parameters = this.options.callback ?
339 311
       this.options.callback(this.element, entry) : entry;
340  
-        
  312
+
341 313
     if(this.options.defaultParams) 
342 314
       this.options.parameters += '&' + this.options.defaultParams;
343  
-    
  315
+
344 316
     new Ajax.Request(this.url, this.options);
345 317
   },
346  
-  
  318
+
347 319
   onComplete: function(request) {
348 320
     this.updateChoices(request.responseText);
349 321
   }
350 322
 
351  
-}));
  323
+});
352 324
 
353 325
 // The local array autocompleter. Used when you'd prefer to
354 326
 // inject an array of autocompletion options into the page, rather
@@ -362,22 +334,22 @@ Object.extend(new Ajax.Base(), {
362 334
 // Extra local autocompletion options:
363 335
 // - choices - How many autocompletion choices to offer
364 336
 //
365  
-// - partial_search - If false, the autocompleter will match entered
  337
+// - partialSearch - If false, the autocompleter will match entered
366 338
 //                    text only at the beginning of strings in the 
367 339
 //                    autocomplete array. Defaults to true, which will
368 340
 //                    match text at the beginning of any *word* in the
369 341
 //                    strings in the autocomplete array. If you want to
370 342
 //                    search anywhere in the string, additionally set
371  
-//                    the option full_search to true (default: off).
  343
+//                    the option fullSearch to true (default: off).
372 344
 //
373  
-// - full_search - Search anywhere in autocomplete array strings.
  345
+// - fullSsearch - Search anywhere in autocomplete array strings.
374 346
 //
375  
-// - partial_chars - How many characters to enter before triggering
376  
-//                   a partial match (unlike min_chars, which defines
  347
+// - partialChars - How many characters to enter before triggering
  348
+//                   a partial match (unlike minChars, which defines
377 349
 //                   how many characters are required to do any match
378 350
 //                   at all). Defaults to 2.
379 351
 //
380  
-// - ignore_case - Whether to ignore case when autocompleting.
  352
+// - ignoreCase - Whether to ignore case when autocompleting.
381 353
 //                 Defaults to true.
382 354
 //
383 355
 // It's possible to pass in a custom function as the 'selector' 
@@ -388,7 +360,7 @@ Object.extend(new Ajax.Base(), {
388 360
 Autocompleter.Local = Class.create();
389 361
 Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
390 362
   initialize: function(element, update, array, options) {
391  
-    this.base_initialize(element, update, options);
  363
+    this.baseInitialize(element, update, options);
392 364
     this.options.array = array;
393 365
   },
394 366
 
@@ -399,41 +371,42 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
399 371
   setOptions: function(options) {
400 372
     this.options = Object.extend({
401 373
       choices: 10,
402  
-      partial_search: true,
403  
-      partial_chars: 2,
404  
-      ignore_case: true,
405  
-      full_search: false,
  374
+      partialSearch: true,
  375
+      partialChars: 2,
  376
+      ignoreCase: true,
  377
+      fullSearch: false,
406 378
       selector: function(instance) {
407  
-        var ret       = new Array(); // Beginning matches
408  
-        var partial   = new Array(); // Inside matches
409  
-        var entry     = instance.getEntry();
  379
+        var ret       = []; // Beginning matches
  380
+        var partial   = []; // Inside matches
  381
+        var entry     = instance.getToken();
410 382
         var count     = 0;
411  
-        
  383
+
412 384
         for (var i = 0; i < instance.options.array.length &&  
413  
-            ret.length < instance.options.choices ; i++) { 
  385
+          ret.length < instance.options.choices ; i++) { 
  386
+
414 387
           var elem = instance.options.array[i];
415  
-          var found_pos = instance.options.ignore_case ? 
  388
+          var foundPos = instance.options.ignoreCase ? 
416 389
             elem.toLowerCase().indexOf(entry.toLowerCase()) : 
417 390
             elem.indexOf(entry);
418 391
 
419  
-          while (found_pos != -1) {
420  
-            if (found_pos == 0 && elem.length != entry.length) { 
  392
+          while (foundPos != -1) {
  393
+            if (foundPos == 0 && elem.length != entry.length) { 
421 394
               ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
422 395
                 elem.substr(entry.length) + "</li>");
423 396
               break;
424  
-            } else if (entry.length >= instance.options.partial_chars && 
425  
-              instance.options.partial_search && found_pos != -1) {
426  
-              if (instance.options.full_search || /\s/.test(elem.substr(found_pos-1,1))) {
427  
-                partial.push("<li>" + elem.substr(0, found_pos) + "<strong>" +
428  
-                  elem.substr(found_pos, entry.length) + "</strong>" + elem.substr(
429  
-                  found_pos + entry.length) + "</li>");
  397
+            } else if (entry.length >= instance.options.partialChars && 
  398
+              instance.options.partialSearch && foundPos != -1) {
  399
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
  400
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
  401
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
  402
+                  foundPos + entry.length) + "</li>");
430 403
                 break;
431 404
               }
432 405
             }
433 406
 
434  
-            found_pos = instance.options.ignore_case ? 
435  
-              elem.toLowerCase().indexOf(entry.toLowerCase(), found_pos + 1) : 
436  
-              elem.indexOf(entry, found_pos + 1);
  407
+            foundPos = instance.options.ignoreCase ? 
  408
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
  409
+              elem.indexOf(entry, foundPos + 1);
437 410
 
438 411
           }
439 412
         }
@@ -444,3 +417,287 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
444 417
     }, options || {});
445 418
   }
446 419
 });
  420
+
  421
+// AJAX in-place editor
  422
+//
  423
+// The constructor takes three parameters. The first is the element
  424
+// that should support in-place editing. The second is the url to submit
  425
+// the changed value to. The server should respond with the updated
  426
+// value (the server might have post-processed it or validation might
  427
+// have prevented it from changing). The third is a hash of options.
  428
+//
  429
+// Supported options are (all are optional and have sensible defaults):
  430
+// - okText - The text of the submit button that submits the changed value
  431
+//            to the server (default: "ok")
  432
+// - cancelText - The text of the link that cancels editing (default: "cancel")
  433
+// - savingText - The text being displayed as the AJAX engine communicates
  434
+//                with the server (default: "Saving...")
  435
+// - formId - The id given to the <form> element
  436
+//            (default: the id of the element to edit plus '-inplaceeditor')
  437
+
  438
+Ajax.InPlaceEditor = Class.create();
  439
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
  440
+Ajax.InPlaceEditor.prototype = {
  441
+  initialize: function(element, url, options) {
  442
+    this.url = url;
  443
+    this.element = $(element);
  444
+
  445
+    this.options = Object.extend({
  446
+      okText: "ok",
  447
+      cancelText: "cancel",
  448
+      savingText: "Saving...",
  449
+      clickToEditText: "Click to edit",
  450
+      okText: "ok",
  451
+      rows: 1,
  452
+      onComplete: function(transport, element) {
  453
+        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
  454
+      },
  455
+      onFailure: function(transport) {
  456
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
  457
+      },
  458
+      callback: function(form) {
  459
+        return Form.serialize(form);
  460
+      },
  461
+      handleLineBreaks: true,
  462
+      loadingText: 'Loading...',
  463
+      savingClassName: 'inplaceeditor-saving',
  464
+      formClassName: 'inplaceeditor-form',
  465
+      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
  466
+      highlightendcolor: "#FFFFFF",
  467
+      externalControl:	null,
  468
+      ajaxOptions: {}
  469
+    }, options || {});
  470
+
  471
+    if(!this.options.formId && this.element.id) {
  472
+      this.options.formId = this.element.id + "-inplaceeditor";
  473
+      if ($(this.options.formId)) {
  474
+        // there's already a form with that name, don't specify an id
  475
+        this.options.formId = null;
  476
+      }
  477
+    }
  478
+    
  479
+    if (this.options.externalControl) {
  480
+      this.options.externalControl = $(this.options.externalControl);
  481
+    }
  482
+    
  483
+    this.originalBackground = Element.getStyle(this.element, 'background-color');
  484
+    if (!this.originalBackground) {
  485
+      this.originalBackground = "transparent";
  486
+    }
  487
+    
  488
+    this.element.title = this.options.clickToEditText;
  489
+    
  490
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
  491
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
  492
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
  493
+    Event.observe(this.element, 'click', this.onclickListener);
  494
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
  495
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
  496
+    if (this.options.externalControl) {
  497
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
  498
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
  499
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
  500
+    }
  501
+  },
  502
+  enterEditMode: function() {
  503
+    if (this.saving) return;
  504
+    if (this.editing) return;
  505
+    this.editing = true;
  506
+    this.onEnterEditMode();
  507
+    if (this.options.externalControl) {
  508
+      Element.hide(this.options.externalControl);
  509
+    }
  510
+    Element.hide(this.element);
  511
+    this.form = this.getForm();
  512
+    this.element.parentNode.insertBefore(this.form, this.element);
  513
+    Field.focus(this.editField);
  514
+    // stop the event to avoid a page refresh in Safari
  515
+    if (arguments.length > 1) {
  516
+      Event.stop(arguments[0]);
  517
+    }
  518
+  },
  519
+  getForm: function() {
  520
+    form = document.createElement("form");
  521
+    form.id = this.options.formId;
  522
+    Element.addClassName(form, this.options.formClassName)
  523
+    form.onsubmit = this.onSubmit.bind(this);
  524
+
  525
+    this.createEditField(form);
  526
+
  527
+    if (this.options.textarea) {
  528
+      var br = document.createElement("br");
  529
+      form.appendChild(br);
  530
+    }
  531
+
  532
+    okButton = document.createElement("input");
  533
+    okButton.type = "submit";
  534
+    okButton.value = this.options.okText;
  535
+    form.appendChild(okButton);
  536
+
  537
+    cancelLink = document.createElement("a");
  538
+    cancelLink.href = "#";
  539
+    cancelLink.appendChild(document.createTextNode(this.options.cancelText));
  540
+    cancelLink.onclick = this.onclickCancel.bind(this);
  541
+    form.appendChild(cancelLink);
  542
+    return form;
  543
+  },
  544
+  hasHTMLLineBreaks: function(string) {
  545
+    if (!this.options.handleLineBreaks) return false;
  546
+    return string.match(/<br/i) || string.match(/<p>/i);
  547
+  },
  548
+  convertHTMLLineBreaks: function(string) {
  549
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
  550
+  },
  551
+  createEditField: function(form) {
  552
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(this.getText())) {
  553
+      this.options.textarea = false;
  554
+      var textField = document.createElement("input");
  555
+      textField.type = "text";
  556
+      textField.name = "value";
  557
+      textField.value = this.getText();
  558
+      textField.style.backgroundColor = this.options.highlightcolor;
  559
+      var size = this.options.size || this.options.cols || 0;
  560
+      if (size != 0)
  561
+        textField.size = size;
  562
+      form.appendChild(textField);
  563
+      this.editField = textField;
  564
+    } else {
  565
+      this.options.textarea = true;
  566
+      var textArea = document.createElement("textarea");
  567
+      textArea.name = "value";
  568
+      textArea.value = this.convertHTMLLineBreaks(this.getText());
  569
+      textArea.rows = this.options.rows;
  570
+      textArea.cols = this.options.cols || 40;
  571
+      form.appendChild(textArea);
  572
+      this.editField = textArea;
  573
+    }
  574
+  },
  575
+  getText: function() {
  576
+    if (this.options.loadTextURL) {
  577
+      this.loadExternalText();
  578
+      return this.options.loadingText;
  579
+    } else {
  580
+      return this.element.innerHTML;
  581
+    }
  582
+  },
  583
+  loadExternalText: function() {
  584
+    new Ajax.Request(
  585
+      this.options.loadTextURL,
  586
+      {
  587
+        asynchronous: true,
  588
+        onComplete: this.onLoadedExternalText.bind(this)
  589
+      }
  590
+    );
  591
+  },
  592
+  onLoadedExternalText: function(transport) {
  593
+    this.form.value.value = transport.responseText.stripTags();
  594
+  },
  595
+  onclickCancel: function() {
  596
+    this.onComplete();
  597
+    this.leaveEditMode();
  598
+    return false;
  599
+  },
  600
+  onFailure: function(transport) {
  601
+    this.options.onFailure(transport);
  602
+    if (this.oldInnerHTML) {
  603
+      this.element.innerHTML = this.oldInnerHTML;
  604
+      this.oldInnerHTML = null;
  605
+    }
  606
+    return false;
  607
+  },
  608
+  onSubmit: function() {
  609
+    this.saving = true;
  610
+    new Ajax.Updater(
  611
+      { 
  612
+        success: this.element,
  613
+         // don't update on failure (this could be an option)
  614
+        failure: null
  615
+      },
  616
+      this.url,
  617
+      Object.extend({
  618
+        parameters: this.options.callback(this.form, this.editField.value),
  619
+        onComplete: this.onComplete.bind(this),
  620
+        onFailure: this.onFailure.bind(this)
  621
+      }, this.options.ajaxOptions)
  622
+    );
  623
+    this.onLoading();
  624
+    // stop the event to avoid a page refresh in Safari
  625
+    if (arguments.length > 1) {
  626
+      Event.stop(arguments[0]);
  627
+    }
  628
+    return false;
  629
+  },
  630
+  onLoading: function() {
  631
+    this.saving = true;
  632
+    this.removeForm();
  633
+    this.leaveHover();
  634
+    this.showSaving();
  635
+  },
  636
+  showSaving: function() {
  637
+    this.oldInnerHTML = this.element.innerHTML;
  638
+    this.element.innerHTML = this.options.savingText;
  639
+    Element.addClassName(this.element, this.options.savingClassName);
  640
+    this.element.style.backgroundColor = this.originalBackground;
  641
+    Element.show(this.element);
  642
+  },
  643
+  removeForm: function() {
  644
+    if(this.form) {
  645
+      Element.remove(this.form);
  646
+      this.form = null;
  647
+    }
  648
+  },
  649
+  enterHover: function() {
  650
+    if (this.saving) return;
  651
+    this.element.style.backgroundColor = this.options.highlightcolor;
  652
+    if (this.effect) {
  653
+      this.effect.cancel();
  654
+    }
  655
+    Element.addClassName(this.element, this.options.hoverClassName)
  656
+  },
  657
+  leaveHover: function() {
  658
+    if (this.options.backgroundColor) {
  659
+      this.element.style.backgroundColor = this.oldBackground;
  660
+    }
  661
+    Element.removeClassName(this.element, this.options.hoverClassName)
  662
+    if (this.saving) return;
  663
+    this.effect = new Effect.Highlight(this.element, {
  664
+      startcolor: this.options.highlightcolor,
  665
+      endcolor: this.options.highlightendcolor,
  666
+      restorecolor: this.originalBackground
  667
+    });
  668
+  },
  669
+  leaveEditMode: function() {
  670
+    Element.removeClassName(this.element, this.options.savingClassName);
  671
+    this.removeForm();
  672
+    this.leaveHover();
  673
+    this.element.style.backgroundColor = this.originalBackground;
  674
+    Element.show(this.element);
  675
+    if (this.options.externalControl) {
  676
+      Element.show(this.options.externalControl);
  677
+    }
  678
+    this.editing = false;
  679
+    this.saving = false;
  680
+    this.oldInnerHTML = null;
  681
+    this.onLeaveEditMode();
  682
+  },
  683
+  onComplete: function(transport) {
  684
+    this.leaveEditMode();
  685
+    this.options.onComplete.bind(this)(transport, this.element);
  686
+  },
  687
+  onEnterEditMode: function() {},
  688
+  onLeaveEditMode: function() {},
  689
+  dispose: function() {
  690
+    if (this.oldInnerHTML) {
  691
+      this.element.innerHTML = this.oldInnerHTML;
  692
+    }
  693
+    this.leaveEditMode();
  694
+    Event.stopObserving(this.element, 'click', this.onclickListener);
  695
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
  696
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
  697
+    if (this.options.externalControl) {
  698
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
  699
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
  700
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
  701
+    }
  702
+  }
  703
+};
509  actionpack/lib/action_view/helpers/javascripts/dragdrop.js
@@ -2,193 +2,79 @@
2 2
 // 
3 3
 // Element.Class part Copyright (c) 2005 by Rick Olson
4 4
 // 
5  
-// Permission is hereby granted, free of charge, to any person obtaining
6  
-// a copy of this software and associated documentation files (the
7  
-// "Software"), to deal in the Software without restriction, including
8  
-// without limitation the rights to use, copy, modify, merge, publish,
9  
-// distribute, sublicense, and/or sell copies of the Software, and to
10  
-// permit persons to whom the Software is furnished to do so, subject to
11  
-// the following conditions:
12  
-// 
13  
-// The above copyright notice and this permission notice shall be
14  
-// included in all copies or substantial portions of the Software.
15  
-// 
16  
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  
-
24  
-Element.Class = {
25  
-    // Element.toggleClass(element, className) toggles the class being on/off
26  
-    // Element.toggleClass(element, className1, className2) toggles between both classes,
27  
-    //   defaulting to className1 if neither exist
28  
-    toggle: function(element, className) {
29  
-      if(Element.Class.has(element, className)) {
30  
-        Element.Class.remove(element, className);
31  
-        if(arguments.length == 3) Element.Class.add(element, arguments[2]);
32  
-      } else {
33  
-        Element.Class.add(element, className);
34  
-        if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
35  
-      }
36  
-    },
37  
-
38  
-    // gets space-delimited classnames of an element as an array
39  
-    get: function(element) {
40  
-      element = $(element);
41  
-      return element.className.split(' ');
42  
-    },
43  
-
44  
-    // functions adapted from original functions by Gavin Kistner
45  
-    remove: function(element) {
46  
-      element = $(element);
47  
-      var regEx;
48  
-      for(var i = 1; i < arguments.length; i++) {
49  
-        regEx = new RegExp("^" + arguments[i] + "\\b\\s*|\\s*\\b" + arguments[i] + "\\b", 'g');
50  
-        element.className = element.className.replace(regEx, '')
51  
-      }
52  
-    },
53  
-
54  
-    add: function(element) {
55  
-      element = $(element);
56  
-      for(var i = 1; i < arguments.length; i++) {
57  
-        Element.Class.remove(element, arguments[i]);
58  
-        element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
59  
-      }
60  
-    },
61  
-
62  
-    // returns true if all given classes exist in said element
63  
-    has: function(element) {
64  
-      element = $(element);
65  
-      if(!element || !element.className) return false;
66  
-      var regEx;
67  
-      for(var i = 1; i < arguments.length; i++) {
68  
-        regEx = new RegExp("\\b" + arguments[i] + "\\b");
69  
-        if(!regEx.test(element.className)) return false;
70  
-      }
71  
-      return true;
72  
-    },
73  
-    
74  
-    // expects arrays of strings and/or strings as optional paramters
75  
-    // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
76  
-    has_any: function(element) {
77  
-      element = $(element);
78  
-      if(!element || !element.className) return false;
79  
-      var regEx;
80  
-      for(var i = 1; i < arguments.length; i++) {
81  
-        if((typeof arguments[i] == 'object') && 
82  
-          (arguments[i].constructor == Array)) {
83  
-          for(var j = 0; j < arguments[i].length; j++) {
84  
-            regEx = new RegExp("\\b" + arguments[i][j] + "\\b");
85  
-            if(regEx.test(element.className)) return true;
86  
-          }
87  
-        } else {
88  
-          regEx = new RegExp("\\b" + arguments[i] + "\\b");
89  
-          if(regEx.test(element.className)) return true;
90  
-        }
91  
-      }
92  
-      return false;
93  
-    },
94  
-    
95  
-    childrenWith: function(element, className) {
96  
-      var children = $(element).getElementsByTagName('*');