jax 0.0.0.7 → 0.0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/CHANGELOG +21 -0
  2. data/Rakefile +13 -1
  3. data/builtin/shaders/functions/noise.ejs +780 -7
  4. data/guides/partials/_top_nav.html.erb +17 -0
  5. data/guides/source/index.html.erb +7 -0
  6. data/guides/source/layout.html.erb +1 -12
  7. data/lib/jax/generators/app/templates/public/javascripts/jax.js +4 -1
  8. data/lib/jax/version.rb +1 -1
  9. data/spec/example_app/app/controllers/lighting_controller.js +0 -2
  10. data/spec/javascripts/shaders/preprocessor_spec.js +19 -4
  11. data/src/constants.yml +1 -1
  12. data/src/jax/anim_frame.js +1 -1
  13. data/src/jax/core/util.js +5 -0
  14. data/src/jax/webgl/shader.js +2 -0
  15. data/vendor/pdoc_template/html/assets/images/pdoc/alias.png +0 -0
  16. data/vendor/pdoc_template/html/assets/images/pdoc/class.png +0 -0
  17. data/vendor/pdoc_template/html/assets/images/pdoc/class_deprecated.png +0 -0
  18. data/vendor/pdoc_template/html/assets/images/pdoc/class_method.png +0 -0
  19. data/vendor/pdoc_template/html/assets/images/pdoc/class_property.png +0 -0
  20. data/vendor/pdoc_template/html/assets/images/pdoc/constant.png +0 -0
  21. data/vendor/pdoc_template/html/assets/images/pdoc/constructor.png +0 -0
  22. data/vendor/pdoc_template/html/assets/images/pdoc/deprecated.png +0 -0
  23. data/vendor/pdoc_template/html/assets/images/pdoc/description.png +0 -0
  24. data/vendor/pdoc_template/html/assets/images/pdoc/information.png +0 -0
  25. data/vendor/pdoc_template/html/assets/images/pdoc/instance_method.png +0 -0
  26. data/vendor/pdoc_template/html/assets/images/pdoc/instance_property.png +0 -0
  27. data/vendor/pdoc_template/html/assets/images/pdoc/method.png +0 -0
  28. data/vendor/pdoc_template/html/assets/images/pdoc/method_deprecated.png +0 -0
  29. data/vendor/pdoc_template/html/assets/images/pdoc/mixin.png +0 -0
  30. data/vendor/pdoc_template/html/assets/images/pdoc/namespace.png +0 -0
  31. data/vendor/pdoc_template/html/assets/images/pdoc/property.png +0 -0
  32. data/vendor/pdoc_template/html/assets/images/pdoc/related_to.png +0 -0
  33. data/vendor/pdoc_template/html/assets/images/pdoc/search-background.png +0 -0
  34. data/vendor/pdoc_template/html/assets/images/pdoc/section-background.png +0 -0
  35. data/vendor/pdoc_template/html/assets/images/pdoc/section.png +0 -0
  36. data/vendor/pdoc_template/html/assets/images/pdoc/selected-section-background.png +0 -0
  37. data/vendor/pdoc_template/html/assets/images/pdoc/subclass.png +0 -0
  38. data/vendor/pdoc_template/html/assets/images/pdoc/superclass.png +0 -0
  39. data/vendor/pdoc_template/html/assets/images/pdoc/utility.png +0 -0
  40. data/vendor/pdoc_template/html/assets/javascripts/pdoc/application.js +478 -0
  41. data/vendor/pdoc_template/html/assets/javascripts/pdoc/prototype.js +4874 -0
  42. data/vendor/pdoc_template/html/assets/javascripts/pdoc/tabs.js +506 -0
  43. data/vendor/pdoc_template/html/assets/stylesheets/jax.css +30 -0
  44. data/vendor/pdoc_template/html/assets/stylesheets/pdoc/api.css +681 -0
  45. data/vendor/pdoc_template/html/assets/stylesheets/pdoc/pygments.css +62 -0
  46. data/vendor/pdoc_template/html/helpers.rb +35 -0
  47. data/vendor/pdoc_template/html/index.erb +18 -0
  48. data/vendor/pdoc_template/html/item_index.js.erb +6 -0
  49. data/vendor/pdoc_template/html/layout.erb +67 -0
  50. data/vendor/pdoc_template/html/leaf.erb +22 -0
  51. data/vendor/pdoc_template/html/node.erb +30 -0
  52. data/vendor/pdoc_template/html/partials/class_relationships.erb +19 -0
  53. data/vendor/pdoc_template/html/partials/classes.erb +7 -0
  54. data/vendor/pdoc_template/html/partials/constructor.erb +5 -0
  55. data/vendor/pdoc_template/html/partials/description.erb +5 -0
  56. data/vendor/pdoc_template/html/partials/link_list.erb +1 -0
  57. data/vendor/pdoc_template/html/partials/method_signatures.erb +14 -0
  58. data/vendor/pdoc_template/html/partials/methodized_note.erb +9 -0
  59. data/vendor/pdoc_template/html/partials/mixins.erb +7 -0
  60. data/vendor/pdoc_template/html/partials/namespaces.erb +7 -0
  61. data/vendor/pdoc_template/html/partials/related_utilities.erb +5 -0
  62. data/vendor/pdoc_template/html/partials/relationships.erb +11 -0
  63. data/vendor/pdoc_template/html/partials/short_description_list.erb +7 -0
  64. data/vendor/pdoc_template/html/partials/title.erb +24 -0
  65. data/vendor/pdoc_template/html/section.erb +18 -0
  66. metadata +56 -4
@@ -0,0 +1,17 @@
1
+ <div id="topNav">
2
+ <div class="wrapper">
3
+ <strong>More at <a href="http://jaxgl.com">jaxgl.com:</a> </strong>
4
+ <a href="http://blog.jaxgl.com/what-is-jax">Overview</a> |
5
+ <%if @link_to_guides%>
6
+ <a href="http://guides.jaxgl.com">Guides</a> |
7
+ <%end%>
8
+ <!-- <a href="http://jaxgl.com/download">Download</a> | -->
9
+ <!-- <a href="http://jaxgl.com/deploy">Deploy</a> | -->
10
+ <a href="http://github.com/sinisterchipmunk/jax">Code</a> |
11
+ <%if !@hide_links_to_api_docs%>
12
+ <a href="http://guides.jaxgl.com/api/js/index.html">Documentation</a> |
13
+ <%end%>
14
+ <a href="http://blog.jaxgl.com/forum">Forums</a> |
15
+ <a href="http://blog.jaxgl.com/">Blog</a>
16
+ </div>
17
+ </div>
@@ -52,3 +52,10 @@
52
52
  <% end %>
53
53
 
54
54
  </dl>
55
+
56
+ <h3>API Documentation</h3>
57
+
58
+ <dl>
59
+ <%=guide("Jax JavaScript API Docs", "api/js/index.html") do%>
60
+ <%end%>
61
+ </dl>
@@ -21,18 +21,7 @@
21
21
  <img src="images/edge_badge.png" alt="edge-badge" id="edge-badge" />
22
22
  </div>
23
23
  <% end %>
24
- <div id="topNav">
25
- <div class="wrapper">
26
- <strong>More at <a href="http://jaxgl.com">jaxgl.com:</a> </strong>
27
- <a href="http://blog.jaxgl.com/what-is-jax">Overview</a> |
28
- <!-- <a href="http://jaxgl.com/download">Download</a> | -->
29
- <!-- <a href="http://jaxgl.com/deploy">Deploy</a> | -->
30
- <a href="http://github.com/sinisterchipmunk/jax">Code</a> |
31
- <!-- <a href="http://jaxgl.com/documentation">Documentation</a> | -->
32
- <a href="http://blog.jaxgl.com/forum">Forums</a> |
33
- <a href="http://blog.jaxgl.com/">Blog</a>
34
- </div>
35
- </div>
24
+ <%= render :partial => '../partials/top_nav' %>
36
25
  <!--
37
26
  I want to use a similar index to below, once the Jax guides start to take shape:
38
27
 
@@ -1,5 +1,5 @@
1
1
 
2
- var Jax = { PRODUCTION: 1, VERSION: "0.0.0.7" };
2
+ var Jax = { PRODUCTION: 1, VERSION: "0.0.0.8" };
3
3
 
4
4
  /* Called by Jax applications as of version 0.0.0.5 to alert the user to incomplete upgrades */
