apipie-rails 0.3.4 → 0.3.5
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.
- checksums.yaml +15 -0
- data/CHANGELOG.md +27 -0
- data/README.rst +18 -11
- data/app/helpers/apipie_helper.rb +1 -0
- data/app/views/apipie/apipies/index.html.erb +3 -0
- data/app/views/apipie/apipies/plain.html.erb +6 -0
- data/app/views/apipie/apipies/resource.html.erb +3 -0
- data/app/views/apipie/apipies/static.html.erb +6 -0
- data/config/locales/es.yml +28 -0
- data/config/locales/pl.yml +29 -0
- data/config/locales/ru.yml +29 -0
- data/config/locales/zh-CN.yml +28 -0
- data/config/locales/zh-TW.yml +28 -0
- data/images/screenshot-1.png +0 -0
- data/images/screenshot-2.png +0 -0
- data/lib/apipie/dsl_definition.rb +9 -1
- data/lib/apipie/extractor/writer.rb +1 -1
- data/lib/apipie/method_description.rb +9 -2
- data/lib/apipie/param_description.rb +13 -5
- data/lib/apipie/version.rb +1 -1
- data/lib/tasks/apipie.rake +3 -3
- data/spec/controllers/users_controller_spec.rb +2 -0
- data/spec/dummy/app/controllers/users_controller.rb +3 -1
- data/spec/lib/extractor/writer_spec.rb +6 -0
- data/spec/lib/param_description_spec.rb +27 -0
- data/spec/lib/rake_spec.rb +4 -1
- metadata +12 -37
- data/spec/dummy/public/javascripts/application.js +0 -2
- data/spec/dummy/public/javascripts/controls.js +0 -965
- data/spec/dummy/public/javascripts/dragdrop.js +0 -974
- data/spec/dummy/public/javascripts/effects.js +0 -1123
- data/spec/dummy/public/javascripts/prototype.js +0 -6001
- data/spec/dummy/public/javascripts/rails.js +0 -202
data/lib/apipie/version.rb
CHANGED
data/lib/tasks/apipie.rake
CHANGED
@@ -19,7 +19,7 @@ namespace :apipie do
|
|
19
19
|
task :static, [:version] => :environment do |t, args|
|
20
20
|
with_loaded_documentation do
|
21
21
|
args.with_defaults(:version => Apipie.configuration.default_version)
|
22
|
-
out = ENV["OUT"] || File.join(::Rails.root,
|
22
|
+
out = ENV["OUT"] || File.join(::Rails.root, Apipie.configuration.doc_path, 'apidoc')
|
23
23
|
subdir = File.basename(out)
|
24
24
|
copy_jscss(out)
|
25
25
|
Apipie.configuration.version_in_url = false
|
@@ -43,7 +43,7 @@ namespace :apipie do
|
|
43
43
|
task :static_json, [:version] => :environment do |t, args|
|
44
44
|
with_loaded_documentation do
|
45
45
|
args.with_defaults(:version => Apipie.configuration.default_version)
|
46
|
-
out = ENV["OUT"] || File.join(::Rails.root,
|
46
|
+
out = ENV["OUT"] || File.join(::Rails.root, Apipie.configuration.doc_path, 'apidoc')
|
47
47
|
([nil] + Apipie.configuration.languages).each do |lang|
|
48
48
|
doc = Apipie.to_json(args[:version], nil, nil, lang)
|
49
49
|
generate_json_page(out, doc, lang)
|
@@ -232,7 +232,7 @@ MESSAGE
|
|
232
232
|
|
233
233
|
desc "Convert your examples from the old yaml into the new json format"
|
234
234
|
task :convert_examples => :environment do
|
235
|
-
yaml_examples_file = File.join(Rails.root,
|
235
|
+
yaml_examples_file = File.join(Rails.root, Apipie.configuration.doc_path, "apipie_examples.yml")
|
236
236
|
if File.exists?(yaml_examples_file)
|
237
237
|
#if SafeYAML gem is enabled, it will load examples as an array of Hash, instead of hash
|
238
238
|
if defined? SafeYAML
|
@@ -402,6 +402,7 @@ describe UsersController do
|
|
402
402
|
context "the key is valid" do
|
403
403
|
it "should contain reference to another method" do
|
404
404
|
api = Apipie[UsersController, :see_another]
|
405
|
+
api.show.should == false
|
405
406
|
see = api.see.first
|
406
407
|
see.see_url.should == Apipie[UsersController, :create].doc_url
|
407
408
|
see.link.should == 'development#users#create'
|
@@ -571,6 +572,7 @@ describe UsersController do
|
|
571
572
|
:expected_type=>"numeric"},
|
572
573
|
],
|
573
574
|
:name => 'two_urls',
|
575
|
+
:show => true,
|
574
576
|
:apis => [
|
575
577
|
{
|
576
578
|
:http_method => 'GET',
|
@@ -169,6 +169,7 @@ class UsersController < ApplicationController
|
|
169
169
|
More builder documentation can be found at http://builder.rubyforge.org.
|
170
170
|
eos
|
171
171
|
api :GET, "/users/:id", "Show user profile"
|
172
|
+
show false
|
172
173
|
formats ['json', 'jsonp']
|
173
174
|
error 401, "Unauthorized"
|
174
175
|
error :code => 404, :description => "Not Found"
|
@@ -256,9 +257,10 @@ class UsersController < ApplicationController
|
|
256
257
|
end
|
257
258
|
|
258
259
|
api :GET, '/users/see_another', 'Boring method'
|
260
|
+
show false
|
259
261
|
see 'development#users#create'
|
260
262
|
see 'development#users#index', "very interesting method reference"
|
261
|
-
desc 'This method is boring, look at users#create'
|
263
|
+
desc 'This method is boring, look at users#create. It is hidden from documentation.'
|
262
264
|
def see_another
|
263
265
|
render :text => 'This is very similar to create action'
|
264
266
|
end
|
@@ -57,6 +57,12 @@ describe Apipie::Extractor::Writer do
|
|
57
57
|
}
|
58
58
|
}
|
59
59
|
|
60
|
+
describe "with doc_path overriden in configuration" do
|
61
|
+
it "should use the doc_path specified in configuration" do
|
62
|
+
Apipie.configuration.doc_path = "user_specified_doc_path"
|
63
|
+
writer_class.examples_file.should eq(File.join(Rails.root, "user_specified_doc_path", "apipie_examples.json"))
|
64
|
+
end
|
65
|
+
end
|
60
66
|
|
61
67
|
describe "storing of examples" do
|
62
68
|
before do
|
@@ -172,6 +172,33 @@ describe Apipie::ParamDescription do
|
|
172
172
|
|
173
173
|
end
|
174
174
|
|
175
|
+
describe "required params on given actions" do
|
176
|
+
let(:method_desc) do
|
177
|
+
Apipie::MethodDescription.new(:create, resource_desc, dsl_data)
|
178
|
+
end
|
179
|
+
|
180
|
+
context "when the param is required for current action" do
|
181
|
+
it "should set param as required" do
|
182
|
+
param = Apipie::ParamDescription.new(method_desc, :required, String, 'description','required' => :create)
|
183
|
+
param.required.should be_true
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "when the param is required for multiple actions" do
|
188
|
+
it "should set param as required if it match current action" do
|
189
|
+
param = Apipie::ParamDescription.new(method_desc, :required, String, 'description','required' => [:update, :create])
|
190
|
+
param.required.should be_true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "when the param is not required for current action" do
|
195
|
+
it "should set param as not required" do
|
196
|
+
param = Apipie::ParamDescription.new(method_desc, :required, String, 'description','required' => :update)
|
197
|
+
param.required.should be_false
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
175
202
|
describe "required params in action aware validator" do
|
176
203
|
|
177
204
|
subject { method_description.params[:user].validator.params_ordered }
|
data/spec/lib/rake_spec.rb
CHANGED
@@ -3,7 +3,10 @@ require 'spec_helper'
|
|
3
3
|
describe 'rake tasks' do
|
4
4
|
include_context "rake"
|
5
5
|
|
6
|
+
let(:doc_path) { "user_specified_doc_path" }
|
7
|
+
|
6
8
|
before do
|
9
|
+
Apipie.configuration.doc_path = doc_path
|
7
10
|
Apipie.stub(:reload_documentation)
|
8
11
|
subject.invoke(*task_args)
|
9
12
|
end
|
@@ -15,7 +18,7 @@ describe 'rake tasks' do
|
|
15
18
|
end
|
16
19
|
|
17
20
|
let(:doc_output) do
|
18
|
-
File.join(::Rails.root,
|
21
|
+
File.join(::Rails.root, doc_path, 'apidoc')
|
19
22
|
end
|
20
23
|
|
21
24
|
after do
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apipie-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.5
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Pavel Pokorny
|
@@ -10,12 +9,11 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date: 2015-
|
12
|
+
date: 2015-10-21 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: rails
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
18
|
- - ! '>='
|
21
19
|
- !ruby/object:Gem::Version
|
@@ -23,7 +21,6 @@ dependencies:
|
|
23
21
|
type: :development
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
25
|
- - ! '>='
|
29
26
|
- !ruby/object:Gem::Version
|
@@ -31,7 +28,6 @@ dependencies:
|
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: json
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
32
|
- - ! '>='
|
37
33
|
- !ruby/object:Gem::Version
|
@@ -39,7 +35,6 @@ dependencies:
|
|
39
35
|
type: :runtime
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
39
|
- - ! '>='
|
45
40
|
- !ruby/object:Gem::Version
|
@@ -47,7 +42,6 @@ dependencies:
|
|
47
42
|
- !ruby/object:Gem::Dependency
|
48
43
|
name: rspec-rails
|
49
44
|
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
45
|
requirements:
|
52
46
|
- - ~>
|
53
47
|
- !ruby/object:Gem::Version
|
@@ -55,7 +49,6 @@ dependencies:
|
|
55
49
|
type: :development
|
56
50
|
prerelease: false
|
57
51
|
version_requirements: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
52
|
requirements:
|
60
53
|
- - ~>
|
61
54
|
- !ruby/object:Gem::Version
|
@@ -63,7 +56,6 @@ dependencies:
|
|
63
56
|
- !ruby/object:Gem::Dependency
|
64
57
|
name: sqlite3
|
65
58
|
requirement: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
59
|
requirements:
|
68
60
|
- - ! '>='
|
69
61
|
- !ruby/object:Gem::Version
|
@@ -71,7 +63,6 @@ dependencies:
|
|
71
63
|
type: :development
|
72
64
|
prerelease: false
|
73
65
|
version_requirements: !ruby/object:Gem::Requirement
|
74
|
-
none: false
|
75
66
|
requirements:
|
76
67
|
- - ! '>='
|
77
68
|
- !ruby/object:Gem::Version
|
@@ -79,7 +70,6 @@ dependencies:
|
|
79
70
|
- !ruby/object:Gem::Dependency
|
80
71
|
name: minitest
|
81
72
|
requirement: !ruby/object:Gem::Requirement
|
82
|
-
none: false
|
83
73
|
requirements:
|
84
74
|
- - ! '>='
|
85
75
|
- !ruby/object:Gem::Version
|
@@ -87,7 +77,6 @@ dependencies:
|
|
87
77
|
type: :development
|
88
78
|
prerelease: false
|
89
79
|
version_requirements: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
80
|
requirements:
|
92
81
|
- - ! '>='
|
93
82
|
- !ruby/object:Gem::Version
|
@@ -95,7 +84,6 @@ dependencies:
|
|
95
84
|
- !ruby/object:Gem::Dependency
|
96
85
|
name: maruku
|
97
86
|
requirement: !ruby/object:Gem::Requirement
|
98
|
-
none: false
|
99
87
|
requirements:
|
100
88
|
- - ! '>='
|
101
89
|
- !ruby/object:Gem::Version
|
@@ -103,7 +91,6 @@ dependencies:
|
|
103
91
|
type: :development
|
104
92
|
prerelease: false
|
105
93
|
version_requirements: !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
94
|
requirements:
|
108
95
|
- - ! '>='
|
109
96
|
- !ruby/object:Gem::Version
|
@@ -111,7 +98,6 @@ dependencies:
|
|
111
98
|
- !ruby/object:Gem::Dependency
|
112
99
|
name: RedCloth
|
113
100
|
requirement: !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
101
|
requirements:
|
116
102
|
- - ! '>='
|
117
103
|
- !ruby/object:Gem::Version
|
@@ -119,7 +105,6 @@ dependencies:
|
|
119
105
|
type: :development
|
120
106
|
prerelease: false
|
121
107
|
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
108
|
requirements:
|
124
109
|
- - ! '>='
|
125
110
|
- !ruby/object:Gem::Version
|
@@ -127,7 +112,6 @@ dependencies:
|
|
127
112
|
- !ruby/object:Gem::Dependency
|
128
113
|
name: rake
|
129
114
|
requirement: !ruby/object:Gem::Requirement
|
130
|
-
none: false
|
131
115
|
requirements:
|
132
116
|
- - ! '>='
|
133
117
|
- !ruby/object:Gem::Version
|
@@ -135,7 +119,6 @@ dependencies:
|
|
135
119
|
type: :development
|
136
120
|
prerelease: false
|
137
121
|
version_requirements: !ruby/object:Gem::Requirement
|
138
|
-
none: false
|
139
122
|
requirements:
|
140
123
|
- - ! '>='
|
141
124
|
- !ruby/object:Gem::Version
|
@@ -143,7 +126,6 @@ dependencies:
|
|
143
126
|
- !ruby/object:Gem::Dependency
|
144
127
|
name: rdoc
|
145
128
|
requirement: !ruby/object:Gem::Requirement
|
146
|
-
none: false
|
147
129
|
requirements:
|
148
130
|
- - ! '>='
|
149
131
|
- !ruby/object:Gem::Version
|
@@ -151,7 +133,6 @@ dependencies:
|
|
151
133
|
type: :development
|
152
134
|
prerelease: false
|
153
135
|
version_requirements: !ruby/object:Gem::Requirement
|
154
|
-
none: false
|
155
136
|
requirements:
|
156
137
|
- - ! '>='
|
157
138
|
- !ruby/object:Gem::Version
|
@@ -206,7 +187,14 @@ files:
|
|
206
187
|
- app/views/apipie/apipies/static.html.erb
|
207
188
|
- app/views/layouts/apipie/apipie.html.erb
|
208
189
|
- config/locales/en.yml
|
190
|
+
- config/locales/es.yml
|
191
|
+
- config/locales/pl.yml
|
209
192
|
- config/locales/pt-BR.yml
|
193
|
+
- config/locales/ru.yml
|
194
|
+
- config/locales/zh-CN.yml
|
195
|
+
- config/locales/zh-TW.yml
|
196
|
+
- images/screenshot-1.png
|
197
|
+
- images/screenshot-2.png
|
210
198
|
- lib/apipie-rails.rb
|
211
199
|
- lib/apipie/apipie_module.rb
|
212
200
|
- lib/apipie/application.rb
|
@@ -285,12 +273,6 @@ files:
|
|
285
273
|
- spec/dummy/public/422.html
|
286
274
|
- spec/dummy/public/500.html
|
287
275
|
- spec/dummy/public/favicon.ico
|
288
|
-
- spec/dummy/public/javascripts/application.js
|
289
|
-
- spec/dummy/public/javascripts/controls.js
|
290
|
-
- spec/dummy/public/javascripts/dragdrop.js
|
291
|
-
- spec/dummy/public/javascripts/effects.js
|
292
|
-
- spec/dummy/public/javascripts/prototype.js
|
293
|
-
- spec/dummy/public/javascripts/rails.js
|
294
276
|
- spec/dummy/public/stylesheets/.gitkeep
|
295
277
|
- spec/dummy/script/rails
|
296
278
|
- spec/lib/application_spec.rb
|
@@ -308,27 +290,26 @@ files:
|
|
308
290
|
- spec/support/rake.rb
|
309
291
|
homepage: http://github.com/Pajk/apipie-rails
|
310
292
|
licenses: []
|
293
|
+
metadata: {}
|
311
294
|
post_install_message:
|
312
295
|
rdoc_options: []
|
313
296
|
require_paths:
|
314
297
|
- lib
|
315
298
|
required_ruby_version: !ruby/object:Gem::Requirement
|
316
|
-
none: false
|
317
299
|
requirements:
|
318
300
|
- - ! '>='
|
319
301
|
- !ruby/object:Gem::Version
|
320
302
|
version: '0'
|
321
303
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
322
|
-
none: false
|
323
304
|
requirements:
|
324
305
|
- - ! '>='
|
325
306
|
- !ruby/object:Gem::Version
|
326
307
|
version: '0'
|
327
308
|
requirements: []
|
328
309
|
rubyforge_project:
|
329
|
-
rubygems_version:
|
310
|
+
rubygems_version: 2.4.8
|
330
311
|
signing_key:
|
331
|
-
specification_version:
|
312
|
+
specification_version: 4
|
332
313
|
summary: Rails REST API documentation tool
|
333
314
|
test_files:
|
334
315
|
- spec/controllers/api/v1/architectures_controller_spec.rb
|
@@ -376,12 +357,6 @@ test_files:
|
|
376
357
|
- spec/dummy/public/422.html
|
377
358
|
- spec/dummy/public/500.html
|
378
359
|
- spec/dummy/public/favicon.ico
|
379
|
-
- spec/dummy/public/javascripts/application.js
|
380
|
-
- spec/dummy/public/javascripts/controls.js
|
381
|
-
- spec/dummy/public/javascripts/dragdrop.js
|
382
|
-
- spec/dummy/public/javascripts/effects.js
|
383
|
-
- spec/dummy/public/javascripts/prototype.js
|
384
|
-
- spec/dummy/public/javascripts/rails.js
|
385
360
|
- spec/dummy/public/stylesheets/.gitkeep
|
386
361
|
- spec/dummy/script/rails
|
387
362
|
- spec/lib/application_spec.rb
|
@@ -1,965 +0,0 @@
|
|
1
|
-
// script.aculo.us controls.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
|
2
|
-
|
3
|
-
// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
4
|
-
// (c) 2005-2009 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
5
|
-
// (c) 2005-2009 Jon Tirsen (http://www.tirsen.com)
|
6
|
-
// Contributors:
|
7
|
-
// Richard Livsey
|
8
|
-
// Rahul Bhargava
|
9
|
-
// Rob Wills
|
10
|
-
//
|
11
|
-
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
12
|
-
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
13
|
-
|
14
|
-
// Autocompleter.Base handles all the autocompletion functionality
|
15
|
-
// that's independent of the data source for autocompletion. This
|
16
|
-
// includes drawing the autocompletion menu, observing keyboard
|
17
|
-
// and mouse events, and similar.
|
18
|
-
//
|
19
|
-
// Specific autocompleters need to provide, at the very least,
|
20
|
-
// a getUpdatedChoices function that will be invoked every time
|
21
|
-
// the text inside the monitored textbox changes. This method
|
22
|
-
// should get the text for which to provide autocompletion by
|
23
|
-
// invoking this.getToken(), NOT by directly accessing
|
24
|
-
// this.element.value. This is to allow incremental tokenized
|
25
|
-
// autocompletion. Specific auto-completion logic (AJAX, etc)
|
26
|
-
// belongs in getUpdatedChoices.
|
27
|
-
//
|
28
|
-
// Tokenized incremental autocompletion is enabled automatically
|
29
|
-
// when an autocompleter is instantiated with the 'tokens' option
|
30
|
-
// in the options parameter, e.g.:
|
31
|
-
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
|
32
|
-
// will incrementally autocomplete with a comma as the token.
|
33
|
-
// Additionally, ',' in the above example can be replaced with
|
34
|
-
// a token array, e.g. { tokens: [',', '\n'] } which
|
35
|
-
// enables autocompletion on multiple tokens. This is most
|
36
|
-
// useful when one of the tokens is \n (a newline), as it
|
37
|
-
// allows smart autocompletion after linebreaks.
|
38
|
-
|
39
|
-
if(typeof Effect == 'undefined')
|
40
|
-
throw("controls.js requires including script.aculo.us' effects.js library");
|
41
|
-
|
42
|
-
var Autocompleter = { };
|
43
|
-
Autocompleter.Base = Class.create({
|
44
|
-
baseInitialize: function(element, update, options) {
|
45
|
-
element = $(element);
|
46
|
-
this.element = element;
|
47
|
-
this.update = $(update);
|
48
|
-
this.hasFocus = false;
|
49
|
-
this.changed = false;
|
50
|
-
this.active = false;
|
51
|
-
this.index = 0;
|
52
|
-
this.entryCount = 0;
|
53
|
-
this.oldElementValue = this.element.value;
|
54
|
-
|
55
|
-
if(this.setOptions)
|
56
|
-
this.setOptions(options);
|
57
|
-
else
|
58
|
-
this.options = options || { };
|
59
|
-
|
60
|
-
this.options.paramName = this.options.paramName || this.element.name;
|
61
|
-
this.options.tokens = this.options.tokens || [];
|
62
|
-
this.options.frequency = this.options.frequency || 0.4;
|
63
|
-
this.options.minChars = this.options.minChars || 1;
|
64
|
-
this.options.onShow = this.options.onShow ||
|
65
|
-
function(element, update){
|
66
|
-
if(!update.style.position || update.style.position=='absolute') {
|
67
|
-
update.style.position = 'absolute';
|
68
|
-
Position.clone(element, update, {
|
69
|
-
setHeight: false,
|
70
|
-
offsetTop: element.offsetHeight
|
71
|
-
});
|
72
|
-
}
|
73
|
-
Effect.Appear(update,{duration:0.15});
|
74
|
-
};
|
75
|
-
this.options.onHide = this.options.onHide ||
|
76
|
-
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
|
77
|
-
|
78
|
-
if(typeof(this.options.tokens) == 'string')
|
79
|
-
this.options.tokens = new Array(this.options.tokens);
|
80
|
-
// Force carriage returns as token delimiters anyway
|
81
|
-
if (!this.options.tokens.include('\n'))
|
82
|
-
this.options.tokens.push('\n');
|
83
|
-
|
84
|
-
this.observer = null;
|
85
|
-
|
86
|
-
this.element.setAttribute('autocomplete','off');
|
87
|
-
|
88
|
-
Element.hide(this.update);
|
89
|
-
|
90
|
-
Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
|
91
|
-
Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
|
92
|
-
},
|
93
|
-
|
94
|
-
show: function() {
|
95
|
-
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
96
|
-
if(!this.iefix &&
|
97
|
-
(Prototype.Browser.IE) &&
|
98
|
-
(Element.getStyle(this.update, 'position')=='absolute')) {
|
99
|
-
new Insertion.After(this.update,
|
100
|
-
'<iframe id="' + this.update.id + '_iefix" '+
|
101
|
-
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
102
|
-
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
|
103
|
-
this.iefix = $(this.update.id+'_iefix');
|
104
|
-
}
|
105
|
-
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
|
106
|
-
},
|
107
|
-
|
108
|
-
fixIEOverlapping: function() {
|
109
|
-
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
|
110
|
-
this.iefix.style.zIndex = 1;
|
111
|
-
this.update.style.zIndex = 2;
|
112
|
-
Element.show(this.iefix);
|
113
|
-
},
|
114
|
-
|
115
|
-
hide: function() {
|
116
|
-
this.stopIndicator();
|
117
|
-
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
|
118
|
-
if(this.iefix) Element.hide(this.iefix);
|
119
|
-
},
|
120
|
-
|
121
|
-
startIndicator: function() {
|
122
|
-
if(this.options.indicator) Element.show(this.options.indicator);
|
123
|
-
},
|
124
|
-
|
125
|
-
stopIndicator: function() {
|
126
|
-
if(this.options.indicator) Element.hide(this.options.indicator);
|
127
|
-
},
|
128
|
-
|
129
|
-
onKeyPress: function(event) {
|
130
|
-
if(this.active)
|
131
|
-
switch(event.keyCode) {
|
132
|
-
case Event.KEY_TAB:
|
133
|
-
case Event.KEY_RETURN:
|
134
|
-
this.selectEntry();
|
135
|
-
Event.stop(event);
|
136
|
-
case Event.KEY_ESC:
|
137
|
-
this.hide();
|
138
|
-
this.active = false;
|
139
|
-
Event.stop(event);
|
140
|
-
return;
|
141
|
-
case Event.KEY_LEFT:
|
142
|
-
case Event.KEY_RIGHT:
|
143
|
-
return;
|
144
|
-
case Event.KEY_UP:
|
145
|
-
this.markPrevious();
|
146
|
-
this.render();
|
147
|
-
Event.stop(event);
|
148
|
-
return;
|
149
|
-
case Event.KEY_DOWN:
|
150
|
-
this.markNext();
|
151
|
-
this.render();
|
152
|
-
Event.stop(event);
|
153
|
-
return;
|
154
|
-
}
|
155
|
-
else
|
156
|
-
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
|
157
|
-
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
|
158
|
-
|
159
|
-
this.changed = true;
|
160
|
-
this.hasFocus = true;
|
161
|
-
|
162
|
-
if(this.observer) clearTimeout(this.observer);
|
163
|
-
this.observer =
|
164
|
-
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
|
165
|
-
},
|
166
|
-
|
167
|
-
activate: function() {
|
168
|
-
this.changed = false;
|
169
|
-
this.hasFocus = true;
|
170
|
-
this.getUpdatedChoices();
|
171
|
-
},
|
172
|
-
|
173
|
-
onHover: function(event) {
|
174
|
-
var element = Event.findElement(event, 'LI');
|
175
|
-
if(this.index != element.autocompleteIndex)
|
176
|
-
{
|
177
|
-
this.index = element.autocompleteIndex;
|
178
|
-
this.render();
|
179
|
-
}
|
180
|
-
Event.stop(event);
|
181
|
-
},
|
182
|
-
|
183
|
-
onClick: function(event) {
|
184
|
-
var element = Event.findElement(event, 'LI');
|
185
|
-
this.index = element.autocompleteIndex;
|
186
|
-
this.selectEntry();
|
187
|
-
this.hide();
|
188
|
-
},
|
189
|
-
|
190
|
-
onBlur: function(event) {
|
191
|
-
// needed to make click events working
|
192
|
-
setTimeout(this.hide.bind(this), 250);
|
193
|
-
this.hasFocus = false;
|
194
|
-
this.active = false;
|
195
|
-
},
|
196
|
-
|
197
|
-
render: function() {
|
198
|
-
if(this.entryCount > 0) {
|
199
|
-
for (var i = 0; i < this.entryCount; i++)
|
200
|
-
this.index==i ?
|
201
|
-
Element.addClassName(this.getEntry(i),"selected") :
|
202
|
-
Element.removeClassName(this.getEntry(i),"selected");
|
203
|
-
if(this.hasFocus) {
|
204
|
-
this.show();
|
205
|
-
this.active = true;
|
206
|
-
}
|
207
|
-
} else {
|
208
|
-
this.active = false;
|
209
|
-
this.hide();
|
210
|
-
}
|
211
|
-
},
|
212
|
-
|
213
|
-
markPrevious: function() {
|
214
|
-
if(this.index > 0) this.index--;
|
215
|
-
else this.index = this.entryCount-1;
|
216
|
-
this.getEntry(this.index).scrollIntoView(true);
|
217
|
-
},
|
218
|
-
|
219
|
-
markNext: function() {
|
220
|
-
if(this.index < this.entryCount-1) this.index++;
|
221
|
-
else this.index = 0;
|
222
|
-
this.getEntry(this.index).scrollIntoView(false);
|
223
|
-
},
|
224
|
-
|
225
|
-
getEntry: function(index) {
|
226
|
-
return this.update.firstChild.childNodes[index];
|
227
|
-
},
|
228
|
-
|
229
|
-
getCurrentEntry: function() {
|
230
|
-
return this.getEntry(this.index);
|
231
|
-
},
|
232
|
-
|
233
|
-
selectEntry: function() {
|
234
|
-
this.active = false;
|
235
|
-
this.updateElement(this.getCurrentEntry());
|
236
|
-
},
|
237
|
-
|
238
|
-
updateElement: function(selectedElement) {
|
239
|
-
if (this.options.updateElement) {
|
240
|
-
this.options.updateElement(selectedElement);
|
241
|
-
return;
|
242
|
-
}
|
243
|
-
var value = '';
|
244
|
-
if (this.options.select) {
|
245
|
-
var nodes = $(selectedElement).select('.' + this.options.select) || [];
|
246
|
-
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
|
247
|
-
} else
|
248
|
-
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
|
249
|
-
|
250
|
-
var bounds = this.getTokenBounds();
|
251
|
-
if (bounds[0] != -1) {
|
252
|
-
var newValue = this.element.value.substr(0, bounds[0]);
|
253
|
-
var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
|
254
|
-
if (whitespace)
|
255
|
-
newValue += whitespace[0];
|
256
|
-
this.element.value = newValue + value + this.element.value.substr(bounds[1]);
|
257
|
-
} else {
|
258
|
-
this.element.value = value;
|
259
|
-
}
|
260
|
-
this.oldElementValue = this.element.value;
|
261
|
-
this.element.focus();
|
262
|
-
|
263
|
-
if (this.options.afterUpdateElement)
|
264
|
-
this.options.afterUpdateElement(this.element, selectedElement);
|
265
|
-
},
|
266
|
-
|
267
|
-
updateChoices: function(choices) {
|
268
|
-
if(!this.changed && this.hasFocus) {
|
269
|
-
this.update.innerHTML = choices;
|
270
|
-
Element.cleanWhitespace(this.update);
|
271
|
-
Element.cleanWhitespace(this.update.down());
|
272
|
-
|
273
|
-
if(this.update.firstChild && this.update.down().childNodes) {
|
274
|
-
this.entryCount =
|
275
|
-
this.update.down().childNodes.length;
|
276
|
-
for (var i = 0; i < this.entryCount; i++) {
|
277
|
-
var entry = this.getEntry(i);
|
278
|
-
entry.autocompleteIndex = i;
|
279
|
-
this.addObservers(entry);
|
280
|
-
}
|
281
|
-
} else {
|
282
|
-
this.entryCount = 0;
|
283
|
-
}
|
284
|
-
|
285
|
-
this.stopIndicator();
|
286
|
-
this.index = 0;
|
287
|
-
|
288
|
-
if(this.entryCount==1 && this.options.autoSelect) {
|
289
|
-
this.selectEntry();
|
290
|
-
this.hide();
|
291
|
-
} else {
|
292
|
-
this.render();
|
293
|
-
}
|
294
|
-
}
|
295
|
-
},
|
296
|
-
|
297
|
-
addObservers: function(element) {
|
298
|
-
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
|
299
|
-
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
|
300
|
-
},
|
301
|
-
|
302
|
-
onObserverEvent: function() {
|
303
|
-
this.changed = false;
|
304
|
-
this.tokenBounds = null;
|
305
|
-
if(this.getToken().length>=this.options.minChars) {
|
306
|
-
this.getUpdatedChoices();
|
307
|
-
} else {
|
308
|
-
this.active = false;
|
309
|
-
this.hide();
|
310
|
-
}
|
311
|
-
this.oldElementValue = this.element.value;
|
312
|
-
},
|
313
|
-
|
314
|
-
getToken: function() {
|
315
|
-
var bounds = this.getTokenBounds();
|
316
|
-
return this.element.value.substring(bounds[0], bounds[1]).strip();
|
317
|
-
},
|
318
|
-
|
319
|
-
getTokenBounds: function() {
|
320
|
-
if (null != this.tokenBounds) return this.tokenBounds;
|
321
|
-
var value = this.element.value;
|
322
|
-
if (value.strip().empty()) return [-1, 0];
|
323
|
-
var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
|
324
|
-
var offset = (diff == this.oldElementValue.length ? 1 : 0);
|
325
|
-
var prevTokenPos = -1, nextTokenPos = value.length;
|
326
|
-
var tp;
|
327
|
-
for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
|
328
|
-
tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
|
329
|
-
if (tp > prevTokenPos) prevTokenPos = tp;
|
330
|
-
tp = value.indexOf(this.options.tokens[index], diff + offset);
|
331
|
-
if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
|
332
|
-
}
|
333
|
-
return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
|
334
|
-
}
|
335
|
-
});
|
336
|
-
|
337
|
-
Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
|
338
|
-
var boundary = Math.min(newS.length, oldS.length);
|
339
|
-
for (var index = 0; index < boundary; ++index)
|
340
|
-
if (newS[index] != oldS[index])
|
341
|
-
return index;
|
342
|
-
return boundary;
|
343
|
-
};
|
344
|
-
|
345
|
-
Ajax.Autocompleter = Class.create(Autocompleter.Base, {
|
346
|
-
initialize: function(element, update, url, options) {
|
347
|
-
this.baseInitialize(element, update, options);
|
348
|
-
this.options.asynchronous = true;
|
349
|
-
this.options.onComplete = this.onComplete.bind(this);
|
350
|
-
this.options.defaultParams = this.options.parameters || null;
|
351
|
-
this.url = url;
|
352
|
-
},
|
353
|
-
|
354
|
-
getUpdatedChoices: function() {
|
355
|
-
this.startIndicator();
|
356
|
-
|
357
|
-
var entry = encodeURIComponent(this.options.paramName) + '=' +
|
358
|
-
encodeURIComponent(this.getToken());
|
359
|
-
|
360
|
-
this.options.parameters = this.options.callback ?
|
361
|
-
this.options.callback(this.element, entry) : entry;
|
362
|
-
|
363
|
-
if(this.options.defaultParams)
|
364
|
-
this.options.parameters += '&' + this.options.defaultParams;
|
365
|
-
|
366
|
-
new Ajax.Request(this.url, this.options);
|
367
|
-
},
|
368
|
-
|
369
|
-
onComplete: function(request) {
|
370
|
-
this.updateChoices(request.responseText);
|
371
|
-
}
|
372
|
-
});
|
373
|
-
|
374
|
-
// The local array autocompleter. Used when you'd prefer to
|
375
|
-
// inject an array of autocompletion options into the page, rather
|
376
|
-
// than sending out Ajax queries, which can be quite slow sometimes.
|
377
|
-
//
|
378
|
-
// The constructor takes four parameters. The first two are, as usual,
|
379
|
-
// the id of the monitored textbox, and id of the autocompletion menu.
|
380
|
-
// The third is the array you want to autocomplete from, and the fourth
|
381
|
-
// is the options block.
|
382
|
-
//
|
383
|
-
// Extra local autocompletion options:
|
384
|
-
// - choices - How many autocompletion choices to offer
|
385
|
-
//
|
386
|
-
// - partialSearch - If false, the autocompleter will match entered
|
387
|
-
// text only at the beginning of strings in the
|
388
|
-
// autocomplete array. Defaults to true, which will
|
389
|
-
// match text at the beginning of any *word* in the
|
390
|
-
// strings in the autocomplete array. If you want to
|
391
|
-
// search anywhere in the string, additionally set
|
392
|
-
// the option fullSearch to true (default: off).
|
393
|
-
//
|
394
|
-
// - fullSsearch - Search anywhere in autocomplete array strings.
|
395
|
-
//
|
396
|
-
// - partialChars - How many characters to enter before triggering
|
397
|
-
// a partial match (unlike minChars, which defines
|
398
|
-
// how many characters are required to do any match
|
399
|
-
// at all). Defaults to 2.
|
400
|
-
//
|
401
|
-
// - ignoreCase - Whether to ignore case when autocompleting.
|
402
|
-
// Defaults to true.
|
403
|
-
//
|
404
|
-
// It's possible to pass in a custom function as the 'selector'
|
405
|
-
// option, if you prefer to write your own autocompletion logic.
|
406
|
-
// In that case, the other options above will not apply unless
|
407
|
-
// you support them.
|
408
|
-
|
409
|
-
Autocompleter.Local = Class.create(Autocompleter.Base, {
|
410
|
-
initialize: function(element, update, array, options) {
|
411
|
-
this.baseInitialize(element, update, options);
|
412
|
-
this.options.array = array;
|
413
|
-
},
|
414
|
-
|
415
|
-
getUpdatedChoices: function() {
|
416
|
-
this.updateChoices(this.options.selector(this));
|
417
|
-
},
|
418
|
-
|
419
|
-
setOptions: function(options) {
|
420
|
-
this.options = Object.extend({
|
421
|
-
choices: 10,
|
422
|
-
partialSearch: true,
|
423
|
-
partialChars: 2,
|
424
|
-
ignoreCase: true,
|
425
|
-
fullSearch: false,
|
426
|
-
selector: function(instance) {
|
427
|
-
var ret = []; // Beginning matches
|
428
|
-
var partial = []; // Inside matches
|
429
|
-
var entry = instance.getToken();
|
430
|
-
var count = 0;
|
431
|
-
|
432
|
-
for (var i = 0; i < instance.options.array.length &&
|
433
|
-
ret.length < instance.options.choices ; i++) {
|
434
|
-
|
435
|
-
var elem = instance.options.array[i];
|
436
|
-
var foundPos = instance.options.ignoreCase ?
|
437
|
-
elem.toLowerCase().indexOf(entry.toLowerCase()) :
|
438
|
-
elem.indexOf(entry);
|
439
|
-
|
440
|
-
while (foundPos != -1) {
|
441
|
-
if (foundPos == 0 && elem.length != entry.length) {
|
442
|
-
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
|
443
|
-
elem.substr(entry.length) + "</li>");
|
444
|
-
break;
|
445
|
-
} else if (entry.length >= instance.options.partialChars &&
|
446
|
-
instance.options.partialSearch && foundPos != -1) {
|
447
|
-
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
|
448
|
-
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
|
449
|
-
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
|
450
|
-
foundPos + entry.length) + "</li>");
|
451
|
-
break;
|
452
|
-
}
|
453
|
-
}
|
454
|
-
|
455
|
-
foundPos = instance.options.ignoreCase ?
|
456
|
-
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
|
457
|
-
elem.indexOf(entry, foundPos + 1);
|
458
|
-
|
459
|
-
}
|
460
|
-
}
|
461
|
-
if (partial.length)
|
462
|
-
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
|
463
|
-
return "<ul>" + ret.join('') + "</ul>";
|
464
|
-
}
|
465
|
-
}, options || { });
|
466
|
-
}
|
467
|
-
});
|
468
|
-
|
469
|
-
// AJAX in-place editor and collection editor
|
470
|
-
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
|
471
|
-
|
472
|
-
// Use this if you notice weird scrolling problems on some browsers,
|
473
|
-
// the DOM might be a bit confused when this gets called so do this
|
474
|
-
// waits 1 ms (with setTimeout) until it does the activation
|
475
|
-
Field.scrollFreeActivate = function(field) {
|
476
|
-
setTimeout(function() {
|
477
|
-
Field.activate(field);
|
478
|
-
}, 1);
|
479
|
-
};
|
480
|
-
|
481
|
-
Ajax.InPlaceEditor = Class.create({
|
482
|
-
initialize: function(element, url, options) {
|
483
|
-
this.url = url;
|
484
|
-
this.element = element = $(element);
|
485
|
-
this.prepareOptions();
|
486
|
-
this._controls = { };
|
487
|
-
arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
|
488
|
-
Object.extend(this.options, options || { });
|
489
|
-
if (!this.options.formId && this.element.id) {
|
490
|
-
this.options.formId = this.element.id + '-inplaceeditor';
|
491
|
-
if ($(this.options.formId))
|
492
|
-
this.options.formId = '';
|
493
|
-
}
|
494
|
-
if (this.options.externalControl)
|
495
|
-
this.options.externalControl = $(this.options.externalControl);
|
496
|
-
if (!this.options.externalControl)
|
497
|
-
this.options.externalControlOnly = false;
|
498
|
-
this._originalBackground = this.element.getStyle('background-color') || 'transparent';
|
499
|
-
this.element.title = this.options.clickToEditText;
|
500
|
-
this._boundCancelHandler = this.handleFormCancellation.bind(this);
|
501
|
-
this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
|
502
|
-
this._boundFailureHandler = this.handleAJAXFailure.bind(this);
|
503
|
-
this._boundSubmitHandler = this.handleFormSubmission.bind(this);
|
504
|
-
this._boundWrapperHandler = this.wrapUp.bind(this);
|
505
|
-
this.registerListeners();
|
506
|
-
},
|
507
|
-
checkForEscapeOrReturn: function(e) {
|
508
|
-
if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
|
509
|
-
if (Event.KEY_ESC == e.keyCode)
|
510
|
-
this.handleFormCancellation(e);
|
511
|
-
else if (Event.KEY_RETURN == e.keyCode)
|
512
|
-
this.handleFormSubmission(e);
|
513
|
-
},
|
514
|
-
createControl: function(mode, handler, extraClasses) {
|
515
|
-
var control = this.options[mode + 'Control'];
|
516
|
-
var text = this.options[mode + 'Text'];
|
517
|
-
if ('button' == control) {
|
518
|
-
var btn = document.createElement('input');
|
519
|
-
btn.type = 'submit';
|
520
|
-
btn.value = text;
|
521
|
-
btn.className = 'editor_' + mode + '_button';
|
522
|
-
if ('cancel' == mode)
|
523
|
-
btn.onclick = this._boundCancelHandler;
|
524
|
-
this._form.appendChild(btn);
|
525
|
-
this._controls[mode] = btn;
|
526
|
-
} else if ('link' == control) {
|
527
|
-
var link = document.createElement('a');
|
528
|
-
link.href = '#';
|
529
|
-
link.appendChild(document.createTextNode(text));
|
530
|
-
link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
|
531
|
-
link.className = 'editor_' + mode + '_link';
|
532
|
-
if (extraClasses)
|
533
|
-
link.className += ' ' + extraClasses;
|
534
|
-
this._form.appendChild(link);
|
535
|
-
this._controls[mode] = link;
|
536
|
-
}
|
537
|
-
},
|
538
|
-
createEditField: function() {
|
539
|
-
var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
|
540
|
-
var fld;
|
541
|
-
if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
|
542
|
-
fld = document.createElement('input');
|
543
|
-
fld.type = 'text';
|
544
|
-
var size = this.options.size || this.options.cols || 0;
|
545
|
-
if (0 < size) fld.size = size;
|
546
|
-
} else {
|
547
|
-
fld = document.createElement('textarea');
|
548
|
-
fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
|
549
|
-
fld.cols = this.options.cols || 40;
|
550
|
-
}
|
551
|
-
fld.name = this.options.paramName;
|
552
|
-
fld.value = text; // No HTML breaks conversion anymore
|
553
|
-
fld.className = 'editor_field';
|
554
|
-
if (this.options.submitOnBlur)
|
555
|
-
fld.onblur = this._boundSubmitHandler;
|
556
|
-
this._controls.editor = fld;
|
557
|
-
if (this.options.loadTextURL)
|
558
|
-
this.loadExternalText();
|
559
|
-
this._form.appendChild(this._controls.editor);
|
560
|
-
},
|
561
|
-
createForm: function() {
|
562
|
-
var ipe = this;
|
563
|
-
function addText(mode, condition) {
|
564
|
-
var text = ipe.options['text' + mode + 'Controls'];
|
565
|
-
if (!text || condition === false) return;
|
566
|
-
ipe._form.appendChild(document.createTextNode(text));
|
567
|
-
};
|
568
|
-
this._form = $(document.createElement('form'));
|
569
|
-
this._form.id = this.options.formId;
|
570
|
-
this._form.addClassName(this.options.formClassName);
|
571
|
-
this._form.onsubmit = this._boundSubmitHandler;
|
572
|
-
this.createEditField();
|
573
|
-
if ('textarea' == this._controls.editor.tagName.toLowerCase())
|
574
|
-
this._form.appendChild(document.createElement('br'));
|
575
|
-
if (this.options.onFormCustomization)
|
576
|
-
this.options.onFormCustomization(this, this._form);
|
577
|
-
addText('Before', this.options.okControl || this.options.cancelControl);
|
578
|
-
this.createControl('ok', this._boundSubmitHandler);
|
579
|
-
addText('Between', this.options.okControl && this.options.cancelControl);
|
580
|
-
this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
|
581
|
-
addText('After', this.options.okControl || this.options.cancelControl);
|
582
|
-
},
|
583
|
-
destroy: function() {
|
584
|
-
if (this._oldInnerHTML)
|
585
|
-
this.element.innerHTML = this._oldInnerHTML;
|
586
|
-
this.leaveEditMode();
|
587
|
-
this.unregisterListeners();
|
588
|
-
},
|
589
|
-
enterEditMode: function(e) {
|
590
|
-
if (this._saving || this._editing) return;
|
591
|
-
this._editing = true;
|
592
|
-
this.triggerCallback('onEnterEditMode');
|
593
|
-
if (this.options.externalControl)
|
594
|
-
this.options.externalControl.hide();
|
595
|
-
this.element.hide();
|
596
|
-
this.createForm();
|
597
|
-
this.element.parentNode.insertBefore(this._form, this.element);
|
598
|
-
if (!this.options.loadTextURL)
|
599
|
-
this.postProcessEditField();
|
600
|
-
if (e) Event.stop(e);
|
601
|
-
},
|
602
|
-
enterHover: function(e) {
|
603
|
-
if (this.options.hoverClassName)
|
604
|
-
this.element.addClassName(this.options.hoverClassName);
|
605
|
-
if (this._saving) return;
|
606
|
-
this.triggerCallback('onEnterHover');
|
607
|
-
},
|
608
|
-
getText: function() {
|
609
|
-
return this.element.innerHTML.unescapeHTML();
|
610
|
-
},
|
611
|
-
handleAJAXFailure: function(transport) {
|
612
|
-
this.triggerCallback('onFailure', transport);
|
613
|
-
if (this._oldInnerHTML) {
|
614
|
-
this.element.innerHTML = this._oldInnerHTML;
|
615
|
-
this._oldInnerHTML = null;
|
616
|
-
}
|
617
|
-
},
|
618
|
-
handleFormCancellation: function(e) {
|
619
|
-
this.wrapUp();
|
620
|
-
if (e) Event.stop(e);
|
621
|
-
},
|
622
|
-
handleFormSubmission: function(e) {
|
623
|
-
var form = this._form;
|
624
|
-
var value = $F(this._controls.editor);
|
625
|
-
this.prepareSubmission();
|
626
|
-
var params = this.options.callback(form, value) || '';
|
627
|
-
if (Object.isString(params))
|
628
|
-
params = params.toQueryParams();
|
629
|
-
params.editorId = this.element.id;
|
630
|
-
if (this.options.htmlResponse) {
|
631
|
-
var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
|
632
|
-
Object.extend(options, {
|
633
|
-
parameters: params,
|
634
|
-
onComplete: this._boundWrapperHandler,
|
635
|
-
onFailure: this._boundFailureHandler
|
636
|
-
});
|
637
|
-
new Ajax.Updater({ success: this.element }, this.url, options);
|
638
|
-
} else {
|
639
|
-
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
640
|
-
Object.extend(options, {
|
641
|
-
parameters: params,
|
642
|
-
onComplete: this._boundWrapperHandler,
|
643
|
-
onFailure: this._boundFailureHandler
|
644
|
-
});
|
645
|
-
new Ajax.Request(this.url, options);
|
646
|
-
}
|
647
|
-
if (e) Event.stop(e);
|
648
|
-
},
|
649
|
-
leaveEditMode: function() {
|
650
|
-
this.element.removeClassName(this.options.savingClassName);
|
651
|
-
this.removeForm();
|
652
|
-
this.leaveHover();
|
653
|
-
this.element.style.backgroundColor = this._originalBackground;
|
654
|
-
this.element.show();
|
655
|
-
if (this.options.externalControl)
|
656
|
-
this.options.externalControl.show();
|
657
|
-
this._saving = false;
|
658
|
-
this._editing = false;
|
659
|
-
this._oldInnerHTML = null;
|
660
|
-
this.triggerCallback('onLeaveEditMode');
|
661
|
-
},
|
662
|
-
leaveHover: function(e) {
|
663
|
-
if (this.options.hoverClassName)
|
664
|
-
this.element.removeClassName(this.options.hoverClassName);
|
665
|
-
if (this._saving) return;
|
666
|
-
this.triggerCallback('onLeaveHover');
|
667
|
-
},
|
668
|
-
loadExternalText: function() {
|
669
|
-
this._form.addClassName(this.options.loadingClassName);
|
670
|
-
this._controls.editor.disabled = true;
|
671
|
-
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
672
|
-
Object.extend(options, {
|
673
|
-
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
674
|
-
onComplete: Prototype.emptyFunction,
|
675
|
-
onSuccess: function(transport) {
|
676
|
-
this._form.removeClassName(this.options.loadingClassName);
|
677
|
-
var text = transport.responseText;
|
678
|
-
if (this.options.stripLoadedTextTags)
|
679
|
-
text = text.stripTags();
|
680
|
-
this._controls.editor.value = text;
|
681
|
-
this._controls.editor.disabled = false;
|
682
|
-
this.postProcessEditField();
|
683
|
-
}.bind(this),
|
684
|
-
onFailure: this._boundFailureHandler
|
685
|
-
});
|
686
|
-
new Ajax.Request(this.options.loadTextURL, options);
|
687
|
-
},
|
688
|
-
postProcessEditField: function() {
|
689
|
-
var fpc = this.options.fieldPostCreation;
|
690
|
-
if (fpc)
|
691
|
-
$(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
|
692
|
-
},
|
693
|
-
prepareOptions: function() {
|
694
|
-
this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
|
695
|
-
Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
|
696
|
-
[this._extraDefaultOptions].flatten().compact().each(function(defs) {
|
697
|
-
Object.extend(this.options, defs);
|
698
|
-
}.bind(this));
|
699
|
-
},
|
700
|
-
prepareSubmission: function() {
|
701
|
-
this._saving = true;
|
702
|
-
this.removeForm();
|
703
|
-
this.leaveHover();
|
704
|
-
this.showSaving();
|
705
|
-
},
|
706
|
-
registerListeners: function() {
|
707
|
-
this._listeners = { };
|
708
|
-
var listener;
|
709
|
-
$H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
|
710
|
-
listener = this[pair.value].bind(this);
|
711
|
-
this._listeners[pair.key] = listener;
|
712
|
-
if (!this.options.externalControlOnly)
|
713
|
-
this.element.observe(pair.key, listener);
|
714
|
-
if (this.options.externalControl)
|
715
|
-
this.options.externalControl.observe(pair.key, listener);
|
716
|
-
}.bind(this));
|
717
|
-
},
|
718
|
-
removeForm: function() {
|
719
|
-
if (!this._form) return;
|
720
|
-
this._form.remove();
|
721
|
-
this._form = null;
|
722
|
-
this._controls = { };
|
723
|
-
},
|
724
|
-
showSaving: function() {
|
725
|
-
this._oldInnerHTML = this.element.innerHTML;
|
726
|
-
this.element.innerHTML = this.options.savingText;
|
727
|
-
this.element.addClassName(this.options.savingClassName);
|
728
|
-
this.element.style.backgroundColor = this._originalBackground;
|
729
|
-
this.element.show();
|
730
|
-
},
|
731
|
-
triggerCallback: function(cbName, arg) {
|
732
|
-
if ('function' == typeof this.options[cbName]) {
|
733
|
-
this.options[cbName](this, arg);
|
734
|
-
}
|
735
|
-
},
|
736
|
-
unregisterListeners: function() {
|
737
|
-
$H(this._listeners).each(function(pair) {
|
738
|
-
if (!this.options.externalControlOnly)
|
739
|
-
this.element.stopObserving(pair.key, pair.value);
|
740
|
-
if (this.options.externalControl)
|
741
|
-
this.options.externalControl.stopObserving(pair.key, pair.value);
|
742
|
-
}.bind(this));
|
743
|
-
},
|
744
|
-
wrapUp: function(transport) {
|
745
|
-
this.leaveEditMode();
|
746
|
-
// Can't use triggerCallback due to backward compatibility: requires
|
747
|
-
// binding + direct element
|
748
|
-
this._boundComplete(transport, this.element);
|
749
|
-
}
|
750
|
-
});
|
751
|
-
|
752
|
-
Object.extend(Ajax.InPlaceEditor.prototype, {
|
753
|
-
dispose: Ajax.InPlaceEditor.prototype.destroy
|
754
|
-
});
|
755
|
-
|
756
|
-
Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
|
757
|
-
initialize: function($super, element, url, options) {
|
758
|
-
this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
|
759
|
-
$super(element, url, options);
|
760
|
-
},
|
761
|
-
|
762
|
-
createEditField: function() {
|
763
|
-
var list = document.createElement('select');
|
764
|
-
list.name = this.options.paramName;
|
765
|
-
list.size = 1;
|
766
|
-
this._controls.editor = list;
|
767
|
-
this._collection = this.options.collection || [];
|
768
|
-
if (this.options.loadCollectionURL)
|
769
|
-
this.loadCollection();
|
770
|
-
else
|
771
|
-
this.checkForExternalText();
|
772
|
-
this._form.appendChild(this._controls.editor);
|
773
|
-
},
|
774
|
-
|
775
|
-
loadCollection: function() {
|
776
|
-
this._form.addClassName(this.options.loadingClassName);
|
777
|
-
this.showLoadingText(this.options.loadingCollectionText);
|
778
|
-
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
779
|
-
Object.extend(options, {
|
780
|
-
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
781
|
-
onComplete: Prototype.emptyFunction,
|
782
|
-
onSuccess: function(transport) {
|
783
|
-
var js = transport.responseText.strip();
|
784
|
-
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
|
785
|
-
throw('Server returned an invalid collection representation.');
|
786
|
-
this._collection = eval(js);
|
787
|
-
this.checkForExternalText();
|
788
|
-
}.bind(this),
|
789
|
-
onFailure: this.onFailure
|
790
|
-
});
|
791
|
-
new Ajax.Request(this.options.loadCollectionURL, options);
|
792
|
-
},
|
793
|
-
|
794
|
-
showLoadingText: function(text) {
|
795
|
-
this._controls.editor.disabled = true;
|
796
|
-
var tempOption = this._controls.editor.firstChild;
|
797
|
-
if (!tempOption) {
|
798
|
-
tempOption = document.createElement('option');
|
799
|
-
tempOption.value = '';
|
800
|
-
this._controls.editor.appendChild(tempOption);
|
801
|
-
tempOption.selected = true;
|
802
|
-
}
|
803
|
-
tempOption.update((text || '').stripScripts().stripTags());
|
804
|
-
},
|
805
|
-
|
806
|
-
checkForExternalText: function() {
|
807
|
-
this._text = this.getText();
|
808
|
-
if (this.options.loadTextURL)
|
809
|
-
this.loadExternalText();
|
810
|
-
else
|
811
|
-
this.buildOptionList();
|
812
|
-
},
|
813
|
-
|
814
|
-
loadExternalText: function() {
|
815
|
-
this.showLoadingText(this.options.loadingText);
|
816
|
-
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
817
|
-
Object.extend(options, {
|
818
|
-
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
819
|
-
onComplete: Prototype.emptyFunction,
|
820
|
-
onSuccess: function(transport) {
|
821
|
-
this._text = transport.responseText.strip();
|
822
|
-
this.buildOptionList();
|
823
|
-
}.bind(this),
|
824
|
-
onFailure: this.onFailure
|
825
|
-
});
|
826
|
-
new Ajax.Request(this.options.loadTextURL, options);
|
827
|
-
},
|
828
|
-
|
829
|
-
buildOptionList: function() {
|
830
|
-
this._form.removeClassName(this.options.loadingClassName);
|
831
|
-
this._collection = this._collection.map(function(entry) {
|
832
|
-
return 2 === entry.length ? entry : [entry, entry].flatten();
|
833
|
-
});
|
834
|
-
var marker = ('value' in this.options) ? this.options.value : this._text;
|
835
|
-
var textFound = this._collection.any(function(entry) {
|
836
|
-
return entry[0] == marker;
|
837
|
-
}.bind(this));
|
838
|
-
this._controls.editor.update('');
|
839
|
-
var option;
|
840
|
-
this._collection.each(function(entry, index) {
|
841
|
-
option = document.createElement('option');
|
842
|
-
option.value = entry[0];
|
843
|
-
option.selected = textFound ? entry[0] == marker : 0 == index;
|
844
|
-
option.appendChild(document.createTextNode(entry[1]));
|
845
|
-
this._controls.editor.appendChild(option);
|
846
|
-
}.bind(this));
|
847
|
-
this._controls.editor.disabled = false;
|
848
|
-
Field.scrollFreeActivate(this._controls.editor);
|
849
|
-
}
|
850
|
-
});
|
851
|
-
|
852
|
-
//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
|
853
|
-
//**** This only exists for a while, in order to let ****
|
854
|
-
//**** users adapt to the new API. Read up on the new ****
|
855
|
-
//**** API and convert your code to it ASAP! ****
|
856
|
-
|
857
|
-
Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
|
858
|
-
if (!options) return;
|
859
|
-
function fallback(name, expr) {
|
860
|
-
if (name in options || expr === undefined) return;
|
861
|
-
options[name] = expr;
|
862
|
-
};
|
863
|
-
fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
|
864
|
-
options.cancelLink == options.cancelButton == false ? false : undefined)));
|
865
|
-
fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
|
866
|
-
options.okLink == options.okButton == false ? false : undefined)));
|
867
|
-
fallback('highlightColor', options.highlightcolor);
|
868
|
-
fallback('highlightEndColor', options.highlightendcolor);
|
869
|
-
};
|
870
|
-
|
871
|
-
Object.extend(Ajax.InPlaceEditor, {
|
872
|
-
DefaultOptions: {
|
873
|
-
ajaxOptions: { },
|
874
|
-
autoRows: 3, // Use when multi-line w/ rows == 1
|
875
|
-
cancelControl: 'link', // 'link'|'button'|false
|
876
|
-
cancelText: 'cancel',
|
877
|
-
clickToEditText: 'Click to edit',
|
878
|
-
externalControl: null, // id|elt
|
879
|
-
externalControlOnly: false,
|
880
|
-
fieldPostCreation: 'activate', // 'activate'|'focus'|false
|
881
|
-
formClassName: 'inplaceeditor-form',
|
882
|
-
formId: null, // id|elt
|
883
|
-
highlightColor: '#ffff99',
|
884
|
-
highlightEndColor: '#ffffff',
|
885
|
-
hoverClassName: '',
|
886
|
-
htmlResponse: true,
|
887
|
-
loadingClassName: 'inplaceeditor-loading',
|
888
|
-
loadingText: 'Loading...',
|
889
|
-
okControl: 'button', // 'link'|'button'|false
|
890
|
-
okText: 'ok',
|
891
|
-
paramName: 'value',
|
892
|
-
rows: 1, // If 1 and multi-line, uses autoRows
|
893
|
-
savingClassName: 'inplaceeditor-saving',
|
894
|
-
savingText: 'Saving...',
|
895
|
-
size: 0,
|
896
|
-
stripLoadedTextTags: false,
|
897
|
-
submitOnBlur: false,
|
898
|
-
textAfterControls: '',
|
899
|
-
textBeforeControls: '',
|
900
|
-
textBetweenControls: ''
|
901
|
-
},
|
902
|
-
DefaultCallbacks: {
|
903
|
-
callback: function(form) {
|
904
|
-
return Form.serialize(form);
|
905
|
-
},
|
906
|
-
onComplete: function(transport, element) {
|
907
|
-
// For backward compatibility, this one is bound to the IPE, and passes
|
908
|
-
// the element directly. It was too often customized, so we don't break it.
|
909
|
-
new Effect.Highlight(element, {
|
910
|
-
startcolor: this.options.highlightColor, keepBackgroundImage: true });
|
911
|
-
},
|
912
|
-
onEnterEditMode: null,
|
913
|
-
onEnterHover: function(ipe) {
|
914
|
-
ipe.element.style.backgroundColor = ipe.options.highlightColor;
|
915
|
-
if (ipe._effect)
|
916
|
-
ipe._effect.cancel();
|
917
|
-
},
|
918
|
-
onFailure: function(transport, ipe) {
|
919
|
-
alert('Error communication with the server: ' + transport.responseText.stripTags());
|
920
|
-
},
|
921
|
-
onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
|
922
|
-
onLeaveEditMode: null,
|
923
|
-
onLeaveHover: function(ipe) {
|
924
|
-
ipe._effect = new Effect.Highlight(ipe.element, {
|
925
|
-
startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
|
926
|
-
restorecolor: ipe._originalBackground, keepBackgroundImage: true
|
927
|
-
});
|
928
|
-
}
|
929
|
-
},
|
930
|
-
Listeners: {
|
931
|
-
click: 'enterEditMode',
|
932
|
-
keydown: 'checkForEscapeOrReturn',
|
933
|
-
mouseover: 'enterHover',
|
934
|
-
mouseout: 'leaveHover'
|
935
|
-
}
|
936
|
-
});
|
937
|
-
|
938
|
-
Ajax.InPlaceCollectionEditor.DefaultOptions = {
|
939
|
-
loadingCollectionText: 'Loading options...'
|
940
|
-
};
|
941
|
-
|
942
|
-
// Delayed observer, like Form.Element.Observer,
|
943
|
-
// but waits for delay after last key input
|
944
|
-
// Ideal for live-search fields
|
945
|
-
|
946
|
-
Form.Element.DelayedObserver = Class.create({
|
947
|
-
initialize: function(element, delay, callback) {
|
948
|
-
this.delay = delay || 0.5;
|
949
|
-
this.element = $(element);
|
950
|
-
this.callback = callback;
|
951
|
-
this.timer = null;
|
952
|
-
this.lastValue = $F(this.element);
|
953
|
-
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
954
|
-
},
|
955
|
-
delayedListener: function(event) {
|
956
|
-
if(this.lastValue == $F(this.element)) return;
|
957
|
-
if(this.timer) clearTimeout(this.timer);
|
958
|
-
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
|
959
|
-
this.lastValue = $F(this.element);
|
960
|
-
},
|
961
|
-
onTimerEvent: function() {
|
962
|
-
this.timer = null;
|
963
|
-
this.callback(this.element, $F(this.element));
|
964
|
-
}
|
965
|
-
});
|