pdoc 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. data/README.markdown +34 -0
  2. data/Rakefile +46 -0
  3. data/bin/pdoc +58 -0
  4. data/lib/pdoc.rb +32 -0
  5. data/lib/pdoc/error.rb +4 -0
  6. data/lib/pdoc/generators.rb +6 -0
  7. data/lib/pdoc/generators/abstract_generator.rb +16 -0
  8. data/lib/pdoc/generators/html.rb +8 -0
  9. data/lib/pdoc/generators/html/helpers.rb +256 -0
  10. data/lib/pdoc/generators/html/page.rb +71 -0
  11. data/lib/pdoc/generators/html/syntax_highlighter.rb +41 -0
  12. data/lib/pdoc/generators/html/template.rb +37 -0
  13. data/lib/pdoc/generators/html/website.rb +194 -0
  14. data/lib/pdoc/generators/json.rb +15 -0
  15. data/lib/pdoc/generators/pythonesque.rb +105 -0
  16. data/lib/pdoc/models.rb +47 -0
  17. data/lib/pdoc/models/argument.rb +37 -0
  18. data/lib/pdoc/models/base.rb +107 -0
  19. data/lib/pdoc/models/callable.rb +19 -0
  20. data/lib/pdoc/models/class.rb +28 -0
  21. data/lib/pdoc/models/class_method.rb +18 -0
  22. data/lib/pdoc/models/class_property.rb +9 -0
  23. data/lib/pdoc/models/constant.rb +9 -0
  24. data/lib/pdoc/models/constructor.rb +14 -0
  25. data/lib/pdoc/models/container.rb +114 -0
  26. data/lib/pdoc/models/entity.rb +54 -0
  27. data/lib/pdoc/models/instance_method.rb +18 -0
  28. data/lib/pdoc/models/instance_property.rb +9 -0
  29. data/lib/pdoc/models/mixin.rb +10 -0
  30. data/lib/pdoc/models/namespace.rb +10 -0
  31. data/lib/pdoc/models/root.rb +27 -0
  32. data/lib/pdoc/models/section.rb +19 -0
  33. data/lib/pdoc/models/signature.rb +27 -0
  34. data/lib/pdoc/models/utility.rb +11 -0
  35. data/lib/pdoc/parser.rb +109 -0
  36. data/lib/pdoc/parser/argument_description_nodes.rb +21 -0
  37. data/lib/pdoc/parser/basic_nodes.rb +31 -0
  38. data/lib/pdoc/parser/description_nodes.rb +42 -0
  39. data/lib/pdoc/parser/documentation_nodes.rb +483 -0
  40. data/lib/pdoc/parser/ebnf_arguments_nodes.rb +58 -0
  41. data/lib/pdoc/parser/ebnf_expression_nodes.rb +227 -0
  42. data/lib/pdoc/parser/fragment.rb +55 -0
  43. data/lib/pdoc/parser/section_content_nodes.rb +19 -0
  44. data/lib/pdoc/parser/tags_nodes.rb +14 -0
  45. data/lib/pdoc/parser/treetop_files/argument_description.treetop +31 -0
  46. data/lib/pdoc/parser/treetop_files/basic.treetop +41 -0
  47. data/lib/pdoc/parser/treetop_files/description.treetop +7 -0
  48. data/lib/pdoc/parser/treetop_files/documentation.treetop +75 -0
  49. data/lib/pdoc/parser/treetop_files/ebnf_arguments.treetop +33 -0
  50. data/lib/pdoc/parser/treetop_files/ebnf_expression.treetop +70 -0
  51. data/lib/pdoc/parser/treetop_files/ebnf_javascript.treetop +54 -0
  52. data/lib/pdoc/parser/treetop_files/events.treetop +17 -0
  53. data/lib/pdoc/parser/treetop_files/section_content.treetop +8 -0
  54. data/lib/pdoc/parser/treetop_files/tags.treetop +31 -0
  55. data/lib/pdoc/runner.rb +110 -0
  56. data/lib/pdoc/treemaker.rb +94 -0
  57. data/pdoc.gemspec +31 -0
  58. data/templates/html/assets/images/pdoc/alias.png +0 -0
  59. data/templates/html/assets/images/pdoc/class.png +0 -0
  60. data/templates/html/assets/images/pdoc/class_deprecated.png +0 -0
  61. data/templates/html/assets/images/pdoc/class_method.png +0 -0
  62. data/templates/html/assets/images/pdoc/class_property.png +0 -0
  63. data/templates/html/assets/images/pdoc/constant.png +0 -0
  64. data/templates/html/assets/images/pdoc/constructor.png +0 -0
  65. data/templates/html/assets/images/pdoc/deprecated.png +0 -0
  66. data/templates/html/assets/images/pdoc/description.png +0 -0
  67. data/templates/html/assets/images/pdoc/information.png +0 -0
  68. data/templates/html/assets/images/pdoc/instance_method.png +0 -0
  69. data/templates/html/assets/images/pdoc/instance_property.png +0 -0
  70. data/templates/html/assets/images/pdoc/method.png +0 -0
  71. data/templates/html/assets/images/pdoc/method_deprecated.png +0 -0
  72. data/templates/html/assets/images/pdoc/mixin.png +0 -0
  73. data/templates/html/assets/images/pdoc/namespace.png +0 -0
  74. data/templates/html/assets/images/pdoc/property.png +0 -0
  75. data/templates/html/assets/images/pdoc/related_to.png +0 -0
  76. data/templates/html/assets/images/pdoc/search-background.png +0 -0
  77. data/templates/html/assets/images/pdoc/section-background.png +0 -0
  78. data/templates/html/assets/images/pdoc/section.png +0 -0
  79. data/templates/html/assets/images/pdoc/selected-section-background.png +0 -0
  80. data/templates/html/assets/images/pdoc/subclass.png +0 -0
  81. data/templates/html/assets/images/pdoc/superclass.png +0 -0
  82. data/templates/html/assets/images/pdoc/utility.png +0 -0
  83. data/templates/html/assets/javascripts/pdoc/application.js +478 -0
  84. data/templates/html/assets/javascripts/pdoc/prototype.js +4874 -0
  85. data/templates/html/assets/javascripts/pdoc/tabs.js +506 -0
  86. data/templates/html/assets/stylesheets/pdoc/api.css +677 -0
  87. data/templates/html/assets/stylesheets/pdoc/pygments.css +62 -0
  88. data/templates/html/helpers.rb +35 -0
  89. data/templates/html/index.erb +18 -0
  90. data/templates/html/item_index.js.erb +6 -0
  91. data/templates/html/layout.erb +67 -0
  92. data/templates/html/leaf.erb +22 -0
  93. data/templates/html/node.erb +30 -0
  94. data/templates/html/partials/class_relationships.erb +19 -0
  95. data/templates/html/partials/classes.erb +7 -0
  96. data/templates/html/partials/constructor.erb +5 -0
  97. data/templates/html/partials/description.erb +5 -0
  98. data/templates/html/partials/link_list.erb +1 -0
  99. data/templates/html/partials/method_signatures.erb +14 -0
  100. data/templates/html/partials/methodized_note.erb +9 -0
  101. data/templates/html/partials/mixins.erb +7 -0
  102. data/templates/html/partials/namespaces.erb +7 -0
  103. data/templates/html/partials/related_utilities.erb +5 -0
  104. data/templates/html/partials/relationships.erb +11 -0
  105. data/templates/html/partials/short_description_list.erb +7 -0
  106. data/templates/html/partials/title.erb +22 -0
  107. data/templates/html/section.erb +18 -0
  108. data/test/unit/parser/argument_description_test.rb +40 -0
  109. data/test/unit/parser/basic_test.rb +55 -0
  110. data/test/unit/parser/description_test.rb +34 -0
  111. data/test/unit/parser/documentation_test.rb +520 -0
  112. data/test/unit/parser/ebnf_arguments_test.rb +81 -0
  113. data/test/unit/parser/ebnf_expression_test.rb +382 -0
  114. data/test/unit/parser/ebnf_javascript_test.rb +37 -0
  115. data/test/unit/parser/events_test.rb +27 -0
  116. data/test/unit/parser/section_content_test.rb +44 -0
  117. data/test/unit/parser/tags_test.rb +39 -0
  118. data/test/unit/parser/test_fragment.rb +80 -0
  119. data/test/unit/parser_test_helper.rb +62 -0
  120. data/test/unit/runner/basic_test.rb +14 -0
  121. data/test/unit/templates/html_helpers_test.rb +25 -0
  122. metadata +222 -0
