gettext 1.91.0 → 1.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. data/ChangeLog +111 -1
  2. data/NEWS +35 -0
  3. data/README +26 -24
  4. data/bin/rgettext +2 -2
  5. data/bin/rmsgfmt +2 -2
  6. data/bin/rmsgmerge +2 -2
  7. data/doc/classes/ActionView/Helpers/FormBuilder.html +11 -11
  8. data/doc/classes/ActionView/TemplateFinder.html +152 -0
  9. data/doc/classes/ActiveRecord/Base.html +87 -87
  10. data/doc/classes/ActiveRecord/ConnectionAdapters/Column.html +6 -6
  11. data/doc/classes/GetText.html +617 -491
  12. data/doc/classes/GetText/ActiveRecordParser.html +13 -13
  13. data/doc/classes/GetText/ErbContainer.html +12 -12
  14. data/doc/classes/GetText/ErbParser.html +6 -6
  15. data/doc/classes/GetText/NoboundTextDomainError.html +1 -1
  16. data/doc/classes/GetText/PoParser.html +11 -11
  17. data/doc/classes/GetText/Rails.html +20 -20
  18. data/doc/classes/GetText/RubyParser.html +5 -0
  19. data/doc/classes/GetText/TextDomain.html +53 -53
  20. data/doc/classes/GetText/TextDomainManager.html +54 -54
  21. data/doc/classes/Iconv.html +20 -20
  22. data/doc/classes/Locale.html +151 -151
  23. data/doc/classes/Locale/Object.html +92 -92
  24. data/doc/classes/Locale/SystemBase.html +32 -32
  25. data/doc/classes/Locale/SystemCGI.html +31 -31
  26. data/doc/classes/Locale/SystemWin32.html +6 -6
  27. data/doc/classes/MOFile.html +311 -293
  28. data/doc/classes/MOFile/HeaderRev1.html +150 -0
  29. data/doc/classes/Module.html +6 -6
  30. data/doc/created.rid +1 -1
  31. data/doc/files/ChangeLog.html +143 -2
  32. data/doc/files/README.html +31 -25
  33. data/doc/files/lib/gettext/active_record_rb.html +1 -1
  34. data/doc/files/lib/gettext/mo_rb.html +1 -1
  35. data/doc/files/lib/gettext/parser/active_record_rb.html +1 -1
  36. data/doc/files/lib/gettext/parser/ruby_rb.html +1 -1
  37. data/doc/files/lib/gettext/poparser_rb.html +1 -1
  38. data/doc/files/lib/gettext/rails_rb.html +1 -1
  39. data/doc/files/lib/gettext/rgettext_rb.html +1 -1
  40. data/doc/files/lib/gettext/rmsgfmt_rb.html +2 -1
  41. data/doc/files/lib/gettext/utils_rb.html +1 -1
  42. data/doc/files/lib/gettext/version_rb.html +1 -1
  43. data/doc/files/lib/gettext_rb.html +1 -1
  44. data/doc/fr_class_index.html +2 -0
  45. data/doc/fr_method_index.html +133 -129
  46. data/lib/gettext.rb +49 -7
  47. data/lib/gettext/active_record.rb +49 -4
  48. data/lib/gettext/mo.rb +39 -12
  49. data/lib/gettext/parser/active_record.rb +6 -4
  50. data/lib/gettext/parser/ruby.rb +11 -4
  51. data/lib/gettext/poparser.rb +81 -52
  52. data/lib/gettext/rails.rb +28 -11
  53. data/lib/gettext/rgettext.rb +6 -1
  54. data/lib/gettext/rmsgfmt.rb +3 -11
  55. data/lib/gettext/utils.rb +7 -3
  56. data/lib/gettext/version.rb +1 -1
  57. data/po/bs/rails.po +55 -20
  58. data/po/bs/rgettext.po +3 -3
  59. data/po/ca/rails.po +58 -31
  60. data/po/ca/rgettext.po +16 -18
  61. data/po/cs/rails.po +55 -20
  62. data/po/cs/rgettext.po +3 -3
  63. data/po/de/rails.po +55 -27
  64. data/po/de/rgettext.po +21 -22
  65. data/po/el/rails.po +60 -36
  66. data/po/el/rgettext.po +15 -17
  67. data/po/eo/rails.po +51 -23
  68. data/po/eo/rgettext.po +21 -20
  69. data/po/es/rails.po +57 -31
  70. data/po/es/rgettext.po +17 -18
  71. data/po/et/rails.po +55 -20
  72. data/po/fr/rails.po +56 -21
  73. data/po/fr/rgettext.po +3 -3
  74. data/po/hr/rails.po +55 -20
  75. data/po/hr/rgettext.po +3 -3
  76. data/po/hu/rails.po +54 -26
  77. data/po/hu/rgettext.po +18 -17
  78. data/po/it/rails.po +55 -20
  79. data/po/it/rgettext.po +3 -3
  80. data/po/ja/rails.po +54 -26
  81. data/po/ja/rgettext.po +17 -18
  82. data/po/ko/rails.po +56 -21
  83. data/po/ko/rgettext.po +3 -3
  84. data/po/lv/rails.po +155 -0
  85. data/po/lv/rgettext.po +137 -0
  86. data/po/nb/rails.po +61 -34
  87. data/po/nb/rgettext.po +22 -19
  88. data/po/nl/rails.po +56 -21
  89. data/po/nl/rgettext.po +3 -3
  90. data/po/pt_BR/rails.po +53 -26
  91. data/po/pt_BR/rgettext.po +19 -19
  92. data/po/rails.pot +44 -16
  93. data/po/rgettext.pot +3 -3
  94. data/po/ru/rails.po +62 -36
  95. data/po/ru/rgettext.po +19 -19
  96. data/po/sr/rails.po +87 -58
  97. data/po/sr/rgettext.po +38 -33
  98. data/po/sv/rgettext.po +3 -3
  99. data/po/ua/rails.po +62 -32
  100. data/po/ua/rgettext.po +19 -17
  101. data/po/vi/rails.po +56 -28
  102. data/po/vi/rgettext.po +35 -34
  103. data/po/zh/rails.po +52 -24
  104. data/po/zh/rgettext.po +22 -24
  105. data/po/zh_TW/rails.po +51 -23
  106. data/po/zh_TW/rgettext.po +16 -18
  107. data/samples/cgi/cookie.cgi +0 -0
  108. data/samples/cgi/helloerb1.cgi +0 -0
  109. data/samples/cgi/helloerb2.cgi +0 -0
  110. data/samples/cgi/http.rb +0 -0
  111. data/samples/cgi/index.cgi +0 -0
  112. data/samples/cgi/po/lv/helloerb1.po +65 -0
  113. data/samples/cgi/po/lv/helloerb2.po +52 -0
  114. data/samples/cgi/po/lv/hellolib.po +24 -0
  115. data/samples/cgi/po/lv/main.po +77 -0
  116. data/samples/cgi/po/nb/helloerb1.po +16 -14
  117. data/samples/cgi/po/nb/helloerb2.po +14 -12
  118. data/samples/cgi/po/nb/hellolib.po +9 -7
  119. data/samples/cgi/po/nb/main.po +20 -18
  120. data/samples/cgi/po/sr/helloerb1.po +8 -8
  121. data/samples/cgi/po/sr/helloerb2.po +6 -6
  122. data/samples/cgi/po/sr/hellolib.po +2 -2
  123. data/samples/cgi/po/sr/main.po +15 -15
  124. data/samples/cgi/po/ua/helloerb1.po +2 -2
  125. data/samples/cgi/po/ua/main.po +3 -3
  126. data/samples/hello_plural.rb +0 -0
  127. data/samples/makemo.rb +0 -0
  128. data/samples/po/lv/hello.po +24 -0
  129. data/samples/po/lv/hello2.po +32 -0
  130. data/samples/po/lv/hello_noop.po +28 -0
  131. data/samples/po/lv/hello_plural.po +26 -0
  132. data/samples/po/lv/helloglade2.po +38 -0
  133. data/samples/po/lv/hellogtk.po +24 -0
  134. data/samples/po/lv/hellotk.po +24 -0
  135. data/samples/po/nb/hello.po +9 -8
  136. data/samples/po/nb/hello2.po +9 -9
  137. data/samples/po/nb/hello_noop.po +10 -9
  138. data/samples/po/nb/hello_plural.po +10 -8
  139. data/samples/po/nb/helloglade2.po +10 -8
  140. data/samples/po/nb/hellogtk.po +9 -8
  141. data/samples/po/nb/hellotk.po +9 -7
  142. data/samples/po/sr/hello.po +2 -2
  143. data/samples/po/sr/hello2.po +4 -4
  144. data/samples/po/sr/hello_noop.po +3 -3
  145. data/samples/po/sr/hello_plural.po +3 -3
  146. data/samples/po/sr/helloglade2.po +4 -4
  147. data/samples/po/sr/hellogtk.po +2 -2
  148. data/samples/po/sr/hellotk.po +2 -2
  149. data/samples/po/ua/hello2.po +2 -2
  150. data/samples/po/ua/hello_plural.po +2 -2
  151. data/samples/rails/config/boot.rb +3 -2
  152. data/samples/rails/config/environment.rb +2 -3
  153. data/samples/rails/db/schema.rb +0 -0
  154. data/samples/rails/po/lv/blog.po +110 -0
  155. data/samples/rails/po/nb/blog.po +19 -15
  156. data/samples/rails/po/sr/blog.po +23 -23
  157. data/samples/rails/po/ua/blog.po +3 -3
  158. data/samples/rails/public/dispatch.cgi +0 -0
  159. data/samples/rails/public/dispatch.fcgi +0 -0
  160. data/samples/rails/public/dispatch.rb +0 -0
  161. data/samples/rails/public/javascripts/controls.js +1 -1
  162. data/samples/rails/public/javascripts/dragdrop.js +1 -1
  163. data/samples/rails/public/javascripts/effects.js +1 -1
  164. data/samples/rails/script/about +0 -0
  165. data/samples/rails/script/breakpointer +0 -0
  166. data/samples/rails/script/console +0 -0
  167. data/samples/rails/script/destroy +0 -0
  168. data/samples/rails/script/generate +0 -0
  169. data/samples/rails/script/performance/benchmarker +0 -0
  170. data/samples/rails/script/performance/profiler +0 -0
  171. data/samples/rails/script/plugin +0 -0
  172. data/samples/rails/script/process/reaper +0 -0
  173. data/samples/rails/script/process/spawner +0 -0
  174. data/samples/rails/script/process/spinner +0 -0
  175. data/samples/rails/script/runner +0 -0
  176. data/samples/rails/script/server +0 -0
  177. data/samples/rails/vendor/plugins/gettext/po/lv/gettext_plugin.po +28 -0
  178. data/samples/rails/vendor/plugins/gettext/po/nb/gettext_plugin.po +11 -8
  179. data/samples/rails/vendor/plugins/gettext/po/sr/gettext_plugin.po +5 -5
  180. data/src/poparser.ry +27 -6
  181. data/test/benchmark.rb +1 -1
  182. data/test/db/mysql.drop.sql +1 -0
  183. data/test/db/mysql.sql +13 -0
  184. data/test/fixtures/inept_wizard.rb +3 -0
  185. data/test/fixtures/people.yml +6 -0
  186. data/test/fixtures/topic.rb +30 -0
  187. data/test/fixtures/wizard.rb +5 -0
  188. data/test/po/active_record.pot +151 -66
  189. data/test/po/ja/active_record.po +177 -92
  190. data/test/po/ja/test_pgettext.po +41 -0
  191. data/test/rails/app/controllers/users_controller.rb +1 -0
  192. data/test/rails/app/views/articles/_form_fr.html.erb +1 -0
  193. data/test/rails/config/boot.rb +96 -32
  194. data/test/rails/config/environment.rb +3 -2
  195. data/test/rails/db/schema.rb +4 -4
  196. data/test/rails/public/dispatch.cgi +0 -0
  197. data/test/rails/public/dispatch.fcgi +0 -0
  198. data/test/rails/public/dispatch.rb +0 -0
  199. data/test/rails/public/javascripts/controls.js +484 -354
  200. data/test/rails/public/javascripts/dragdrop.js +88 -58
  201. data/test/rails/public/javascripts/effects.js +396 -364
  202. data/test/rails/public/javascripts/prototype.js +2817 -1107
  203. data/test/rails/script/about +0 -0
  204. data/test/rails/script/breakpointer +0 -0
  205. data/test/rails/script/console +0 -0
  206. data/test/rails/script/destroy +0 -0
  207. data/test/rails/script/generate +0 -0
  208. data/test/rails/script/performance/benchmarker +0 -0
  209. data/test/rails/script/performance/profiler +0 -0
  210. data/test/rails/script/plugin +0 -0
  211. data/test/rails/script/process/inspector +0 -0
  212. data/test/rails/script/process/reaper +0 -0
  213. data/test/rails/script/process/spawner +0 -0
  214. data/test/rails/script/runner +0 -0
  215. data/test/rails/script/server +0 -0
  216. data/test/rails/test/functional/articles_controller_test.rb +5 -0
  217. data/test/rails/test/result/en/create_error.html +3 -3
  218. data/test/rails/test/result/en/custom_error_message.html +3 -3
  219. data/test/rails/test/result/en/custom_error_message_with_plural.html +60 -60
  220. data/test/rails/test/result/en/multi_error_messages_for.html +6 -6
  221. data/test/rails/test/result/en/new.html +3 -3
  222. data/test/rails/test/result/fr/custom_error_message.html +3 -3
  223. data/test/rails/test/result/fr/custom_error_message_with_plural.html +60 -60
  224. data/test/rails/test/result/fr/new.html +23 -0
  225. data/test/rails/test/result/ja/create_error.html +3 -3
  226. data/test/rails/test/result/ja/custom_error_message.html +3 -3
  227. data/test/rails/test/result/ja/custom_error_message_with_plural.html +60 -60
  228. data/test/rails/test/result/ja/multi_error_messages_for.html +6 -6
  229. data/test/rails/test/result/ja/new.html +3 -3
  230. data/test/test.sh +2 -2
  231. data/test/test_active_record.rb +824 -20
  232. data/test/test_gettext.rb +34 -4
  233. data/test/test_java.sh +0 -0
  234. data/test/test_locale.rb +2 -2
  235. data/test/test_parser.rb +13 -2
  236. data/test/test_rails_caching.rb +11 -4
  237. data/test/testlib/pgettext.rb +33 -0
  238. metadata +32 -4
