jax 0.0.0.7 → 0.0.0.8

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 (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
+ });