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.
- data/CHANGELOG +21 -0
- data/Rakefile +13 -1
- data/builtin/shaders/functions/noise.ejs +780 -7
- data/guides/partials/_top_nav.html.erb +17 -0
- data/guides/source/index.html.erb +7 -0
- data/guides/source/layout.html.erb +1 -12
- data/lib/jax/generators/app/templates/public/javascripts/jax.js +4 -1
- data/lib/jax/version.rb +1 -1
- data/spec/example_app/app/controllers/lighting_controller.js +0 -2
- data/spec/javascripts/shaders/preprocessor_spec.js +19 -4
- data/src/constants.yml +1 -1
- data/src/jax/anim_frame.js +1 -1
- data/src/jax/core/util.js +5 -0
- data/src/jax/webgl/shader.js +2 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/alias.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/class.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/class_deprecated.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/class_method.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/class_property.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/constant.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/constructor.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/deprecated.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/description.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/information.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/instance_method.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/instance_property.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/method.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/method_deprecated.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/mixin.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/namespace.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/property.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/related_to.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/search-background.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/section-background.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/section.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/selected-section-background.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/subclass.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/superclass.png +0 -0
- data/vendor/pdoc_template/html/assets/images/pdoc/utility.png +0 -0
- data/vendor/pdoc_template/html/assets/javascripts/pdoc/application.js +478 -0
- data/vendor/pdoc_template/html/assets/javascripts/pdoc/prototype.js +4874 -0
- data/vendor/pdoc_template/html/assets/javascripts/pdoc/tabs.js +506 -0
- data/vendor/pdoc_template/html/assets/stylesheets/jax.css +30 -0
- data/vendor/pdoc_template/html/assets/stylesheets/pdoc/api.css +681 -0
- data/vendor/pdoc_template/html/assets/stylesheets/pdoc/pygments.css +62 -0
- data/vendor/pdoc_template/html/helpers.rb +35 -0
- data/vendor/pdoc_template/html/index.erb +18 -0
- data/vendor/pdoc_template/html/item_index.js.erb +6 -0
- data/vendor/pdoc_template/html/layout.erb +67 -0
- data/vendor/pdoc_template/html/leaf.erb +22 -0
- data/vendor/pdoc_template/html/node.erb +30 -0
- data/vendor/pdoc_template/html/partials/class_relationships.erb +19 -0
- data/vendor/pdoc_template/html/partials/classes.erb +7 -0
- data/vendor/pdoc_template/html/partials/constructor.erb +5 -0
- data/vendor/pdoc_template/html/partials/description.erb +5 -0
- data/vendor/pdoc_template/html/partials/link_list.erb +1 -0
- data/vendor/pdoc_template/html/partials/method_signatures.erb +14 -0
- data/vendor/pdoc_template/html/partials/methodized_note.erb +9 -0
- data/vendor/pdoc_template/html/partials/mixins.erb +7 -0
- data/vendor/pdoc_template/html/partials/namespaces.erb +7 -0
- data/vendor/pdoc_template/html/partials/related_utilities.erb +5 -0
- data/vendor/pdoc_template/html/partials/relationships.erb +11 -0
- data/vendor/pdoc_template/html/partials/short_description_list.erb +7 -0
- data/vendor/pdoc_template/html/partials/title.erb +24 -0
- data/vendor/pdoc_template/html/section.erb +18 -0
- 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>
|
@@ -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
|
-
|
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.
|
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
@@ -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.
|
1
|
+
JAX_VERSION: 0.0.0.8
|
data/src/jax/anim_frame.js
CHANGED
@@ -31,7 +31,7 @@
|
|
31
31
|
|
32
32
|
|
33
33
|
/**
|
34
|
-
* Global
|
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
data/src/jax/webgl/shader.js
CHANGED
@@ -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);
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -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
|
+
});
|