@@ -0,0 +1,41 @@
1
+ # Japanese translations for PACKAGE package
2
+ # PACKAGE パッケージに対する英訳.
3
+ # Copyright (C) 2008 THE PACKAGE'S COPYRIGHT HOLDER
4
+ # This file is distributed under the same license as the PACKAGE package.
5
+ # Masao Mutoh <mutoh@highway.ne.jp>, 2008.
6
+ #
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "POT-Creation-Date: 2008-07-26 15:39+0900\n"
11
+ "PO-Revision-Date: 2008-07-26 15:39+0900\n"
12
+ "Last-Translator: Masao Mutoh <mutoh@highway.ne.jp>\n"
13
+ "Language-Team: Japanese\n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: 8bit\n"
17
+ "Plural-Forms: nplurals=1; plural=0;\n"
18
+
19
+ #: testlib/pgettext.rb:10 testlib/pgettext.rb:14
20
+ msgctxt "AAA"
21
+ msgid "BBB"
22
+ msgstr "えーびー"
23
+
24
+ #: testlib/pgettext.rb:18
25
+ msgctxt "AAA|BBB"
26
+ msgid "CCC"
27
+ msgstr "えーびーしー"
28
+
29
+ #: testlib/pgettext.rb:22
30
+ msgctxt "AAA"
31
+ msgid "CCC"
32
+ msgstr ""
33
+
34
+ #: testlib/pgettext.rb:26
35
+ msgctxt "CCC"
36
+ msgid "BBB"
37
+ msgstr "しーびー"
38
+
39
+ #: testlib/pgettext.rb:33
40
+ msgid "BBB"
41
+ msgstr "びー"
@@ -6,5 +6,6 @@ class UsersController < ApplicationController
6
6
  @user.lastupdate = "2007-01-01"
7
7
  end
8
8
  @user.valid?
9
+ @user.lastupdate = "2007-01-01"
9
10
  end
10
11
  end
@@ -0,0 +1 @@
1
+ <h1>FRENCH</h1>
@@ -1,45 +1,109 @@
1
- # Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
1
+ # Don't change this file!
2
+ # Configure your app in config/environment.rb and config/environments/*.rb
2
3
 
3
- unless defined?(RAILS_ROOT)
4
- root_path = File.join(File.dirname(__FILE__), '..')
4
+ RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
5
5
 
6
- unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
7
- require 'pathname'
8
- root_path = Pathname.new(root_path).cleanpath(true).to_s
9
- end
6
+ module Rails
7
+ class << self
8
+ def boot!
9
+ unless booted?
10
+ preinitialize
11
+ pick_boot.run
12
+ end
13
+ end
10
14
 
11
- RAILS_ROOT = root_path
12
- end
15
+ def booted?
16
+ defined? Rails::Initializer
17
+ end
18
+
19
+ def pick_boot
20
+ (vendor_rails? ? VendorBoot : GemBoot).new
21
+ end
22
+
23
+ def vendor_rails?
24
+ File.exist?("#{RAILS_ROOT}/vendor/rails")
25
+ end
13
26
 
14
- unless defined?(Rails::Initializer)
15
- if File.directory?("#{RAILS_ROOT}/vendor/rails")
16
- require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
17
- else
18
- require 'rubygems'
27
+ def preinitialize
28
+ load(preinitializer_path) if File.exist?(preinitializer_path)
29
+ end
19
30
 
