gettext 1.91.0 → 1.92.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ });