rails 0.13.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rails might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -1,4 +1,17 @@
1
- *SVN*
1
+ *0.13.1* (11 July, 2005)
2
+
3
+ * Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson]
4
+
5
+ * Added support for SQL Server in the database rake tasks #1652 [ken.barker@gmail.com] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
6
+
7
+ * Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
8
+
9
+ * Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay@gmail.com]
10
+
11
+ * Improved documentation for environment config files #1625 [court3nay@gmail.com]
12
+
13
+
14
+ *0.13.0* (6 July, 2005)
2
15
 
3
16
  * Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged)
4
17
 
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ require 'rbconfig'
9
9
 
10
10
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
11
11
  PKG_NAME = 'rails'
12
- PKG_VERSION = '0.13.0' + PKG_BUILD
12
+ PKG_VERSION = '0.13.1' + PKG_BUILD
13
13
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
14
14
  PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
15
15
 
@@ -247,15 +247,15 @@ spec = Gem::Specification.new do |s|
247
247
  s.summary = "Web-application framework with template engine, control-flow layer, and ORM."
248
248
  s.description = <<-EOF
249
249
  Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick
250
- on top of either MySQL, PostgreSQL, or SQLite with eRuby-based templates.
250
+ on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.
251
251
  EOF
252
252
 
253
253
  s.add_dependency('rake', '>= 0.5.3')
254
- s.add_dependency('activesupport', '= 1.1.0' + PKG_BUILD)
255
- s.add_dependency('activerecord', '= 1.11.0' + PKG_BUILD)
256
- s.add_dependency('actionpack', '= 1.9.0' + PKG_BUILD)
257
- s.add_dependency('actionmailer', '= 1.0.0' + PKG_BUILD)
258
- s.add_dependency('actionwebservice', '= 0.8.0' + PKG_BUILD)
254
+ s.add_dependency('activesupport', '= 1.1.1' + PKG_BUILD)
255
+ s.add_dependency('activerecord', '= 1.11.1' + PKG_BUILD)
256
+ s.add_dependency('actionpack', '= 1.9.1' + PKG_BUILD)
257
+ s.add_dependency('actionmailer', '= 1.0.1' + PKG_BUILD)
258
+ s.add_dependency('actionwebservice', '= 0.8.1' + PKG_BUILD)
259
259
 
260
260
  s.rdoc_options << '--exclude' << '.'
261
261
  s.has_rdoc = false
data/bin/rails CHANGED
@@ -1,4 +1,13 @@
1
- abort "Rails requires Ruby 1.8.2" if RUBY_VERSION < "1.8.2"
1
+ min_release = "1.8.2 (2004-12-25)"
2
+ ruby_release = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
3
+ if ruby_release < min_release
4
+ abort <<-end_message
5
+
6
+ Rails requires Ruby version #{min_release} or later.
7
+ You're running #{ruby_release}; please upgrade to continue.
8
+
9
+ end_message
10
+ end
2
11
 
3
12
  Signal.trap("INT") { puts; exit }
4
13
 
@@ -1,6 +1,14 @@
1
+ # In the development environment your application's code is reloaded on
2
+ # every request. This slows down response time but is perfect for development
3
+ # since you don't have to restart the webserver when you make code changes.
4
+
5
+ # Log error messages when you accidentally call methods on nil.
1
6
  require 'active_support/whiny_nil'
2
7
 
8
+ # Reload code; show full error reports; disable caching.
3
9
  Dependencies.mechanism = :load
4
10
  ActionController::Base.consider_all_requests_local = true
5
11
  ActionController::Base.perform_caching = false
6
- BREAKPOINT_SERVER_PORT = 42531
12
+
13
+ # The breakpoint server port that script/breakpointer connects to.
14
+ BREAKPOINT_SERVER_PORT = 42531
@@ -1,8 +1,20 @@
1
- RAILS_ROOT = File.dirname(__FILE__) + "/../"
1
+ # Load the Rails framework and configure your application.
2
+ # You can include your own configuration at the end of this file.
3
+ #
4
+ # Be sure to restart your webserver when you modify this file.
5
+
6
+ # The path to the root directory of your application.
7
+ RAILS_ROOT = File.join(File.dirname(__FILE__), '..')
8
+
9
+ # The environment your application is currently running. Don't set
10
+ # this here; put it in your webserver's configuration as the RAILS_ENV
11
+ # environment variable instead.
12
+ #
13
+ # See config/environments/*.rb for environment-specific configuration.
2
14
  RAILS_ENV = ENV['RAILS_ENV'] || 'development'
3
15
 
4
16
 
5
- # Mocks first.
17
+ # Load the Rails framework. Mock classes for testing come first.
6
18
  ADDITIONAL_LOAD_PATHS = ["#{RAILS_ROOT}/test/mocks/#{RAILS_ENV}"]
7
19
 
8
20
  # Then model subdirectories.
@@ -62,6 +74,8 @@ end
62
74
 
63
75
  [ActiveRecord, ActionController, ActionMailer].each { |mod| mod::Base.logger ||= RAILS_DEFAULT_LOGGER }
64
76
  [ActionController, ActionMailer].each { |mod| mod::Base.template_root ||= "#{RAILS_ROOT}/app/views/" }
77
+
78
+ # Set up routes.
65
79
  ActionController::Routing::Routes.reload
66
80
 
67
81
  Controllers = Dependencies::LoadingModule.root(
@@ -70,3 +84,4 @@ Controllers = Dependencies::LoadingModule.root(
70
84
  )
71
85
 
72
86
  # Include your app's configuration here:
87
+
@@ -1,3 +1,8 @@
1
+ # The production environment is meant for finished, "live" apps.
2
+ # Code is not reloaded between requests, full error reports are disabled,
3
+ # and caching is turned on.
4
+
5
+ # Don't reload code; don't show full error reports; enable caching.
1
6
  Dependencies.mechanism = :require
2
7
  ActionController::Base.consider_all_requests_local = false
3
- ActionController::Base.perform_caching = true
8
+ ActionController::Base.perform_caching = true
@@ -1,6 +1,17 @@
1
+ # The test environment is used exclusively to run your application's
2
+ # test suite. You never need to work with it otherwise. Remember that
3
+ # your test database is "scratch space" for the test suite and is wiped
4
+ # and recreated between test runs. Don't rely on the data there!
5
+
6
+ # Log error messages when you accidentally call methods on nil.
1
7
  require 'active_support/whiny_nil'
2
8
 
9
+ # Don't reload code; show full error reports; disable caching.
3
10
  Dependencies.mechanism = :require
4
11
  ActionController::Base.consider_all_requests_local = true
5
12
  ActionController::Base.perform_caching = false
6
- ActionMailer::Base.delivery_method = :test
13
+
14
+ # Tell ActionMailer not to deliver emails to the real world.
15
+ # The :test delivery method accumulates sent emails in the
16
+ # ActionMailer::Base.deliveries array.
17
+ ActionMailer::Base.delivery_method = :test
@@ -135,6 +135,8 @@ task :clone_structure_to_test => [ :db_structure_dump, :purge_test_database ] do
135
135
  `psql -U "#{abcs["test"]["username"]}" -f db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}`
136
136
  when "sqlite", "sqlite3"
137
137
  `#{abcs[RAILS_ENV]["adapter"]} #{abcs["test"]["dbfile"]} < db/#{RAILS_ENV}_structure.sql`
138
+ when "sqlserver"
139
+ `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`
138
140
  else
139
141
  raise "Unknown database adapter '#{abcs["test"]["adapter"]}'"
140
142
  end
@@ -154,6 +156,9 @@ task :db_structure_dump => :environment do
154
156
  `pg_dump -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{abcs[RAILS_ENV]["database"]}`
155
157
  when "sqlite", "sqlite3"
156
158
  `#{abcs[RAILS_ENV]["adapter"]} #{abcs[RAILS_ENV]["dbfile"]} .schema > db/#{RAILS_ENV}_structure.sql`
159
+ when "sqlserver"
160
+ `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r`
161
+ `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r`
157
162
  else
158
163
  raise "Unknown database adapter '#{abcs["test"]["adapter"]}'"
159
164
  end
@@ -174,6 +179,10 @@ task :purge_test_database => :environment do
174
179
  `createdb -T template0 -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}`
175
180
  when "sqlite","sqlite3"
176
181
  File.delete(abcs["test"]["dbfile"]) if File.exist?(abcs["test"]["dbfile"])
182
+ when "sqlserver"
183
+ dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-')
184
+ `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}`
185
+ `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`
177
186
  else
178
187
  raise "Unknown database adapter '#{abcs["test"]["adapter"]}'"
179
188
  end
@@ -187,7 +196,7 @@ task :clear_logs => :environment do
187
196
  end
188
197
  end
189
198
 
190
- desc "Migrate the database according to the migrate scripts in db/migrate"
199
+ desc "Migrate the database according to the migrate scripts in db/migrate (only supported on PG/MySQL). A specific version can be targetted with VERSION=x"
191
200
  task :migrate => :environment do
192
- ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/db/migrate/')
201
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/db/migrate/', ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
193
202
  end
@@ -1,4 +1,5 @@
1
1
  // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
+ // (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
2
3
  //
3
4
  // Permission is hereby granted, free of charge, to any person obtaining
4
5
  // a copy of this software and associated documentation files (the
@@ -19,7 +20,6 @@
19
20
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
21
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
 
22
-
23
23
  Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
24
24
  var children = $(element).childNodes;
25
25
  var text = "";
@@ -37,42 +37,70 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
37
37
  return text;
38
38
  }
39
39
 
40
- Ajax.Autocompleter = Class.create();
41
- Ajax.Autocompleter.prototype = (new Ajax.Base()).extend({
42
- initialize: function(element, update, url, options) {
40
+ // Autocompleter.Base handles all the autocompletion functionality
41
+ // that's independent of the data source for autocompletion. This
42
+ // includes drawing the autocompletion menu, observing keyboard
43
+ // and mouse events, and similar.
44
+ //
45
+ // Specific autocompleters need to provide, at the very least,
46
+ // a getUpdatedChoices function that will be invoked every time
47
+ // the text inside the monitored textbox changes. This method
48
+ // should get the text for which to provide autocompletion by
49
+ // invoking this.getEntry(), NOT by directly accessing
50
+ // this.element.value. This is to allow incremental tokenized
51
+ // autocompletion. Specific auto-completion logic (AJAX, etc)
52
+ // belongs in getUpdatedChoices.
53
+ //
54
+ // Tokenized incremental autocompletion is enabled automatically
55
+ // when an autocompleter is instantiated with the 'tokens' option
56
+ // in the options parameter, e.g.:
57
+ // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
58
+ // will incrementally autocomplete with a comma as the token.
59
+ // Additionally, ',' in the above example can be replaced with
60
+ // a token array, e.g. { tokens: new Array (',', '\n') } which
61
+ // enables autocompletion on multiple tokens. This is most
62
+ // useful when one of the tokens is \n (a newline), as it
63
+ // allows smart autocompletion after linebreaks.
64
+
65
+ var Autocompleter = {}
66
+ Autocompleter.Base = function() {};
67
+ Autocompleter.Base.prototype = {
68
+ base_initialize: function(element, update, options) {
43
69
  this.element = $(element);
44
70
  this.update = $(update);
45
71
  this.has_focus = false;
46
72
  this.changed = false;
47
73
  this.active = false;
48
74
  this.index = 0;
49
- this.entry_count = 0;
50
- this.url = url;
75
+ this.entry_count = 0;
51
76
 
52
- this.setOptions(options);
53
- this.options.asynchronous = true;
54
- this.options.onComplete = this.onComplete.bind(this)
77
+ if (this.setOptions)
78
+ this.setOptions(options);
79
+ else
80
+ this.options = {}
81
+
82
+ this.options.tokens = this.options.tokens || new Array();
55
83
  this.options.frequency = this.options.frequency || 0.4;
56
84
  this.options.min_chars = this.options.min_chars || 1;
57
- this.options.method = 'post';
58
-
59
- this.options.onShow = this.options.onShow ||
60
- function(element, update){
61
- if(!update.style.position || update.style.position=='absolute') {
62
- update.style.position = 'absolute';
85
+ this.options.onShow = this.options.onShow ||
86
+ function(element, update){
87
+ if(!update.style.position || update.style.position=='absolute') {
88
+ update.style.position = 'absolute';
63
89
  var offsets = Position.cumulativeOffset(element);
64
90
  update.style.left = offsets[0] + 'px';
65
91
  update.style.top = (offsets[1] + element.offsetHeight) + 'px';
66
92
  update.style.width = element.offsetWidth + 'px';
67
- }
68
- new Effect.Appear(update,{duration:0.3});
69
- };
93
+ }
94
+ new Effect.Appear(update,{duration:0.15});
95
+ };
70
96
  this.options.onHide = this.options.onHide ||
71
- function(element, update){ new Effect.Fade(update,{duration:0.3}) };
72
-
97
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
73
98
 
74
99
  if(this.options.indicator)
75
100
  this.indicator = $(this.options.indicator);
101
+
102
+ if (typeof(this.options.tokens) == 'string')
103
+ this.options.tokens = new Array(this.options.tokens);
76
104
 
77
105
  this.observer = null;
78
106
 
@@ -81,14 +109,14 @@ Ajax.Autocompleter.prototype = (new Ajax.Base()).extend({
81
109
  Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
82
110
  Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
83
111
  },
84
-
112
+
85
113
  show: function() {
86
114
  if(this.update.style.display=='none') this.options.onShow(this.element, this.update);
87
115
  if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && this.update.style.position=='absolute') {
88
116
  new Insertion.After(this.update,
89
117
  '<iframe id="' + this.update.id + '_iefix" '+
90
- 'style="display:none;filter:progid:DXImageTransform.Microsoft.Alpha(apacity=0);" ' +
91
- 'src="javascript:;" frameborder="0" scrolling="no"></iframe>');
118
+ 'style="display:none;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
119
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
92
120
  this.iefix = $(this.update.id+'_iefix');
93
121
  }
94
122
  if(this.iefix) {
@@ -111,51 +139,7 @@ Ajax.Autocompleter.prototype = (new Ajax.Base()).extend({
111
139
  stopIndicator: function() {
112
140
  if(this.indicator) Element.hide(this.indicator);
113
141
  },
114
-
115
- onObserverEvent: function() {
116
- this.changed = false;
117
- if(this.element.value.length>=this.options.min_chars) {
118
- this.startIndicator();
119
- this.options.parameters = this.options.callback ?
120
- this.options.callback(this.element, Form.Element.getValue(this.element)) :
121
- Form.Element.serialize(this.element);
122
- new Ajax.Request(this.url, this.options);
123
- } else {
124
- this.active = false;
125
- this.hide();
126
- }
127
- },
128
-
129
- addObservers: function(element) {
130
- Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
131
- Event.observe(element, "click", this.onClick.bindAsEventListener(this));
132
- },
133
-
134
- onComplete: function(request) {
135
- if(!this.changed && this.has_focus) {
136
- this.update.innerHTML = request.responseText;
137
- Element.cleanWhitespace(this.update);
138
- Element.cleanWhitespace(this.update.firstChild);
139
142
 
140
- if(this.update.firstChild && this.update.firstChild.childNodes) {
141
- this.entry_count =
142
- this.update.firstChild.childNodes.length;
143
- for (var i = 0; i < this.entry_count; i++) {
144
- entry = this.get_entry(i);
145
- entry.autocompleteIndex = i;
146
- this.addObservers(entry);
147
- }
148
- } else {
149
- this.entry_count = 0;
150
- }
151
-
152
- this.stopIndicator();
153
-
154
- this.index = 0;
155
- this.render();
156
- }
157
- },
158
-
159
143
  onKeyPress: function(event) {
160
144
  if(this.active)
161
145
  switch(event.keyCode) {
@@ -255,7 +239,208 @@ Ajax.Autocompleter.prototype = (new Ajax.Base()).extend({
255
239
  select_entry: function() {
256
240
  this.active = false;
257
241
  value = Element.collectTextNodesIgnoreClass(this.get_current_entry(), 'informal').unescapeHTML();
258
- this.element.value = value;
242
+ this.updateElement(value);
259
243
  this.element.focus();
244
+ },
245
+
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+/);
251
+ if (whitespace)
252
+ new_value += whitespace[0];
253
+ this.element.value = new_value + value;
254
+ } else {
255
+ this.element.value = value;
256
+ }
257
+ },
258
+
259
+ updateChoices: function(choices) {
260
+ if(!this.changed && this.has_focus) {
261
+ this.update.innerHTML = choices;
262
+ Element.cleanWhitespace(this.update);
263
+ Element.cleanWhitespace(this.update.firstChild);
264
+
265
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
266
+ this.entry_count =
267
+ this.update.firstChild.childNodes.length;
268
+ for (var i = 0; i < this.entry_count; i++) {
269
+ entry = this.get_entry(i);
270
+ entry.autocompleteIndex = i;
271
+ this.addObservers(entry);
272
+ }
273
+ } else {
274
+ this.entry_count = 0;
275
+ }
276
+
277
+ this.stopIndicator();
278
+
279
+ this.index = 0;
280
+ this.render();
281
+ }
282
+ },
283
+
284
+ addObservers: function(element) {
285
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
286
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
287
+ },
288
+
289
+ onObserverEvent: function() {
290
+ this.changed = false;
291
+ if(this.getEntry().length>=this.options.min_chars) {
292
+ this.startIndicator();
293
+ this.getUpdatedChoices();
294
+ } else {
295
+ this.active = false;
296
+ this.hide();
297
+ }
298
+ },
299
+
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+$/,'');
304
+ else
305
+ var ret = this.element.value;
306
+
307
+ return /\n/.test(ret) ? '' : ret;
308
+ },
309
+
310
+ findLastToken: function() {
311
+ var last_token_pos = -1;
312
+
313
+ 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;
317
+ }
318
+ return last_token_pos;
319
+ }
320
+ }
321
+
322
+ Ajax.Autocompleter = Class.create();
323
+ Ajax.Autocompleter.prototype = Object.extend(new Autocompleter.Base(),
324
+ Object.extend(new Ajax.Base(), {
325
+ initialize: function(element, update, url, options) {
326
+ this.base_initialize(element, update, options);
327
+ this.options.asynchronous = true;
328
+ this.options.onComplete = this.onComplete.bind(this)
329
+ this.options.method = 'post';
330
+ this.options.defaultParams = this.options.parameters || null;
331
+ this.url = url;
332
+ },
333
+
334
+ getUpdatedChoices: function() {
335
+ entry = encodeURIComponent(this.element.name) + '=' +
336
+ encodeURIComponent(this.getEntry());
337
+
338
+ this.options.parameters = this.options.callback ?
339
+ this.options.callback(this.element, entry) : entry;
340
+
341
+ if(this.options.defaultParams)
342
+ this.options.parameters += '&' + this.options.defaultParams;
343
+
344
+ new Ajax.Request(this.url, this.options);
345
+ },
346
+
347
+ onComplete: function(request) {
348
+ this.updateChoices(request.responseText);
349
+ }
350
+
351
+ }));
352
+
353
+ // The local array autocompleter. Used when you'd prefer to
354
+ // inject an array of autocompletion options into the page, rather
355
+ // than sending out Ajax queries, which can be quite slow sometimes.
356
+ //
357
+ // The constructor takes four parameters. The first two are, as usual,
358
+ // the id of the monitored textbox, and id of the autocompletion menu.
359
+ // The third is the array you want to autocomplete from, and the fourth
360
+ // is the options block.
361
+ //
362
+ // Extra local autocompletion options:
363
+ // - choices - How many autocompletion choices to offer
364
+ //
365
+ // - partial_search - If false, the autocompleter will match entered
366
+ // text only at the beginning of strings in the
367
+ // autocomplete array. Defaults to true, which will
368
+ // match text at the beginning of any *word* in the
369
+ // strings in the autocomplete array. If you want to
370
+ // search anywhere in the string, additionally set
371
+ // the option full_search to true (default: off).
372
+ //
373
+ // - full_search - Search anywhere in autocomplete array strings.
374
+ //
375
+ // - partial_chars - How many characters to enter before triggering
376
+ // a partial match (unlike min_chars, which defines
377
+ // how many characters are required to do any match
378
+ // at all). Defaults to 2.
379
+ //
380
+ // - ignore_case - Whether to ignore case when autocompleting.
381
+ // Defaults to true.
382
+ //
383
+ // It's possible to pass in a custom function as the 'selector'
384
+ // option, if you prefer to write your own autocompletion logic.
385
+ // In that case, the other options above will not apply unless
386
+ // you support them.
387
+
388
+ Autocompleter.Local = Class.create();
389
+ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
390
+ initialize: function(element, update, array, options) {
391
+ this.base_initialize(element, update, options);
392
+ this.options.array = array;
393
+ },
394
+
395
+ getUpdatedChoices: function() {
396
+ this.updateChoices(this.options.selector(this));
397
+ },
398
+
399
+ setOptions: function(options) {
400
+ this.options = Object.extend({
401
+ choices: 10,
402
+ partial_search: true,
403
+ partial_chars: 2,
404
+ ignore_case: true,
405
+ full_search: false,
406
+ selector: function(instance) {
407
+ var ret = new Array(); // Beginning matches
408
+ var partial = new Array(); // Inside matches
409
+ var entry = instance.getEntry();
410
+ var count = 0;
411
+
412
+ for (var i = 0; i < instance.options.array.length &&
413
+ ret.length < instance.options.choices ; i++) {
414
+ var elem = instance.options.array[i];
415
+ var found_pos = instance.options.ignore_case ?
416
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
417
+ elem.indexOf(entry);
418
+
419
+ while (found_pos != -1) {
420
+ if (found_pos == 0 && elem.length != entry.length) {
421
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
422
+ elem.substr(entry.length) + "</li>");
423
+ 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>");
430
+ break;
431
+ }
432
+ }
433
+
434
+ found_pos = instance.options.ignore_case ?
435
+ elem.toLowerCase().indexOf(entry.toLowerCase(), found_pos + 1) :
436
+ elem.indexOf(entry, found_pos + 1);
437
+
438
+ }
439
+ }
440
+ if (partial.length)
441
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
442
+ return "<ul>" + ret.join('') + "</ul>";
443
+ }
444
+ }, options || {});
260
445
  }
261
- });
446
+ });