data/pdoc.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "pdoc"
3
+ s.version = "0.2.0"
4
+ s.date = "2008-11-17"
5
+ s.summary = "Inline comment parser and JavaScript documentation generator"
6
+ s.email = "tobie.langel@gmail.com"
7
+ s.homepage = "http://pdoc.org/"
8
+ s.description = "PDoc is an inline comment parser and JavaScript documentation generator written in Ruby. It is designed for documenting Prototype and Prototype-based libraries."
9
+ s.has_rdoc = true
10
+ s.authors = ["Tobie Langel"]
11
+ s.files = [
12
+ "README.markdown",
13
+ "Rakefile",
14
+ "pdoc.gemspec"] +
15
+ Dir['lib/**/*'] +
16
+ Dir['templates/**/*']
17
+
18
+ s.autorequire = "lib/pdoc.rb"
19
+
20
+ s.bindir = "bin"
21
+ s.executables = ["pdoc"]
22
+ s.default_executable = "pdoc"
23
+
24
+ s.test_files = Dir['test/**/*.rb']
25
+ s.rdoc_options = ["--main", "README.markdown"]
26
+ s.extra_rdoc_files = ["README.markdown"]
27
+ s.add_dependency("BlueCloth", ["> 0.0.0"])
28
+ s.add_dependency("treetop", ["> 0.0.0"])
29
+ s.add_dependency("oyster", ["> 0.0.0"])
30
+ end
31
+
@@ -0,0 +1,478 @@
1
+ //= require <prototype>
2
+
3
+ if (!Prototype || Prototype.Version.indexOf('1.6') !== 0) {
4
+ throw "This script requires Prototype >= 1.6.";
5
+ }
6
+
7
+ Object.isDate = function(object) {
8
+ return object instanceof Date;
9
+ };
10
+
11
+ /**
12
+ * class Cookie
13
+ * Creates a cookie.
14
+ **/
15
+ var Cookie = Class.create({
16
+ /**
17
+ * new Cookie(name, value[, expires])
18
+ *
19
+ * - name (String): The name of the cookie.
20
+ * - value (String): The value of the cookie.
21
+ * - expires (Number | Date): Exact date (or number of days from now) that
22
+ * the cookie will expire.
23
+ **/
24
+ initialize: function(name, value, expires) {
25
+ expires = expires || "";
26
+ if (Object.isNumber(expires)) {
27
+ var days = expires;
28
+ expires = new Date();
29
+ expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
30
+ }
31
+
32
+ if (Object.isDate(expires))
33
+ expires = expires.toGMTString();
34
+
35
+ if (!Object.isUndefined(expires) && expires !== "")
36
+ expires = "; expires=" + expires;
37
+
38
+ this.name = name;
39
+ this.value = value;
40
+ this.expires = expires;
41
+
42
+ document.cookie = name + "=" + value + expires + "; path=/";
43
+ },
44
+
45
+ toString: function() {
46
+ return this.value;
47
+ },
48
+
49
+ inspect: function() {
50
+ return "#<Cookie #{name}:#{value}>".interpolate(this);
51
+ }
52
+ });
53
+
54
+ /**
55
+ * Cookie
56
+ **/
57
+ Object.extend(Cookie, {
58
+ /**
59
+ * Cookie.set(name, value, expires)
60
+ *
61
+ * Alias of [[Cookie#initialize]].
62
+ **/
63
+ set: function(name, value, expires) {
64
+ return new Cookie(name, value, expires);
65
+ },
66
+
67
+ /**
68
+ * Cookie.get(name)
69
+ *
70
+ * Returns the value of the cookie with the given name.
71
+ * - name (String): The name of the cookie to retrieve.
72
+ **/
73
+ get: function(name) {
74
+ var c = document.cookie.split(';');
75
+
76
+ for (var i = 0, cookie; i < c.length; i++) {
77
+ cookie = c[i].split('=');
78
+ if (cookie[0].strip() === name)
79
+ return cookie[1].strip();
80
+ }
81
+
82
+ return null;
83
+ },
84
+
85
+ /**
86
+ * Cookie.unset(name)
87
+ *
88
+ * Deletes a cookie.
89
+ * - name (String): The name of the cookie to delete.
90
+ *
91
+ **/
92
+ unset: function(name) {
93
+ return Cookie.set(name, "", -1);
94
+ }
95
+ });
96
+
97
+ Cookie.erase = Cookie.unset;
98
+
99
+
100
+
101
+ if (typeof PDoc === 'undefined') {
102
+ window.PDoc = {
103
+ Sidebar: {}
104
+ };
105
+ }
106
+
107
+ // HISTORY MANAGER (sort of)
108
+ // Polls for changes to the hash.
109
+
110
+ (function() {
111
+ var PREVIOUS_HASH = null;
112
+
113
+ function poll() {
114
+ var hash = window.location.hash;
115
+ if (hash && hash !== PREVIOUS_HASH) {
116
+ document.fire('hash:changed', {
117
+ previous: PREVIOUS_HASH, current: hash
118
+ });
119
+ }
120
+ PREVIOUS_HASH = hash;
121
+ window.setTimeout(arguments.callee, 100);
122
+ }
123
+
124
+ Event.observe(window, 'load', poll);
125
+ })();
126
+
127
+ Object.extend(PDoc.Sidebar, {
128
+ getActiveTab: function() {
129
+ var activeTab = $('sidebar_tabs').down('.active');
130
+ if (!activeTab) return null;
131
+
132
+ var href = activeTab.readAttribute('href');
133
+ return href.endsWith('menu_pane') ? 'menu_pane' : 'search_pane';
134
+ },
135
+
136
+ // Remember the state of the sidebar so it can be restored on the next page.
137
+ serialize: function() {
138
+ var state = $H({
139
+ activeTab: PDoc.Sidebar.getActiveTab(),
140
+ menuScrollOffset: $('menu_pane').scrollTop,
141
+ searchScrollOffset: $('search_results').scrollTop,
142
+ searchValue: $('search').getValue()
143
+ });
144
+
145
+ return escape(state.toJSON());
146
+ },
147
+
148
+ // Restore the tree to a certain point based on a cookie.
149
+ restore: function(state) {
150
+ try {
151
+ state = unescape(state).evalJSON();
152
+ var filterer = $('search').retrieve('filterer');
153
+ filterer.setSearchValue(state.searchValue);
154
+
155
+ (function() {
156
+ $('menu_pane').scrollTop = state.menuScrollOffset;
157
+ $('search_results').scrollTop = state.searchScrollOffset;
158
+ }).defer();
159
+ } catch(error) {
160
+ console.log(error);
161
+ if (!(error instanceof SyntaxError)) throw error;
162
+ }
163
+ }
164
+ });
165
+
166
+
167
+
168
+ // Live API search.
169
+ PDoc.Sidebar.Filterer = Class.create({
170
+ initialize: function(element, options) {
171
+ this.element = $(element);
172
+ this.options = Object.extend(
173
+ Object.clone(PDoc.Sidebar.Filterer.DEFAULT_OPTIONS),
174
+ options || {}
175
+ );
176
+
177
+ // The browser's "helpful" auto-complete gets in the way.
178
+ this.element.writeAttribute("autocomplete", "off");
179
+ this.element.setValue('');
180
+
181
+ // Hitting "enter" should do nothing.
182
+ this.element.up('form').observe("submit", Event.stop);
183
+
184
+ this.menu = this.options.menu;
185
+ this.links = this.menu.select('a');
186
+
187
+ this.resultsElement = this.options.resultsElement;
188
+
189
+ this.observers = {
190
+ filter: this.filter.bind(this),
191
+ keydown: this.keydown.bind(this),
192
+ keyup: this.keyup.bind(this)
193
+ };
194
+
195
+ this.menu.setStyle({ opacity: 0.9 });
196
+ this.addObservers();
197
+ },
198
+
199
+ addObservers: function() {
200
+ this.element.observe('keyup', this.observers.filter);
201
+ },
202
+
203
+ // Called whenever the list of results needs to update as a result of a
204
+ // changed search key.
205
+ filter: function(event) {
206
+ // Clear the text box on ESC.
207
+ if (event.keyCode && event.keyCode === Event.KEY_ESC) {
208
+ this.element.setValue('');
209
+ }
210
+
211
+ if (PDoc.Sidebar.Filterer.INTERCEPT_KEYS.include(event.keyCode))
212
+ return;
213
+
214
+ // If there's nothing in the text box, clear the results list.
215
+ var value = $F(this.element).strip().toLowerCase();
216
+ if (value === '') {
217
+ this.emptyResults();
218
+ this.hideResults();
219
+ return;
220
+ }
221
+
222
+ var urls = this.findURLs(value);
223
+ this.buildResults(urls);
224
+ },
225
+
226
+ setSearchValue: function(value) {
227
+ this.element.setValue(value);
228
+ if (value.strip() === "") {
229
+ PDoc.Sidebar.Tabs.setActiveTab(0);
230
+ return;
231
+ }
232
+ this.buildResults(this.findURLs(value));
233
+ },
234
+
235
+ // Given a key, finds all the PDoc objects that match.
236
+ findURLs: function(str) {
237
+ var results = [];
238
+ for (var name in PDoc.elements) {
239
+ if (name.toLowerCase().include(str.toLowerCase()))
240
+ results.push(PDoc.elements[name]);
241
+ }
242
+ return results;
243
+ },
244
+
245
+ buildResults: function(results) {
246
+ this.emptyResults();
247
+
248
+ results.each( function(result) {
249
+ var li = this._buildResult(result);
250
+ this.resultsElement.appendChild(li);
251
+ }, this);
252
+ this.showResults();
253
+ },
254
+
255
+ _buildResult: function(obj) {
256
+ var li = new Element('li', { 'class': 'menu-item' });
257
+ var a = new Element('a', {
258
+ 'class': obj.type.gsub(/\s/, '-'),
259
+ 'href': PDoc.pathPrefix + obj.path
260
+ }).update(obj.name);
261
+
262
+ li.appendChild(a);
263
+ return li;
264
+ },
265
+
266
+ emptyResults: function() {
267
+ this.resultsElement.update();
268
+ },
269
+
270
+ hideResults: function() {
271
+ PDoc.Sidebar.Tabs.setActiveTab(0);
272
+ //this.resultsElement.hide();
273
+ document.stopObserving('keydown', this.observers.keydown);
274
+ document.stopObserving('keyup', this.observers.keyup);
275
+ },
276
+
277
+ showResults: function() {
278
+ PDoc.Sidebar.Tabs.setActiveTab(1);
279
+ //this.resultsElement.show();
280
+ document.stopObserving('keydown', this.observers.keydown);
281
+ this.element.stopObserving('keyup', this.observers.keyup);
282
+ this.element.observe('keydown', this.observers.keydown);
283
+ document.observe('keyup', this.observers.keyup);
284
+ },
285
+
286
+ keydown: function(event) {
287
+ if (!PDoc.Sidebar.Filterer.INTERCEPT_KEYS.include(event.keyCode))
288
+ return;
289
+
290
+ // Also ignore if any modifier keys are present.
291
+ if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey)
292
+ return;
293
+
294
+ event.stop();
295
+
296
+ if (event.keyCode === Event.KEY_RETURN) {
297
+ // Follow the highlighted item, unless there is none.
298
+ if (!this.highlighted) return;
299
+ var a = this.highlighted.down('a');
300
+ if (a) {
301
+ window.location.href = a.href;
302
+ }
303
+ } else if ([Event.KEY_UP, Event.KEY_DOWN].include(event.keyCode)) {
304
+ // Is an arrow key.
305
+ var direction = (Event.KEY_DOWN === event.keyCode) ? 1 : -1;
306
+ this.highlighted = this.moveHighlight(direction);
307
+
308
+ if (!Prototype.Browser.WebKit) {
309
+ // If up/down key is held down, list should keep scrolling.
310
+ // WebKit does this automatically because it fires the keydown
311
+ // event over and over.
312
+ this._scrollTimer = window.setTimeout(
313
+ this.scrollList.bind(this, direction), 1000);
314
+ }
315
+ }
316
+ },
317
+
318
+ keyup: function(event) {
319
+ if (this._scrollTimer) {
320
+ window.clearTimeout(this._scrollTimer);
321
+ }
322
+ },
323
+
324
+ moveHighlight: function(direction) {
325
+ if (!this.highlighted) {
326
+ // If there is none, highlight the first result.
327
+ this.highlighted =
328
+ this.resultsElement.down('li').addClassName('highlighted');
329
+ } else {
330
+ var method = (direction === 1) ? 'next' : 'previous';
331
+ this.highlighted.removeClassName('highlighted');
332
+ var adjacent = this.highlighted[method]('li');
333
+ // If there isn't an adjacent one, we're at the top or bottom
334
+ // of the list. Flip it.
335
+ if (!adjacent) {
336
+ adjacent = method == 'next' ? this.resultsElement.down('li') :
337
+ this.resultsElement.down('li:last-of-type');
338
+ }
339
+ adjacent.addClassName('highlighted');
340
+ this.highlighted = adjacent;
341
+ }
342
+
343
+ var h = this.highlighted, r = this.resultsElement;
344
+
345
+ var distanceToBottom = h.offsetTop + h.offsetHeight;
346
+ if (distanceToBottom > (r.offsetHeight + r.scrollTop)) {
347
+ // Item is below the visible frame.
348
+ r.scrollTop = distanceToBottom - r.offsetHeight;
349
+ } else if (h.offsetTop < r.scrollTop) {
350
+ // Item is above the visible frame.
351
+ r.scrollTop = h.offsetTop;
352
+ }
353
+
354
+ return this.highlighted;
355
+ },
356
+
357
+ scrollList: function(direction) {
358
+ this.moveHighlight(direction);
359
+ this._scrollTimer = window.setTimeout(
360
+ this.scrollList.bind(this, direction), 100);
361
+ }
362
+ });
363
+
364
+ Object.extend(PDoc.Sidebar.Filterer, {
365
+ INTERCEPT_KEYS: [Event.KEY_UP, Event.KEY_DOWN, Event.KEY_RETURN],
366
+ DEFAULT_OPTIONS: {
367
+ interval: 0.1,
368
+ resultsElement: '.search-results'
369
+ }
370
+ });
371
+
372
+
373
+ Form.GhostedField = Class.create({
374
+ initialize: function(element, title, options) {
375
+ options = options || {};
376
+
377
+ this.element = $(element);
378
+ this.title = title;
379
+
380
+ this.isGhosted = true;
381
+
382
+ if (options.cloak) {
383
+
384
+ // Wrap the native getValue function so that it never returns the
385
+ // ghosted value. This is optional because it presumes the ghosted
386
+ // value isn't valid input for the field.
387
+ this.element.getValue = this.element.getValue.wrap(this.wrappedGetValue.bind(this));
388
+ }
389
+
390
+ this.addObservers();
391
+
392
+ this.onBlur();
393
+ },
394
+
395
+ wrappedGetValue: function($proceed) {
396
+ var value = $proceed();
397
+ return value === this.title ? "" : value;
398
+ },
399
+
400
+ addObservers: function() {
401
+ this.element.observe('focus', this.onFocus.bind(this));
402
+ this.element.observe('blur', this.onBlur.bind(this));
403
+
404
+ var form = this.element.up('form');
405
+ if (form) {
406
+ form.observe('submit', this.onSubmit.bind(this));
407
+ }
408
+
409
+ // Firefox's bfcache means that form fields need to be re-initialized
410
+ // when you hit the "back" button to return to the page.
411
+ if (Prototype.Browser.Gecko) {
412
+ window.addEventListener('pageshow', this.onBlur.bind(this), false);
413
+ }
414
+ },
415
+
416
+ onFocus: function() {
417
+ if (this.isGhosted) {
418
+ this.element.setValue('');
419
+ this.setGhosted(false);
420
+ }
421
+ },
422
+
423
+ onBlur: function() {
424
+ var value = this.element.getValue();
425
+ if (value.blank() || value == this.title) {
426
+ this.setGhosted(true);
427
+ } else {
428
+ this.setGhosted(false);
429
+ }
430
+ },
431
+
432
+ setGhosted: function(isGhosted) {
433
+ this.isGhosted = isGhosted;
434
+ this.element[isGhosted ? 'addClassName' : 'removeClassName']('ghosted');
435
+ if (isGhosted) {
436
+ this.element.setValue(this.title);
437
+ }
438
+ },
439
+
440
+ // Hook into the enclosing form's `onsubmit` event so that we clear any
441
+ // ghosted text before the form is sent.
442
+ onSubmit: function() {
443
+ if (this.isGhosted) {
444
+ this.element.setValue('');
445
+ }
446
+ }
447
+ });
448
+
449
+ document.observe('dom:loaded', function() {
450
+ PDoc.Sidebar.Tabs = new Control.Tabs($('sidebar_tabs'));
451
+
452
+ var searchField = $('search');
453
+
454
+ if (searchField) {
455
+ var filterer = new PDoc.Sidebar.Filterer(searchField, {
456
+ menu: $('api_menu'),
457
+ resultsElement: $('search_results')
458
+ });
459
+ searchField.store('filterer', filterer);
460
+ }
461
+
462
+ // Prevent horizontal scrolling in scrollable sidebar areas.
463
+ $$('.scrollable').invoke('observe', 'scroll', function() {
464
+ this.scrollLeft = 0;
465
+ });
466
+
467
+ var sidebarState = Cookie.get('sidebar_state');
468
+ if (sidebarState) {
469
+ PDoc.Sidebar.restore(sidebarState);
470
+ }
471
+
472
+ new Form.GhostedField(searchField, searchField.getAttribute('title'),
473
+ { cloak: true });
474
+ });
475
+
476
+ Event.observe(window, 'unload', function() {
477
+ Cookie.set('sidebar_state', PDoc.Sidebar.serialize());
478
+ });