win_gui 0.1.6 → 0.2.0

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