20
- environment_without_comments = IO.readlines(File.dirname(__FILE__) + '/environment.rb').reject { |l| l =~ /^#/ }.join
21
- environment_without_comments =~ /[^#]RAILS_GEM_VERSION = '([\d.]+)'/
22
- rails_gem_version = $1
31
+ def preinitializer_path
32
+ "#{RAILS_ROOT}/config/preinitializer.rb"
33
+ end
34
+ end
23
35
 
24
- if version = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : rails_gem_version
25
- # Asking for 1.1.6 will give you 1.1.6.5206, if available -- makes it easier to use beta gems
26
- rails_gem = Gem.cache.search('rails', "~>#{version}.0").sort_by { |g| g.version.version }.last
36
+ class Boot
37
+ def run
38
+ load_initializer
39
+ Rails::Initializer.run(:set_load_path)
40
+ end
41
+ end
27
42
 
28
- if rails_gem
29
- gem "rails", "=#{rails_gem.version.version}"
30
- require rails_gem.full_gem_path + '/lib/initializer'
43
+ class VendorBoot < Boot
44
+ def load_initializer
45
+ require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
46
+ Rails::Initializer.run(:install_gem_spec_stubs)
47
+ end
48
+ end
49
+
50
+ class GemBoot < Boot
51
+ def load_initializer
52
+ self.class.load_rubygems
53
+ load_rails_gem
54
+ require 'initializer'
55
+ end
56
+
57
+ def load_rails_gem
58
+ if version = self.class.gem_version
59
+ gem 'rails', version
31
60
  else
32
- STDERR.puts %(Cannot find gem for Rails ~>#{version}.0:
33
- Install the missing gem with 'gem install -v=#{version} rails', or
34
- change environment.rb to define RAILS_GEM_VERSION with your desired version.
35
- )
61
+ gem 'rails'
62
+ end
63
+ rescue Gem::LoadError => load_error
64
+ $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
65
+ exit 1
66
+ end
67
+
68
+ class << self
69
+ def rubygems_version
70
+ Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
71
+ end
72
+
73
+ def gem_version
74
+ if defined? RAILS_GEM_VERSION
75
+ RAILS_GEM_VERSION
76
+ elsif ENV.include?('RAILS_GEM_VERSION')
77
+ ENV['RAILS_GEM_VERSION']
78
+ else
79
+ parse_gem_version(read_environment_rb)
80
+ end
81
+ end
82
+
83
+ def load_rubygems
84
+ require 'rubygems'
85
+
86
+ unless rubygems_version >= '0.9.4'
87
+ $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
88
+ exit 1
89
+ end
90
+
91
+ rescue LoadError
92
+ $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
36
93
  exit 1
37
94
  end
38
- else
39
- gem "rails"
40
- require 'initializer'
95
+
96
+ def parse_gem_version(text)
97
+ $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
98
+ end
99
+
100
+ private
101
+ def read_environment_rb
102
+ File.read("#{RAILS_ROOT}/config/environment.rb")
103
+ end
41
104
  end
42
105
  end
43
-
44
- Rails::Initializer.run(:set_load_path)
45
106
  end
107
+
108
+ # All that for this:
109
+ Rails.boot!
@@ -7,7 +7,7 @@
7
7
  $KCODE = "U"
8
8
 
9
9
  # Specifies gem version of Rails to use when vendor/rails is not present
10
- RAILS_GEM_VERSION = '2.0.2' unless defined? RAILS_GEM_VERSION
10
+ RAILS_GEM_VERSION = '2.1.0' unless defined? RAILS_GEM_VERSION
11
11
 
12
12
  # Bootstrap the Rails environment, frameworks, and default configuration
13
13
  require File.join(File.dirname(__FILE__), 'boot')
@@ -49,6 +49,8 @@ Rails::Initializer.run do |config|
49
49
  :session_key => '_blog_session',
50
50
  :secret => '481e79cd6d557ee64d26390fb85013f3'
51
51
  }
52
+
53
+ config.gem 'gettext', :lib => 'gettext/rails'
52
54
  end
53
55
 
54
56
  # Add new inflection rules using the following format
@@ -66,4 +68,3 @@ end
66
68
 
67
69
  # Include your application configuration below
68
70
 
69
- require 'gettext/rails'
@@ -1,5 +1,5 @@
1
1
  # This file is auto-generated from the current state of the database. Instead of editing this file,
2
- # please use the migrations feature of ActiveRecord to incrementally modify your database, and
2
+ # please use the migrations feature of Active Record to incrementally modify your database, and
3
3
  # then regenerate this schema definition.
4
4
  #
5
5
  # Note that this schema.rb definition is the authoritative source for your database schema. If you need
@@ -12,13 +12,13 @@
12
12
  ActiveRecord::Schema.define(:version => 1) do
13
13
 
14
14
  create_table "accounts", :force => true do |t|
15
- t.integer "amount"
16
- t.integer "person_id"
15
+ t.integer "amount", :limit => 11
16
+ t.integer "person_id", :limit => 11
17
17
  end
18
18
 
19
19
  create_table "articles", :force => true do |t|
20
20
  t.string "title", :default => "", :null => false
21
- t.text "description", :default => "", :null => false
21
+ t.text "description", :null => false
22
22
  t.date "lastupdate"
23
23
  end
24
24
 
File without changes
File without changes
File without changes
@@ -1,6 +1,6 @@
1
- // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
- // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3
- // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
1
+ // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
+ // (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3
+ // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
4
4
  // Contributors:
5
5
  // Richard Livsey
6
6
  // Rahul Bhargava
@@ -37,22 +37,23 @@
37
37
  if(typeof Effect == 'undefined')
38
38
  throw("controls.js requires including script.aculo.us' effects.js library");
39
39
 
40
- var Autocompleter = {}
41
- Autocompleter.Base = function() {};
42
- Autocompleter.Base.prototype = {
40
+ var Autocompleter = { }
41
+ Autocompleter.Base = Class.create({
43
42
  baseInitialize: function(element, update, options) {
44
- this.element = $(element);
43
+ element = $(element)
44
+ this.element = element;
45
45
  this.update = $(update);
46
46
  this.hasFocus = false;
47
47
  this.changed = false;
48
48
  this.active = false;
49
49
  this.index = 0;
50
50
  this.entryCount = 0;
51
+ this.oldElementValue = this.element.value;
51
52
 
52
53
  if(this.setOptions)
53
54
  this.setOptions(options);
54
55
  else
55
- this.options = options || {};
56
+ this.options = options || { };
56
57
 
57
58
  this.options.paramName = this.options.paramName || this.element.name;
58
59
  this.options.tokens = this.options.tokens || [];
@@ -74,6 +75,9 @@ Autocompleter.Base.prototype = {
74
75
 
75
76
  if(typeof(this.options.tokens) == 'string')
76
77
  this.options.tokens = new Array(this.options.tokens);
78
+ // Force carriage returns as token delimiters anyway
79
+ if (!this.options.tokens.include('\n'))
80
+ this.options.tokens.push('\n');
77
81
 
78
82
  this.observer = null;
79
83
 
@@ -81,15 +85,14 @@ Autocompleter.Base.prototype = {
81
85
 
82
86
  Element.hide(this.update);
83
87
 
84
- Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
85
- Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
88
+ Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
89
+ Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
86
90
  },
87
91
 
88
92
  show: function() {
89
93
  if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
90
94
  if(!this.iefix &&
91
- (navigator.appVersion.indexOf('MSIE')>0) &&
92
- (navigator.userAgent.indexOf('Opera')<0) &&
95
+ (Prototype.Browser.IE) &&
93
96
  (Element.getStyle(this.update, 'position')=='absolute')) {
94
97
  new Insertion.After(this.update,
95
98
  '<iframe id="' + this.update.id + '_iefix" '+
@@ -139,17 +142,17 @@ Autocompleter.Base.prototype = {
139
142
  case Event.KEY_UP:
140
143
  this.markPrevious();
141
144
  this.render();
142
- if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
145
+ Event.stop(event);
143
146
  return;
144
147
  case Event.KEY_DOWN:
145
148
  this.markNext();
146
149
  this.render();
147
- if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
150
+ Event.stop(event);
148
151
  return;
149
152
  }
150
153
  else
151
154
  if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
152
- (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
155
+ (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
153
156
 
154
157
  this.changed = true;
155
158
  this.hasFocus = true;
@@ -195,7 +198,6 @@ Autocompleter.Base.prototype = {
195
198
  this.index==i ?
196
199
  Element.addClassName(this.getEntry(i),"selected") :
197
200
  Element.removeClassName(this.getEntry(i),"selected");
198
-
199
201
  if(this.hasFocus) {
200
202
  this.show();
201
203
  this.active = true;
@@ -238,21 +240,22 @@ Autocompleter.Base.prototype = {
238
240
  }
239
241
  var value = '';
240
242
  if (this.options.select) {
241
- var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
243
+ var nodes = $(selectedElement).select('.' + this.options.select) || [];
242
244
  if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
243
245
  } else
244
246
  value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
245
247
 
246
- var lastTokenPos = this.findLastToken();
247
- if (lastTokenPos != -1) {
248
- var newValue = this.element.value.substr(0, lastTokenPos + 1);
249
- var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
248
+ var bounds = this.getTokenBounds();
249
+ if (bounds[0] != -1) {
250
+ var newValue = this.element.value.substr(0, bounds[0]);
251
+ var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
250
252
  if (whitespace)
251
253
  newValue += whitespace[0];
252
- this.element.value = newValue + value;
254
+ this.element.value = newValue + value + this.element.value.substr(bounds[1]);
253
255
  } else {
254
256
  this.element.value = value;
255
257
  }
258
+ this.oldElementValue = this.element.value;
256
259
  this.element.focus();
257
260
 
258
261
  if (this.options.afterUpdateElement)
@@ -296,39 +299,48 @@ Autocompleter.Base.prototype = {
296
299
 
297
300
  onObserverEvent: function() {
298
301
  this.changed = false;
302
+ this.tokenBounds = null;
299
303
  if(this.getToken().length>=this.options.minChars) {
300
- this.startIndicator();
301
304
  this.getUpdatedChoices();
302
305
  } else {
303
306
  this.active = false;
304
307
  this.hide();
305
308
  }
309
+ this.oldElementValue = this.element.value;
306
310
  },
307
311
 
308
312
  getToken: function() {
309
- var tokenPos = this.findLastToken();
310
- if (tokenPos != -1)
311
- var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
312
- else
313
- var ret = this.element.value;
314
-
315
- return /\n/.test(ret) ? '' : ret;
316
- },
317
-
318
- findLastToken: function() {
319
- var lastTokenPos = -1;
320
-
321
- for (var i=0; i<this.options.tokens.length; i++) {
322
- var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
323
- if (thisTokenPos > lastTokenPos)
324
- lastTokenPos = thisTokenPos;
313
+ var bounds = this.getTokenBounds();
314
+ return this.element.value.substring(bounds[0], bounds[1]).strip();
315
+ },
316
+
317
+ getTokenBounds: function() {
318
+ if (null != this.tokenBounds) return this.tokenBounds;
319
+ var value = this.element.value;
320
+ if (value.strip().empty()) return [-1, 0];
321
+ var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
322
+ var offset = (diff == this.oldElementValue.length ? 1 : 0);
323
+ var prevTokenPos = -1, nextTokenPos = value.length;
324
+ var tp;
325
+ for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
326
+ tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
327
+ if (tp > prevTokenPos) prevTokenPos = tp;
328
+ tp = value.indexOf(this.options.tokens[index], diff + offset);
329
+ if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
325
330
  }
326
- return lastTokenPos;
331
+ return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
327
332
  }
328
- }
333
+ });
334
+
335
+ Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
336
+ var boundary = Math.min(newS.length, oldS.length);
337
+ for (var index = 0; index < boundary; ++index)
338
+ if (newS[index] != oldS[index])
339
+ return index;
340
+ return boundary;
341
+ };
329
342
 
330
- Ajax.Autocompleter = Class.create();
331
- Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
343
+ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
332
344
  initialize: function(element, update, url, options) {
333
345
  this.baseInitialize(element, update, options);
334
346
  this.options.asynchronous = true;
@@ -338,7 +350,9 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
338
350
  },
339
351
 
340
352
  getUpdatedChoices: function() {
341
- entry = encodeURIComponent(this.options.paramName) + '=' +
353
+ this.startIndicator();
354
+
355
+ var entry = encodeURIComponent(this.options.paramName) + '=' +
342
356
  encodeURIComponent(this.getToken());
343
357
 
344
358
  this.options.parameters = this.options.callback ?
@@ -346,14 +360,13 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
346
360
 
347
361
  if(this.options.defaultParams)
348
362
  this.options.parameters += '&' + this.options.defaultParams;
349
-
363
+
350
364
  new Ajax.Request(this.url, this.options);
351
365
  },
352
366
 
353
367
  onComplete: function(request) {
354
368
  this.updateChoices(request.responseText);
355
369
  }
356
-
357
370
  });
358
371
 
359
372
  // The local array autocompleter. Used when you'd prefer to
@@ -391,8 +404,7 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
391
404
  // In that case, the other options above will not apply unless
392
405
  // you support them.
393
406
 
394
- Autocompleter.Local = Class.create();
395
- Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
407
+ Autocompleter.Local = Class.create(Autocompleter.Base, {
396
408
  initialize: function(element, update, array, options) {
397
409
  this.baseInitialize(element, update, options);
398
410
  this.options.array = array;
@@ -448,13 +460,12 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
448
460
  ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
449
461
  return "<ul>" + ret.join('') + "</ul>";
450
462
  }
451
- }, options || {});
463
+ }, options || { });
452
464
  }
453
465
  });
454
466
 
455
- // AJAX in-place editor
456
- //
457
- // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
467
+ // AJAX in-place editor and collection editor
468
+ // Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
458
469
 
459
470
  // Use this if you notice weird scrolling problems on some browsers,
460
471
  // the DOM might be a bit confused when this gets called so do this
@@ -465,353 +476,472 @@ Field.scrollFreeActivate = function(field) {
465
476
  }, 1);
466
477
  }
467
478
 
468
- Ajax.InPlaceEditor = Class.create();
469
- Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
470
- Ajax.InPlaceEditor.prototype = {
479
+ Ajax.InPlaceEditor = Class.create({
471
480
  initialize: function(element, url, options) {
472
481
  this.url = url;
473
- this.element = $(element);
474
-
475
- this.options = Object.extend({
476
- paramName: "value",
477
- okButton: true,
478
- okText: "ok",
479
- cancelLink: true,
480
- cancelText: "cancel",
481
- savingText: "Saving...",
482
- clickToEditText: "Click to edit",
483
- okText: "ok",
484
- rows: 1,
485
- onComplete: function(transport, element) {
486
- new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
487
- },
488
- onFailure: function(transport) {
489
- alert("Error communicating with the server: " + transport.responseText.stripTags());
490
- },
491
- callback: function(form) {
492
- return Form.serialize(form);
493
- },
494
- handleLineBreaks: true,
495
- loadingText: 'Loading...',
496
- savingClassName: 'inplaceeditor-saving',
497
- loadingClassName: 'inplaceeditor-loading',
498
- formClassName: 'inplaceeditor-form',
499
- highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
500
- highlightendcolor: "#FFFFFF",
501
- externalControl: null,
502
- submitOnBlur: false,
503
- ajaxOptions: {},
504
- evalScripts: false
505
- }, options || {});
506
-
507
- if(!this.options.formId && this.element.id) {
508
- this.options.formId = this.element.id + "-inplaceeditor";
509
- if ($(this.options.formId)) {
510
- // there's already a form with that name, don't specify an id
511
- this.options.formId = null;
512
- }
482
+ this.element = element = $(element);
483
+ this.prepareOptions();
484
+ this._controls = { };
485
+ arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
486
+ Object.extend(this.options, options || { });
487
+ if (!this.options.formId && this.element.id) {
488
+ this.options.formId = this.element.id + '-inplaceeditor';
489
+ if ($(this.options.formId))
490
+ this.options.formId = '';
513
491
  }
514
-
515
- if (this.options.externalControl) {
492
+ if (this.options.externalControl)
516
493
  this.options.externalControl = $(this.options.externalControl);
517
- }
518
-
519
- this.originalBackground = Element.getStyle(this.element, 'background-color');
520
- if (!this.originalBackground) {
521
- this.originalBackground = "transparent";
522
- }
523
-
494
+ if (!this.options.externalControl)
495
+ this.options.externalControlOnly = false;
496
+ this._originalBackground = this.element.getStyle('background-color') || 'transparent';
524
497
  this.element.title = this.options.clickToEditText;
525
-
526
- this.onclickListener = this.enterEditMode.bindAsEventListener(this);
527
- this.mouseoverListener = this.enterHover.bindAsEventListener(this);
528
- this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
529
- Event.observe(this.element, 'click', this.onclickListener);
530
- Event.observe(this.element, 'mouseover', this.mouseoverListener);
531
- Event.observe(this.element, 'mouseout', this.mouseoutListener);
532
- if (this.options.externalControl) {
533
- Event.observe(this.options.externalControl, 'click', this.onclickListener);
534
- Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
535
- Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
498
+ this._boundCancelHandler = this.handleFormCancellation.bind(this);
499
+ this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
500
+ this._boundFailureHandler = this.handleAJAXFailure.bind(this);
501
+ this._boundSubmitHandler = this.handleFormSubmission.bind(this);
502
+ this._boundWrapperHandler = this.wrapUp.bind(this);
503
+ this.registerListeners();
504
+ },
505
+ checkForEscapeOrReturn: function(e) {
506
+ if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
507
+ if (Event.KEY_ESC == e.keyCode)
508
+ this.handleFormCancellation(e);
509
+ else if (Event.KEY_RETURN == e.keyCode)
510
+ this.handleFormSubmission(e);
511
+ },
512
+ createControl: function(mode, handler, extraClasses) {
513
+ var control = this.options[mode + 'Control'];
514
+ var text = this.options[mode + 'Text'];
515
+ if ('button' == control) {
516
+ var btn = document.createElement('input');
517
+ btn.type = 'submit';
518
+ btn.value = text;
519
+ btn.className = 'editor_' + mode + '_button';
520
+ if ('cancel' == mode)
521
+ btn.onclick = this._boundCancelHandler;
522
+ this._form.appendChild(btn);
523
+ this._controls[mode] = btn;
524
+ } else if ('link' == control) {
525
+ var link = document.createElement('a');
526
+ link.href = '#';
527
+ link.appendChild(document.createTextNode(text));
528
+ link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
529
+ link.className = 'editor_' + mode + '_link';
530
+ if (extraClasses)
531
+ link.className += ' ' + extraClasses;
532
+ this._form.appendChild(link);
533
+ this._controls[mode] = link;
536
534
  }
537
535
  },
538
- enterEditMode: function(evt) {
539
- if (this.saving) return;
540
- if (this.editing) return;
541
- this.editing = true;
542
- this.onEnterEditMode();
543
- if (this.options.externalControl) {
544
- Element.hide(this.options.externalControl);
545
- }
546
- Element.hide(this.element);
547
- this.createForm();
548
- this.element.parentNode.insertBefore(this.form, this.element);
549
- if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
550
- // stop the event to avoid a page refresh in Safari
551
- if (evt) {
552
- Event.stop(evt);
553
- }
554
- return false;
555
- },
556
- createForm: function() {
557
- this.form = document.createElement("form");
558
- this.form.id = this.options.formId;
559
- Element.addClassName(this.form, this.options.formClassName)
560
- this.form.onsubmit = this.onSubmit.bind(this);
561
-
562
- this.createEditField();
563
-
564
- if (this.options.textarea) {
565
- var br = document.createElement("br");
566
- this.form.appendChild(br);
567
- }
568
-
569
- if (this.options.okButton) {
570
- okButton = document.createElement("input");
571
- okButton.type = "submit";
572
- okButton.value = this.options.okText;
573
- okButton.className = 'editor_ok_button';
574
- this.form.appendChild(okButton);
575
- }
576
-
577
- if (this.options.cancelLink) {
578
- cancelLink = document.createElement("a");
579
- cancelLink.href = "#";
580
- cancelLink.appendChild(document.createTextNode(this.options.cancelText));
581
- cancelLink.onclick = this.onclickCancel.bind(this);
582
- cancelLink.className = 'editor_cancel';
583
- this.form.appendChild(cancelLink);
584
- }
585
- },
586
- hasHTMLLineBreaks: function(string) {
587
- if (!this.options.handleLineBreaks) return false;
588
- return string.match(/<br/i) || string.match(/<p>/i);
589
- },
590
- convertHTMLLineBreaks: function(string) {
591
- return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
592
- },
593
536
  createEditField: function() {
594
- var text;
595
- if(this.options.loadTextURL) {
596
- text = this.options.loadingText;
597
- } else {
598
- text = this.getText();
599
- }
600
-
601
- var obj = this;
602
-
603
- if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
604
- this.options.textarea = false;
605
- var textField = document.createElement("input");
606
- textField.obj = this;
607
- textField.type = "text";
608
- textField.name = this.options.paramName;
609
- textField.value = text;
610
- textField.style.backgroundColor = this.options.highlightcolor;
611
- textField.className = 'editor_field';
537
+ var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
538
+ var fld;
539
+ if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
540
+ fld = document.createElement('input');
541
+ fld.type = 'text';
612
542
  var size = this.options.size || this.options.cols || 0;
613
- if (size != 0) textField.size = size;
614
- if (this.options.submitOnBlur)
615
- textField.onblur = this.onSubmit.bind(this);
616
- this.editField = textField;
543
+ if (0 < size) fld.size = size;
617
544
  } else {
618
- this.options.textarea = true;
619
- var textArea = document.createElement("textarea");
620
- textArea.obj = this;
621
- textArea.name = this.options.paramName;
622
- textArea.value = this.convertHTMLLineBreaks(text);
623
- textArea.rows = this.options.rows;
624
- textArea.cols = this.options.cols || 40;
625
- textArea.className = 'editor_field';
626
- if (this.options.submitOnBlur)
627
- textArea.onblur = this.onSubmit.bind(this);
628
- this.editField = textArea;
545
+ fld = document.createElement('textarea');
546
+ fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
547
+ fld.cols = this.options.cols || 40;
629
548
  }
630
-
631
- if(this.options.loadTextURL) {
549
+ fld.name = this.options.paramName;
550
+ fld.value = text; // No HTML breaks conversion anymore
551
+ fld.className = 'editor_field';
552
+ if (this.options.submitOnBlur)
553
+ fld.onblur = this._boundSubmitHandler;
554
+ this._controls.editor = fld;
555
+ if (this.options.loadTextURL)
632
556
  this.loadExternalText();
633
- }
634
- this.form.appendChild(this.editField);
557
+ this._form.appendChild(this._controls.editor);
558
+ },
559
+ createForm: function() {
560
+ var ipe = this;
561
+ function addText(mode, condition) {
562
+ var text = ipe.options['text' + mode + 'Controls'];
563
+ if (!text || condition === false) return;
564
+ ipe._form.appendChild(document.createTextNode(text));
565
+ };
566
+ this._form = $(document.createElement('form'));
567
+ this._form.id = this.options.formId;
568
+ this._form.addClassName(this.options.formClassName);
569
+ this._form.onsubmit = this._boundSubmitHandler;
570
+ this.createEditField();
571
+ if ('textarea' == this._controls.editor.tagName.toLowerCase())
572
+ this._form.appendChild(document.createElement('br'));
573
+ if (this.options.onFormCustomization)
574
+ this.options.onFormCustomization(this, this._form);
575
+ addText('Before', this.options.okControl || this.options.cancelControl);
576
+ this.createControl('ok', this._boundSubmitHandler);
577
+ addText('Between', this.options.okControl && this.options.cancelControl);
578
+ this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
579
+ addText('After', this.options.okControl || this.options.cancelControl);
580
+ },
581
+ destroy: function() {
582
+ if (this._oldInnerHTML)
583
+ this.element.innerHTML = this._oldInnerHTML;
584
+ this.leaveEditMode();
585
+ this.unregisterListeners();
586
+ },
587
+ enterEditMode: function(e) {
588
+ if (this._saving || this._editing) return;
589
+ this._editing = true;
590
+ this.triggerCallback('onEnterEditMode');
591
+ if (this.options.externalControl)
592
+ this.options.externalControl.hide();
593
+ this.element.hide();
594
+ this.createForm();
595
+ this.element.parentNode.insertBefore(this._form, this.element);
596
+ if (!this.options.loadTextURL)
597
+ this.postProcessEditField();
598
+ if (e) Event.stop(e);
599
+ },
600
+ enterHover: function(e) {
601
+ if (this.options.hoverClassName)
602
+ this.element.addClassName(this.options.hoverClassName);
603
+ if (this._saving) return;
604
+ this.triggerCallback('onEnterHover');
635
605
  },
636
606
  getText: function() {
637
607
  return this.element.innerHTML;
638
608
  },
639
- loadExternalText: function() {
640
- Element.addClassName(this.form, this.options.loadingClassName);
641
- this.editField.disabled = true;
642
- new Ajax.Request(
643
- this.options.loadTextURL,
644
- Object.extend({
645
- asynchronous: true,
646
- onComplete: this.onLoadedExternalText.bind(this)
647
- }, this.options.ajaxOptions)
648
- );
649
- },
650
- onLoadedExternalText: function(transport) {
651
- Element.removeClassName(this.form, this.options.loadingClassName);
652
- this.editField.disabled = false;
653
- this.editField.value = transport.responseText.stripTags();
654
- Field.scrollFreeActivate(this.editField);
655
- },
656
- onclickCancel: function() {
657
- this.onComplete();
658
- this.leaveEditMode();
659
- return false;
660
- },
661
- onFailure: function(transport) {
662
- this.options.onFailure(transport);
663
- if (this.oldInnerHTML) {
664
- this.element.innerHTML = this.oldInnerHTML;
665
- this.oldInnerHTML = null;
609
+ handleAJAXFailure: function(transport) {
610
+ this.triggerCallback('onFailure', transport);
611
+ if (this._oldInnerHTML) {
612
+ this.element.innerHTML = this._oldInnerHTML;
613
+ this._oldInnerHTML = null;
666
614
  }
667
- return false;
668
615
  },
669
- onSubmit: function() {
670
- // onLoading resets these so we need to save them away for the Ajax call
671
- var form = this.form;
672
- var value = this.editField.value;
673
-
674
- // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
675
- // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
676
- // to be displayed indefinitely
677
- this.onLoading();
678
-
679
- if (this.options.evalScripts) {
680
- new Ajax.Request(
681
- this.url, Object.extend({
682
- parameters: this.options.callback(form, value),
683
- onComplete: this.onComplete.bind(this),
684
- onFailure: this.onFailure.bind(this),
685
- asynchronous:true,
686
- evalScripts:true
687
- }, this.options.ajaxOptions));
688
- } else {
689
- new Ajax.Updater(
690
- { success: this.element,
691
- // don't update on failure (this could be an option)
692
- failure: null },
693
- this.url, Object.extend({
694
- parameters: this.options.callback(form, value),
695
- onComplete: this.onComplete.bind(this),
696
- onFailure: this.onFailure.bind(this)
697
- }, this.options.ajaxOptions));
698
- }
699
- // stop the event to avoid a page refresh in Safari
700
- if (arguments.length > 1) {
701
- Event.stop(arguments[0]);
616
+ handleFormCancellation: function(e) {
617
+ this.wrapUp();
618
+ if (e) Event.stop(e);
619
+ },
620
+ handleFormSubmission: function(e) {
621
+ var form = this._form;
622
+ var value = $F(this._controls.editor);
623
+ this.prepareSubmission();
624
+ var params = this.options.callback(form, value) || '';
625
+ if (Object.isString(params))
626
+ params = params.toQueryParams();
627
+ params.editorId = this.element.id;
628
+ if (this.options.htmlResponse) {
629
+ var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
630
+ Object.extend(options, {
631
+ parameters: params,
632
+ onComplete: this._boundWrapperHandler,
633
+ onFailure: this._boundFailureHandler
634
+ });
635
+ new Ajax.Updater({ success: this.element }, this.url, options);
636
+ } else {
637
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
638
+ Object.extend(options, {
639
+ parameters: params,
640
+ onComplete: this._boundWrapperHandler,
641
+ onFailure: this._boundFailureHandler
642
+ });
643
+ new Ajax.Request(this.url, options);
702
644
  }
703
- return false;
645
+ if (e) Event.stop(e);
646
+ },
647
+ leaveEditMode: function() {
648
+ this.element.removeClassName(this.options.savingClassName);
649
+ this.removeForm();
650
+ this.leaveHover();
651
+ this.element.style.backgroundColor = this._originalBackground;
652
+ this.element.show();
653
+ if (this.options.externalControl)
654
+ this.options.externalControl.show();
655
+ this._saving = false;
656
+ this._editing = false;
657
+ this._oldInnerHTML = null;
658
+ this.triggerCallback('onLeaveEditMode');
659
+ },
660
+ leaveHover: function(e) {
661
+ if (this.options.hoverClassName)
662
+ this.element.removeClassName(this.options.hoverClassName);
663
+ if (this._saving) return;
664
+ this.triggerCallback('onLeaveHover');
704
665
  },
705
- onLoading: function() {
706
- this.saving = true;
666
+ loadExternalText: function() {
667
+ this._form.addClassName(this.options.loadingClassName);
668
+ this._controls.editor.disabled = true;
669
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
670
+ Object.extend(options, {
671
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
672
+ onComplete: Prototype.emptyFunction,
673
+ onSuccess: function(transport) {
674
+ this._form.removeClassName(this.options.loadingClassName);
675
+ var text = transport.responseText;
676
+ if (this.options.stripLoadedTextTags)
677
+ text = text.stripTags();
678
+ this._controls.editor.value = text;
679
+ this._controls.editor.disabled = false;
680
+ this.postProcessEditField();
681
+ }.bind(this),
682
+ onFailure: this._boundFailureHandler
683
+ });
684
+ new Ajax.Request(this.options.loadTextURL, options);
685
+ },
686
+ postProcessEditField: function() {
687
+ var fpc = this.options.fieldPostCreation;
688
+ if (fpc)
689
+ $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
690
+ },
691
+ prepareOptions: function() {
692
+ this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
693
+ Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
694
+ [this._extraDefaultOptions].flatten().compact().each(function(defs) {
695
+ Object.extend(this.options, defs);
696
+ }.bind(this));
697
+ },
698
+ prepareSubmission: function() {
699
+ this._saving = true;
707
700
  this.removeForm();
708
701
  this.leaveHover();
709
702
  this.showSaving();
710
703
  },
704
+ registerListeners: function() {
705
+ this._listeners = { };
706
+ var listener;
707
+ $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
708
+ listener = this[pair.value].bind(this);
709
+ this._listeners[pair.key] = listener;
710
+ if (!this.options.externalControlOnly)
711
+ this.element.observe(pair.key, listener);
712
+ if (this.options.externalControl)
713
+ this.options.externalControl.observe(pair.key, listener);
714
+ }.bind(this));
715
+ },
716
+ removeForm: function() {
717
+ if (!this._form) return;
718
+ this._form.remove();
719
+ this._form = null;
720
+ this._controls = { };
721
+ },
711
722
  showSaving: function() {
712
- this.oldInnerHTML = this.element.innerHTML;
723
+ this._oldInnerHTML = this.element.innerHTML;
713
724
  this.element.innerHTML = this.options.savingText;
714
- Element.addClassName(this.element, this.options.savingClassName);
715
- this.element.style.backgroundColor = this.originalBackground;
716
- Element.show(this.element);
725
+ this.element.addClassName(this.options.savingClassName);
726
+ this.element.style.backgroundColor = this._originalBackground;
727
+ this.element.show();
717
728
  },
718
- removeForm: function() {
719
- if(this.form) {
720
- if (this.form.parentNode) Element.remove(this.form);
721
- this.form = null;
729
+ triggerCallback: function(cbName, arg) {
730
+ if ('function' == typeof this.options[cbName]) {
731
+ this.options[cbName](this, arg);
722
732
  }
723
733
  },
724
- enterHover: function() {
725
- if (this.saving) return;
726
- this.element.style.backgroundColor = this.options.highlightcolor;
727
- if (this.effect) {
728
- this.effect.cancel();
729
- }
730
- Element.addClassName(this.element, this.options.hoverClassName)
734
+ unregisterListeners: function() {
735
+ $H(this._listeners).each(function(pair) {
736
+ if (!this.options.externalControlOnly)
737
+ this.element.stopObserving(pair.key, pair.value);
738
+ if (this.options.externalControl)
739
+ this.options.externalControl.stopObserving(pair.key, pair.value);
740
+ }.bind(this));
731
741
  },
732
- leaveHover: function() {
733
- if (this.options.backgroundColor) {
734
- this.element.style.backgroundColor = this.oldBackground;
735
- }
736
- Element.removeClassName(this.element, this.options.hoverClassName)
737
- if (this.saving) return;
738
- this.effect = new Effect.Highlight(this.element, {
739
- startcolor: this.options.highlightcolor,
740
- endcolor: this.options.highlightendcolor,
741
- restorecolor: this.originalBackground
742
+ wrapUp: function(transport) {
743
+ this.leaveEditMode();
744
+ // Can't use triggerCallback due to backward compatibility: requires
745
+ // binding + direct element
746
+ this._boundComplete(transport, this.element);
747
+ }
748
+ });
749
+
750
+ Object.extend(Ajax.InPlaceEditor.prototype, {
751
+ dispose: Ajax.InPlaceEditor.prototype.destroy
752
+ });
753
+
754
+ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
755
+ initialize: function($super, element, url, options) {
756
+ this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
757
+ $super(element, url, options);
758
+ },
759
+
760
+ createEditField: function() {
761
+ var list = document.createElement('select');
762
+ list.name = this.options.paramName;
763
+ list.size = 1;
764
+ this._controls.editor = list;
765
+ this._collection = this.options.collection || [];
766
+ if (this.options.loadCollectionURL)
767
+ this.loadCollection();
768
+ else
769
+ this.checkForExternalText();
770
+ this._form.appendChild(this._controls.editor);
771
+ },
772
+
773
+ loadCollection: function() {
774
+ this._form.addClassName(this.options.loadingClassName);
775
+ this.showLoadingText(this.options.loadingCollectionText);
776
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
777
+ Object.extend(options, {
778
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
779
+ onComplete: Prototype.emptyFunction,
780
+ onSuccess: function(transport) {
781
+ var js = transport.responseText.strip();
782
+ if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
783
+ throw 'Server returned an invalid collection representation.';
784
+ this._collection = eval(js);
785
+ this.checkForExternalText();
786
+ }.bind(this),
787
+ onFailure: this.onFailure
742
788
  });
789
+ new Ajax.Request(this.options.loadCollectionURL, options);
743
790
  },
744
- leaveEditMode: function() {
745
- Element.removeClassName(this.element, this.options.savingClassName);
746
- this.removeForm();
747
- this.leaveHover();
748
- this.element.style.backgroundColor = this.originalBackground;
749
- Element.show(this.element);
750
- if (this.options.externalControl) {
751
- Element.show(this.options.externalControl);
791
+
792
+ showLoadingText: function(text) {
793
+ this._controls.editor.disabled = true;
794
+ var tempOption = this._controls.editor.firstChild;
795
+ if (!tempOption) {
796
+ tempOption = document.createElement('option');
797
+ tempOption.value = '';
798
+ this._controls.editor.appendChild(tempOption);
799
+ tempOption.selected = true;
752
800
  }
753
- this.editing = false;
754
- this.saving = false;
755
- this.oldInnerHTML = null;
756
- this.onLeaveEditMode();
801
+ tempOption.update((text || '').stripScripts().stripTags());
757
802
  },
758
- onComplete: function(transport) {
759
- this.leaveEditMode();
760
- this.options.onComplete.bind(this)(transport, this.element);
803
+
804
+ checkForExternalText: function() {
805
+ this._text = this.getText();
806
+ if (this.options.loadTextURL)
807
+ this.loadExternalText();
808
+ else
809
+ this.buildOptionList();
761
810
  },
762
- onEnterEditMode: function() {},
763
- onLeaveEditMode: function() {},
764
- dispose: function() {
765
- if (this.oldInnerHTML) {
766
- this.element.innerHTML = this.oldInnerHTML;
767
- }
768
- this.leaveEditMode();
769
- Event.stopObserving(this.element, 'click', this.onclickListener);
770
- Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
771
- Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
772
- if (this.options.externalControl) {
773
- Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
774
- Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
775
- Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
776
- }
811
+
812
+ loadExternalText: function() {
813
+ this.showLoadingText(this.options.loadingText);
814
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
815
+ Object.extend(options, {
816
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
817
+ onComplete: Prototype.emptyFunction,
818
+ onSuccess: function(transport) {
819
+ this._text = transport.responseText.strip();
820
+ this.buildOptionList();
821
+ }.bind(this),
822
+ onFailure: this.onFailure
823
+ });
824
+ new Ajax.Request(this.options.loadTextURL, options);
825
+ },
826
+
827
+ buildOptionList: function() {
828
+ this._form.removeClassName(this.options.loadingClassName);
829
+ this._collection = this._collection.map(function(entry) {
830
+ return 2 === entry.length ? entry : [entry, entry].flatten();
831
+ });
832
+ var marker = ('value' in this.options) ? this.options.value : this._text;
833
+ var textFound = this._collection.any(function(entry) {
834
+ return entry[0] == marker;
835
+ }.bind(this));
836
+ this._controls.editor.update('');
837
+ var option;
838
+ this._collection.each(function(entry, index) {
839
+ option = document.createElement('option');
840
+ option.value = entry[0];
841
+ option.selected = textFound ? entry[0] == marker : 0 == index;
842
+ option.appendChild(document.createTextNode(entry[1]));
843
+ this._controls.editor.appendChild(option);
844
+ }.bind(this));
845
+ this._controls.editor.disabled = false;
846
+ Field.scrollFreeActivate(this._controls.editor);
777
847
  }
778
- };
848
+ });
779
849
 
780
- Ajax.InPlaceCollectionEditor = Class.create();
781
- Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
782
- Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
783
- createEditField: function() {
784
- if (!this.cached_selectTag) {
785
- var selectTag = document.createElement("select");
786
- var collection = this.options.collection || [];
787
- var optionTag;
788
- collection.each(function(e,i) {
789
- optionTag = document.createElement("option");
790
- optionTag.value = (e instanceof Array) ? e[0] : e;
791
- if((typeof this.options.value == 'undefined') &&
792
- ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
793
- if(this.options.value==optionTag.value) optionTag.selected = true;
794
- optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
795
- selectTag.appendChild(optionTag);
796
- }.bind(this));
797
- this.cached_selectTag = selectTag;
798
- }
850
+ //**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
851
+ //**** This only exists for a while, in order to let ****
852
+ //**** users adapt to the new API. Read up on the new ****
853
+ //**** API and convert your code to it ASAP! ****
854
+
855
+ Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
856
+ if (!options) return;
857
+ function fallback(name, expr) {
858
+ if (name in options || expr === undefined) return;
859
+ options[name] = expr;
860
+ };
861
+ fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
862
+ options.cancelLink == options.cancelButton == false ? false : undefined)));
863
+ fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
864
+ options.okLink == options.okButton == false ? false : undefined)));
865
+ fallback('highlightColor', options.highlightcolor);
866
+ fallback('highlightEndColor', options.highlightendcolor);
867
+ };
799
868
 
800
- this.editField = this.cached_selectTag;
801
- if(this.options.loadTextURL) this.loadExternalText();
802
- this.form.appendChild(this.editField);
803
- this.options.callback = function(form, value) {
804
- return "value=" + encodeURIComponent(value);
869
+ Object.extend(Ajax.InPlaceEditor, {
870
+ DefaultOptions: {
871
+ ajaxOptions: { },
872
+ autoRows: 3, // Use when multi-line w/ rows == 1
873
+ cancelControl: 'link', // 'link'|'button'|false
874
+ cancelText: 'cancel',
875
+ clickToEditText: 'Click to edit',
876
+ externalControl: null, // id|elt
877
+ externalControlOnly: false,
878
+ fieldPostCreation: 'activate', // 'activate'|'focus'|false
879
+ formClassName: 'inplaceeditor-form',
880
+ formId: null, // id|elt
881
+ highlightColor: '#ffff99',
882
+ highlightEndColor: '#ffffff',
883
+ hoverClassName: '',
884
+ htmlResponse: true,
885
+ loadingClassName: 'inplaceeditor-loading',
886
+ loadingText: 'Loading...',
887
+ okControl: 'button', // 'link'|'button'|false
888
+ okText: 'ok',
889
+ paramName: 'value',
890
+ rows: 1, // If 1 and multi-line, uses autoRows
891
+ savingClassName: 'inplaceeditor-saving',
892
+ savingText: 'Saving...',
893
+ size: 0,
894
+ stripLoadedTextTags: false,
895
+ submitOnBlur: false,
896
+ textAfterControls: '',
897
+ textBeforeControls: '',
898
+ textBetweenControls: ''
899
+ },
900
+ DefaultCallbacks: {
901
+ callback: function(form) {
902
+ return Form.serialize(form);
903
+ },
904
+ onComplete: function(transport, element) {
905
+ // For backward compatibility, this one is bound to the IPE, and passes
906
+ // the element directly. It was too often customized, so we don't break it.
907
+ new Effect.Highlight(element, {
908
+ startcolor: this.options.highlightColor, keepBackgroundImage: true });
909
+ },
910
+ onEnterEditMode: null,
911
+ onEnterHover: function(ipe) {
912
+ ipe.element.style.backgroundColor = ipe.options.highlightColor;
913
+ if (ipe._effect)
914
+ ipe._effect.cancel();
915
+ },
916
+ onFailure: function(transport, ipe) {
917
+ alert('Error communication with the server: ' + transport.responseText.stripTags());
918
+ },
919
+ onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
920
+ onLeaveEditMode: null,
921
+ onLeaveHover: function(ipe) {
922
+ ipe._effect = new Effect.Highlight(ipe.element, {
923
+ startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
924
+ restorecolor: ipe._originalBackground, keepBackgroundImage: true
925
+ });
805
926
  }
927
+ },
928
+ Listeners: {
929
+ click: 'enterEditMode',
930
+ keydown: 'checkForEscapeOrReturn',
931
+ mouseover: 'enterHover',
932
+ mouseout: 'leaveHover'
806
933
  }
807
934
  });
808
935
 
936
+ Ajax.InPlaceCollectionEditor.DefaultOptions = {
937
+ loadingCollectionText: 'Loading options...'
938
+ };
939
+
809
940
  // Delayed observer, like Form.Element.Observer,
810
941
  // but waits for delay after last key input
811
942
  // Ideal for live-search fields
812
943
 
813
- Form.Element.DelayedObserver = Class.create();
814
- Form.Element.DelayedObserver.prototype = {
944
+ Form.Element.DelayedObserver = Class.create({
815
945
  initialize: function(element, delay, callback) {
816
946
  this.delay = delay || 0.5;
817
947
  this.element = $(element);
@@ -830,4 +960,4 @@ Form.Element.DelayedObserver.prototype = {
830
960
  this.timer = null;
831
961
  this.callback(this.element, $F(this.element));
832
962
  }
833
- };
963
+ });