5
5
  Jax.doVersionCheck = function(targetVersion) {
@@ -2243,6 +2243,7 @@ Math.equalish = Math.equalish || function(a, b) {
2243
2243
  if (Math.abs(a[i] - b[i]) > Math.EPSILON) return false;
2244
2244
  return true;
2245
2245
  };
2246
+
2246
2247
  Jax.Util = {
2247
2248
  decodePickingColor: function(red, green, blue, alpha) {
2248
2249
  /* blue is a key. It is always max. So if it's 1, we're dealing with floats; else, bytes. */
@@ -3289,6 +3290,7 @@ Jax.Shader = (function() {
3289
3290
 
3290
3291
  getRawSource: function(options, which) {
3291
3292
  var source = this.options[which];
3293
+ options = Jax.Util.normalizeOptions(options, {shader_type:which});
3292
3294
  if (source && (source = source.render(options))) {
3293
3295
  var result = this.getPreamble(options);
3294
3296
  if (!options || !options.skip_export_definitions)
@@ -3379,6 +3381,7 @@ Jax.Shader.max_varyings = gl.getParameter(GL_MAX_VARYING_VECTORS);
3379
3381
  Jax.Shader.max_vertex_uniforms = gl.getParameter(GL_MAX_VERTEX_UNIFORM_VECTORS);
3380
3382
  Jax.Shader.max_fragment_uniforms = gl.getParameter(GL_MAX_FRAGMENT_UNIFORM_VECTORS);
3381
3383
  Jax.Shader.max_attributes = gl.getParameter(GL_MAX_VERTEX_ATTRIBS);
3384
+ Jax.Shader.max_vertex_textures = gl.getParameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
3382
3385
 
3383
3386
  Jax.Shader.max_uniforms = Math.min(Jax.Shader.max_fragment_uniforms, Jax.Shader.max_vertex_uniforms);
3384
3387
 
data/lib/jax/version.rb CHANGED
@@ -3,7 +3,7 @@ module Jax
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
5
  TINY = 0
6
- PATCH = 7
6
+ PATCH = 8
7
7
 
8
8
  STRING = PATCH == 0 ? "#{MAJOR}.#{MINOR}.#{TINY}" : "#{MAJOR}.#{MINOR}.#{TINY}.#{PATCH}"
9
9
  end
@@ -12,8 +12,6 @@ var LightingController = (function() {
12
12
  // build a new material, since the default one isn't quite to our liking for this demo
13
13
  // all unspecified options will simply inherit the default ones.
14
14
  var custom_material = Jax.Material.find("lighting_with_shadows");
15
- // var custom_material = new Jax.Material({shininess:128,ambient:[0.05,0.05,0.05,1]});
16
- // custom_material.addLayer(new Jax.Material.ShadowMap());
17
15
 
18
16
  // add a Teapot
19
17
  this.world.addObject(new Jax.Model({ mesh: new Jax.Mesh.Teapot({size:10, material:custom_material}),position:[0,0,-25] }));
@@ -2,6 +2,25 @@ describe("Preprocessor", function() {
2
2
  var context;
3
3
  var matr;
4
4
 
5
+ beforeEach(function() {
6
+ context = new Jax.Context(document.getElementById('canvas-element'));
7
+ matr = new Jax.Material();
8
+ });
9
+
10
+ afterEach(function() { context.dispose(); });
11
+
12
+ it("should be able to determine shader type from within common code", function() {
13
+ Jax.shaders['test'] = new Jax.Shader({
14
+ common: "void <%=shader_type%>(void) { return; }",
15
+ vertex: "void main(void) { gl_Position = vec4(0,0,0,1); }",
16
+ fragment: "void main(void) { discard; }",
17
+ name: "test"
18
+ });
19
+
20
+ expect(Jax.shaders['test'].getVertexSource(matr)).toMatch(/void vertex\(void\)/);
21
+ expect(Jax.shaders['test'].getFragmentSource(matr)).toMatch(/void fragment\(void\)/);
22
+ });
23
+
5
24
  describe("with multiple similar vardecs", function() {
6
25
  Jax.shaders['test'] = new Jax.Shader({
7
26
  vertex: "shared uniform mat4 ivMatrix, mvMatrix, pMatrix, vMatrix;\n" +
@@ -21,13 +40,9 @@ describe("Preprocessor", function() {
21
40
  });
22
41
 
23
42
  beforeEach(function() {
24
- context = new Jax.Context(document.getElementById('canvas-element'));
25
- matr = new Jax.Material();
26
43
  matr.addLayer(new TestMaterial());
27
44
  });
28
45
 
29
- afterEach(function() { context.dispose(); });
30
-
31
46
  it("should should not fail", function() {
32
47
  new Jax.Mesh({material:matr}).render(context);
33
48
  });
data/src/constants.yml CHANGED
@@ -1 +1 @@
1
- JAX_VERSION: 0.0.0.7
1
+ JAX_VERSION: 0.0.0.8
@@ -31,7 +31,7 @@
31
31
 
32
32
 
33
33
  /**
34
- * Global.requestAnimFrame(callback, element) -> undefined
34
+ * Global#requestAnimFrame(callback, element) -> undefined
35
35
  * - callback (Function): callback to be fired to initiate a render sequence
36
36
  * - element (DOMElement): a DOM element to attach the animation state to
37
37
  *
data/src/jax/core/util.js CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * class Array
3
+ * A standard JavaScript array.
4
+ **/
5
+
1
6
  /**
2
7
  * Jax.Util
3
8
  * Contains general-purpose utility and helper functions
@@ -196,6 +196,7 @@ Jax.Shader = (function() {
196
196
 
197
197
  getRawSource: function(options, which) {
198
198
  var source = this.options[which];
199
+ options = Jax.Util.normalizeOptions(options, {shader_type:which});
199
200
  if (source && (source = source.render(options))) {
200
201
  var result = this.getPreamble(options);
201
202
  if (!options || !options.skip_export_definitions)
@@ -287,6 +288,7 @@ Jax.Shader.max_varyings = gl.getParameter(GL_MAX_VARYING_VECTORS);
287
288
  Jax.Shader.max_vertex_uniforms = gl.getParameter(GL_MAX_VERTEX_UNIFORM_VECTORS);
288
289
  Jax.Shader.max_fragment_uniforms = gl.getParameter(GL_MAX_FRAGMENT_UNIFORM_VECTORS);
289
290
  Jax.Shader.max_attributes = gl.getParameter(GL_MAX_VERTEX_ATTRIBS);
291
+ Jax.Shader.max_vertex_textures = gl.getParameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
290
292
 
291
293
  // FIXME differentiate between vertex & fragment uniforms
292
294
  Jax.Shader.max_uniforms = Math.min(Jax.Shader.max_fragment_uniforms, Jax.Shader.max_vertex_uniforms);
@@ -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
+ });