topfunky-castanaut 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ module Castanaut
2
+
3
+ # Castanaut uses plugins to extend the available actions beyond simple
4
+ # mouse and keyboard input. Typically each plugin is application-specific.
5
+ # See the Safari, Mousepose and Ishowu plugins for examples, and review the
6
+ # README.txt for details on creating your own.
7
+ #
8
+ # In short, for a plugin called "foo", your script should have this structure:
9
+ #
10
+ # module Castanaut
11
+ # module Plugin
12
+ # module Foo
13
+ #
14
+ # # define your stage directions (ie, Movie instance methods) here.
15
+ #
16
+ # end
17
+ # end
18
+ # end
19
+ #
20
+ # The script must exist in a sub-directory of the screenplay's location
21
+ # called "plugins", and must be called (in this case): foo.rb.
22
+ #
23
+ module Plugin
24
+ end
25
+
26
+ end
@@ -0,0 +1,94 @@
1
+ module Castanaut; module Plugin
2
+
3
+ # This module provides primitive support for iShowU, a screencast capturing
4
+ # tool for Mac OS X from Shiny White Box.
5
+ #
6
+ # iShowU is widely considered a good, simple application for its purpose,
7
+ # but you're by no means required to use it for Castanaut. Simply write
8
+ # your own module for Snapz Pro, or ScreenFlow, or whatever you like.
9
+ #
10
+ # Shiny White Box is promising much better Applescript support in an
11
+ # imminent version, which could tidy up this module quite a bit.
12
+ #
13
+ # More info: http://www.shinywhitebox.com/home/home.html
14
+ module Ishowu
15
+
16
+ # Set the screencast to capture a particular region of the screen.
17
+ # Generate appropriately-formatted co-ordinates using Castanaut::Movie#to.
18
+ def ishowu_set_region(*options)
19
+ ishowu_applescriptify
20
+
21
+ options = combine_options(*options)
22
+
23
+ ishowu_menu_item("Capture", "Capture full screen")
24
+ sleep(0.2)
25
+ ishowu_menu_item("Capture", "Capture custom area", false)
26
+ sleep(0.2)
27
+ automatically "mousewarp 4 4"
28
+
29
+ drag to(options[:to][:left], options[:to][:top])
30
+
31
+ sleep(0.2)
32
+ bounds = screen_size
33
+ automatically "mousewarp #{bounds[:to][:width]} #{bounds[:to][:height]}"
34
+ drag to(
35
+ options[:to][:left] + options[:to][:width],
36
+ options[:to][:top] + options[:to][:height]
37
+ )
38
+ hit Enter
39
+ ishowu_hide
40
+ end
41
+
42
+ # Tell iShowU to start recording. Will automatically stop recording when
43
+ # the movie is ended, unless you set :auto_stop => false in options.
44
+ def ishowu_start_recording(options = {})
45
+ ishowu_hide
46
+ ishowu_menu_item("Capture", "Start capture")
47
+ unless options[:auto_stop] == false
48
+ at_end_of_movie { ishowu_stop_recording }
49
+ end
50
+ end
51
+
52
+ # Tell iShowU to stop recording.
53
+ def ishowu_stop_recording
54
+ ishowu_menu_item("Capture", "Stop capture")
55
+ end
56
+
57
+ # Execute an iShowU menu option.
58
+ def ishowu_menu_item(menu, item, quiet = true)
59
+ ascript = %Q`
60
+ tell application "iShowU"
61
+ activate
62
+ tell application "System Events"
63
+ click menu item "#{item}" of menu "#{menu}" of menu bar item "#{menu}" of menu bar 1 of process "iShowU"
64
+ #{'set visible of process "iShowU" to false' if quiet}
65
+ end tell
66
+ end
67
+ `
68
+ execute_applescript(ascript)
69
+ end
70
+
71
+ # Hide the iShowU window. This is a bit random, and suggestions are
72
+ # welcomed.
73
+ def ishowu_hide
74
+ ishowu_menu_item("iShowU", "Hide iShowU")
75
+ end
76
+
77
+ private
78
+ # iShowU is not Applescript-enabled out of the box. This fix, arguably
79
+ # a hack, lets us do some limited work with it in Applescript.
80
+ def ishowu_applescriptify
81
+ execute_applescript(%Q`
82
+ try
83
+ tell application "Finder"
84
+ set the a_app to (application file id "com.tcdc.Digitizer") as alias
85
+ end tell
86
+ set the plist_filepath to the quoted form of ¬
87
+ ((POSIX path of the a_app) & "Contents/Info")
88
+ do shell script "defaults write " & the plist_filepath & space ¬
89
+ & "NSAppleScriptEnabled -bool YES"
90
+ end try
91
+ `)
92
+ end
93
+ end
94
+ end; end
@@ -0,0 +1,38 @@
1
+ module Castanaut; module Plugin
2
+
3
+ # This module provides actions for controlling Mousepose, a commercial
4
+ # application from Boinx Software. Basically it lets you put a halo around
5
+ # the mouse whenever a key mouse action occurs.
6
+ #
7
+ # It doesn't do any configuration of Mousepose on the fly. Configure
8
+ # Mousepose settings before running your screenplay.
9
+ #
10
+ # Tested against Mousepose 2. More info: http://www.boinx.com/mousepose
11
+ module Mousepose
12
+
13
+ # Put a halo around the mouse. If a block is given to this method,
14
+ # the halo will be turned off when the block completes. Otherwise,
15
+ # you'll have to use dim to dismiss the halo.
16
+ def highlight
17
+ execute_applescript(%Q`
18
+ tell application "Mousepose"
19
+ start effect
20
+ end
21
+ `)
22
+ if block_given?
23
+ yield
24
+ dim
25
+ end
26
+ end
27
+
28
+ # Dismiss the halo around the mouse that was invoked by a previous
29
+ # highlight method.
30
+ def dim
31
+ execute_applescript(%Q`
32
+ tell application "Mousepose"
33
+ stop effect
34
+ end
35
+ `)
36
+ end
37
+ end
38
+ end; end
@@ -0,0 +1,124 @@
1
+ module Castanaut
2
+
3
+ module Plugin
4
+ # This module provides actions for controlling Safari. It's tested against
5
+ # Safari 3 on Mac OS X 10.5.2.
6
+ module Safari
7
+
8
+ # Open a URL in the front Safari tab.
9
+ def url(str)
10
+ execute_applescript(%Q`
11
+ tell application "safari"
12
+ do JavaScript "location.href = '#{str}'" in front document
13
+ end tell
14
+ `)
15
+ end
16
+
17
+ # Get the co-ordinates of an element in the front Safari tab. Use this
18
+ # with Castanaut::Movie#cursor to send the mouse cursor to the element.
19
+ #
20
+ # Options include:
21
+ # * :index - an integer (*n*) that gets the *n*th element matching the
22
+ # selector. Defaults to the first element.
23
+ # * :area - whereabouts in the element do you want the coordinates.
24
+ # Valid values are: left, center, right, and top, middle, bottom.
25
+ # Defaults to ["center", "middle"].
26
+ # If single axis is given (eg "left"), the other axis uses its default.
27
+ def to_element(selector, options = {})
28
+ pos = options.delete(:area)
29
+ coords = element_coordinates(selector, options)
30
+
31
+ x_axis, y_axis = [:center, :middle]
32
+ [pos].flatten.first(2).each do |p|
33
+ p = p.to_s.downcase
34
+ x_axis = p.to_sym if %w[left center right].include?(p)
35
+ y_axis = p.to_sym if %w[top middle bottom].include?(p)
36
+ end
37
+
38
+ edge_offset = options[:edge_offset] || 3
39
+ case x_axis
40
+ when :left
41
+ x = coords[0] + edge_offset
42
+ when :center
43
+ x = (coords[0] + coords[2] * 0.5).to_i
44
+ when :right
45
+ x = (coords[0] + coords[2]) - edge_offset
46
+ end
47
+
48
+ case y_axis
49
+ when :top
50
+ y = coords[1] + edge_offset
51
+ when :middle
52
+ y = (coords[1] + coords[3] * 0.5).to_i
53
+ when :bottom
54
+ y = (coords[1] + coords[3]) - edge_offset
55
+ end
56
+
57
+ result = { :to => { :left => x, :top => y } }
58
+ end
59
+
60
+ private
61
+ # Note: the script should set the Castanaut.result variable.
62
+ def execute_javascript(scpt)
63
+ execute_applescript %Q`
64
+ tell application "Safari"
65
+ do JavaScript "
66
+ document.oldTitle = document.title;
67
+ #{escape_dq(scpt)}
68
+ if (typeof Castanaut.result != 'undefined') {
69
+ document.title = Castanaut.result;
70
+ }
71
+ " in front document
72
+ set the_result to ((name of window 1) as string)
73
+ do JavaScript "
74
+ document.title = document.oldTitle;
75
+ " in front document
76
+ return the_result
77
+ end tell
78
+ `
79
+ end
80
+
81
+ def element_coordinates(selector, options = {})
82
+ index = options[:index] || 0
83
+ gebys = script('gebys.js')
84
+ cjs = script('coords.js')
85
+ coords = execute_javascript(%Q`
86
+ #{gebys}
87
+ #{cjs}
88
+ Castanaut.result = Castanaut.Coords.forElement(
89
+ '#{selector}',
90
+ #{index}
91
+ );
92
+ `)
93
+
94
+ unless coords.match(/\d+ \d+ \d+ \d+/)
95
+ raise Castanaut::Exceptions::ElementNotFound
96
+ end
97
+
98
+ coords = coords.split(' ').collect {|c| c.to_i}
99
+
100
+ if coords.any? {|c| c < 0 }
101
+ raise Castanaut::Exceptions::ElementOffScreen
102
+ end
103
+
104
+ coords
105
+ end
106
+
107
+ end
108
+ end
109
+
110
+ module Exceptions
111
+ # When getting an element's coordinates, this is raised if no element on
112
+ # the page matches the selector given.
113
+ class ElementNotFound < CastanautError
114
+ end
115
+
116
+ # When getting an element's coordinates, this is raised if the element
117
+ # is found, but cannot be shown on the screen. Normally, we automatically
118
+ # scroll to an element that is currently off-screen, but sometimes that
119
+ # might not be possible (such as if the element is display: none).
120
+ class ElementOffScreen < CastanautError
121
+ end
122
+ end
123
+
124
+ end
@@ -0,0 +1,39 @@
1
+ module Castanaut; module Plugin
2
+
3
+ ##
4
+ # Contributed by Geoffrey Grosenbach
5
+ # http://peepcode.com
6
+
7
+ module Textmate
8
+
9
+ ##
10
+ # Types text into TextMate all at once.
11
+ #
12
+ # The as_snippet option is documented but doesn't seem to do anything.
13
+
14
+ def tm_insert_text(text, as_snippet=false)
15
+ escaped_text = text.gsub(/"/, '\"')
16
+ snippet_directive = (as_snippet ? "with as snippet" : "")
17
+
18
+ execute_applescript(%Q`
19
+ tell application "TextMate"
20
+ insert "#{escaped_text}" #{snippet_directive}
21
+ end
22
+ `)
23
+ end
24
+
25
+ ##
26
+ # Open a file, optionally at a specific line and column.
27
+
28
+ def tm_get_url(file_path, line=0, column=0)
29
+ full_url = "txmt://open?url=file://#{file_path}&line=#{line}&column=#{column}"
30
+ execute_applescript(%Q`
31
+ tell application "TextMate"
32
+ get url "#{full_url}"
33
+ end
34
+ `)
35
+ end
36
+
37
+ end
38
+
39
+ end; end
@@ -0,0 +1,48 @@
1
+ var Castanaut = Castanaut || {};
2
+
3
+ Castanaut.Coords = Castanaut.Coords || {
4
+ documentPos: function (obj) {
5
+ origObj = obj;
6
+ var curleft = curtop = 0;
7
+ if (obj.offsetParent) {
8
+ do {
9
+ curleft += obj.offsetLeft;
10
+ curtop += obj.offsetTop;
11
+ } while (obj = obj.offsetParent);
12
+ }
13
+ return [curleft,curtop,origObj.offsetWidth,origObj.offsetHeight];
14
+ },
15
+
16
+ windowPos: function (obj) {
17
+ var pos = Castanaut.Coords.documentPos(obj);
18
+ pos[0] -= window.scrollX;
19
+ pos[1] -= window.scrollY;
20
+ if (pos[0] > window.innerWidth || pos[0] < 0) {
21
+ pos[0] = -1;
22
+ }
23
+ if (pos[1] > window.innerHeight || pos[1] < 0) {
24
+ pos[1] = -1;
25
+ }
26
+ return pos;
27
+ },
28
+
29
+ forElement: function (selector, index) {
30
+ var obj = Castanaut.DomQuery.select(selector)[index];
31
+ var pos = Castanaut.Coords.windowPos(obj);
32
+ if (pos[0] < 0 || pos[1] < 0) {
33
+ obj.scrollIntoView();
34
+ pos = Castanaut.Coords.windowPos(obj);
35
+ if (pos[0] < 0 || pos[1] < 0) {
36
+ return pos.join(' ');
37
+ }
38
+ }
39
+
40
+ pos[0] += window.screenX + (window.outerWidth - window.innerWidth);
41
+
42
+ pos[1] += window.screenY + (window.outerHeight - window.innerHeight);
43
+ if (window.statusbar.visible) {
44
+ pos[1] -= 14;
45
+ }
46
+ return pos.join(' ');
47
+ }
48
+ }
@@ -0,0 +1,612 @@
1
+ // NB: this is actually a slightly modified form of the Ext JS implementation,
2
+ // re-namespaced to avoid conflicts. (See also Copyright.txt.)
3
+ var Castanaut = Castanaut || {};
4
+ Castanaut.DomQuery = Castanaut.DomQuery || function(){
5
+ var cache = {}, simpleCache = {}, valueCache = {};
6
+ var nonSpace = /\S/;
7
+ var trimRe = /^\s+|\s+$/g;
8
+ var tplRe = /\{(\d+)\}/g;
9
+ var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
10
+ var tagTokenRe = /^(#)?([\w-\*]+)/;
11
+ var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
12
+
13
+ function child(p, index){
14
+ var i = 0;
15
+ var n = p.firstChild;
16
+ while(n){
17
+ if(n.nodeType == 1){
18
+ if(++i == index){
19
+ return n;
20
+ }
21
+ }
22
+ n = n.nextSibling;
23
+ }
24
+ return null;
25
+ };
26
+
27
+ function next(n){
28
+ while((n = n.nextSibling) && n.nodeType != 1);
29
+ return n;
30
+ };
31
+
32
+ function prev(n){
33
+ while((n = n.previousSibling) && n.nodeType != 1);
34
+ return n;
35
+ };
36
+
37
+ function children(d){
38
+ var n = d.firstChild, ni = -1;
39
+ while(n){
40
+ var nx = n.nextSibling;
41
+ if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
42
+ d.removeChild(n);
43
+ }else{
44
+ n.nodeIndex = ++ni;
45
+ }
46
+ n = nx;
47
+ }
48
+ return this;
49
+ };
50
+
51
+ function byClassName(c, a, v){
52
+ if(!v){
53
+ return c;
54
+ }
55
+ var r = [], ri = -1, cn;
56
+ for(var i = 0, ci; ci = c[i]; i++){
57
+ if((' '+ci.className+' ').indexOf(v) != -1){
58
+ r[++ri] = ci;
59
+ }
60
+ }
61
+ return r;
62
+ };
63
+
64
+ function attrValue(n, attr){
65
+ if(!n.tagName && typeof n.length != "undefined"){
66
+ n = n[0];
67
+ }
68
+ if(!n){
69
+ return null;
70
+ }
71
+ if(attr == "for"){
72
+ return n.htmlFor;
73
+ }
74
+ if(attr == "class" || attr == "className"){
75
+ return n.className;
76
+ }
77
+ return n.getAttribute(attr) || n[attr];
78
+
79
+ };
80
+
81
+ function getNodes(ns, mode, tagName){
82
+ var result = [], ri = -1, cs;
83
+ if(!ns){
84
+ return result;
85
+ }
86
+ tagName = tagName || "*";
87
+ if(typeof ns.getElementsByTagName != "undefined"){
88
+ ns = [ns];
89
+ }
90
+ if(!mode){
91
+ for(var i = 0, ni; ni = ns[i]; i++){
92
+ cs = ni.getElementsByTagName(tagName);
93
+ for(var j = 0, ci; ci = cs[j]; j++){
94
+ result[++ri] = ci;
95
+ }
96
+ }
97
+ }else if(mode == "/" || mode == ">"){
98
+ var utag = tagName.toUpperCase();
99
+ for(var i = 0, ni, cn; ni = ns[i]; i++){
100
+ cn = ni.children || ni.childNodes;
101
+ for(var j = 0, cj; cj = cn[j]; j++){
102
+ if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
103
+ result[++ri] = cj;
104
+ }
105
+ }
106
+ }
107
+ }else if(mode == "+"){
108
+ var utag = tagName.toUpperCase();
109
+ for(var i = 0, n; n = ns[i]; i++){
110
+ while((n = n.nextSibling) && n.nodeType != 1);
111
+ if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
112
+ result[++ri] = n;
113
+ }
114
+ }
115
+ }else if(mode == "~"){
116
+ for(var i = 0, n; n = ns[i]; i++){
117
+ while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
118
+ if(n){
119
+ result[++ri] = n;
120
+ }
121
+ }
122
+ }
123
+ return result;
124
+ };
125
+
126
+ function concat(a, b){
127
+ if(b.slice){
128
+ return a.concat(b);
129
+ }
130
+ for(var i = 0, l = b.length; i < l; i++){
131
+ a[a.length] = b[i];
132
+ }
133
+ return a;
134
+ }
135
+
136
+ function byTag(cs, tagName){
137
+ if(cs.tagName || cs == document){
138
+ cs = [cs];
139
+ }
140
+ if(!tagName){
141
+ return cs;
142
+ }
143
+ var r = [], ri = -1;
144
+ tagName = tagName.toLowerCase();
145
+ for(var i = 0, ci; ci = cs[i]; i++){
146
+ if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
147
+ r[++ri] = ci;
148
+ }
149
+ }
150
+ return r;
151
+ };
152
+
153
+ function byId(cs, attr, id){
154
+ if(cs.tagName || cs == document){
155
+ cs = [cs];
156
+ }
157
+ if(!id){
158
+ return cs;
159
+ }
160
+ var r = [], ri = -1;
161
+ for(var i = 0,ci; ci = cs[i]; i++){
162
+ if(ci && ci.id == id){
163
+ r[++ri] = ci;
164
+ return r;
165
+ }
166
+ }
167
+ return r;
168
+ };
169
+
170
+ function byAttribute(cs, attr, value, op, custom){
171
+ var r = [], ri = -1, st = custom=="{";
172
+ var f = Castanaut.DomQuery.operators[op];
173
+ for(var i = 0, ci; ci = cs[i]; i++){
174
+ var a;
175
+ if(st){
176
+ a = Castanaut.DomQuery.getStyle(ci, attr);
177
+ }
178
+ else if(attr == "class" || attr == "className"){
179
+ a = ci.className;
180
+ }else if(attr == "for"){
181
+ a = ci.htmlFor;
182
+ }else if(attr == "href"){
183
+ a = ci.getAttribute("href", 2);
184
+ }else{
185
+ a = ci.getAttribute(attr);
186
+ }
187
+ if((f && f(a, value)) || (!f && a)){
188
+ r[++ri] = ci;
189
+ }
190
+ }
191
+ return r;
192
+ };
193
+
194
+ function byPseudo(cs, name, value){
195
+ return Castanaut.DomQuery.pseudos[name](cs, value);
196
+ };
197
+
198
+ eval("var batch = 30803;");
199
+
200
+ var key = 30803;
201
+
202
+ function nodup(cs){
203
+ if(!cs){
204
+ return [];
205
+ }
206
+ var len = cs.length, c, i, r = cs, cj, ri = -1;
207
+ if(!len || typeof cs.nodeType != "undefined" || len == 1){
208
+ return cs;
209
+ }
210
+ var d = ++key;
211
+ cs[0]._nodup = d;
212
+ for(i = 1; c = cs[i]; i++){
213
+ if(c._nodup != d){
214
+ c._nodup = d;
215
+ }else{
216
+ r = [];
217
+ for(var j = 0; j < i; j++){
218
+ r[++ri] = cs[j];
219
+ }
220
+ for(j = i+1; cj = cs[j]; j++){
221
+ if(cj._nodup != d){
222
+ cj._nodup = d;
223
+ r[++ri] = cj;
224
+ }
225
+ }
226
+ return r;
227
+ }
228
+ }
229
+ return r;
230
+ }
231
+
232
+ function quickDiff(c1, c2){
233
+ var len1 = c1.length;
234
+ if(!len1){
235
+ return c2;
236
+ }
237
+ var d = ++key;
238
+ for(var i = 0; i < len1; i++){
239
+ c1[i]._qdiff = d;
240
+ }
241
+ var r = [];
242
+ for(var i = 0, len = c2.length; i < len; i++){
243
+ if(c2[i]._qdiff != d){
244
+ r[r.length] = c2[i];
245
+ }
246
+ }
247
+ return r;
248
+ }
249
+
250
+ function quickId(ns, mode, root, id){
251
+ if(ns == root){
252
+ var d = root.ownerDocument || root;
253
+ return d.getElementById(id);
254
+ }
255
+ ns = getNodes(ns, mode, "*");
256
+ return byId(ns, null, id);
257
+ }
258
+
259
+ return {
260
+ getStyle : function(el, name){
261
+ return Ext.fly(el).getStyle(name);
262
+ },
263
+ compile : function(path, type){
264
+ type = type || "select";
265
+
266
+ var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
267
+ var q = path, mode, lq;
268
+ var tk = Castanaut.DomQuery.matchers;
269
+ var tklen = tk.length;
270
+ var mm;
271
+
272
+ var lmode = q.match(modeRe);
273
+ if(lmode && lmode[1]){
274
+ fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
275
+ q = q.replace(lmode[1], "");
276
+ }
277
+ while(path.substr(0, 1)=="/"){
278
+ path = path.substr(1);
279
+ }
280
+
281
+ while(q && lq != q){
282
+ lq = q;
283
+ var tm = q.match(tagTokenRe);
284
+ if(type == "select"){
285
+ if(tm){
286
+ if(tm[1] == "#"){
287
+ fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
288
+ }else{
289
+ fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
290
+ }
291
+ q = q.replace(tm[0], "");
292
+ }else if(q.substr(0, 1) != '@'){
293
+ fn[fn.length] = 'n = getNodes(n, mode, "*");';
294
+ }
295
+ }else{
296
+ if(tm){
297
+ if(tm[1] == "#"){
298
+ fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
299
+ }else{
300
+ fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
301
+ }
302
+ q = q.replace(tm[0], "");
303
+ }
304
+ }
305
+ while(!(mm = q.match(modeRe))){
306
+ var matched = false;
307
+ for(var j = 0; j < tklen; j++){
308
+ var t = tk[j];
309
+ var m = q.match(t.re);
310
+ if(m){
311
+ fn[fn.length] = t.select.replace(tplRe, function(x, i){
312
+ return m[i];
313
+ });
314
+ q = q.replace(m[0], "");
315
+ matched = true;
316
+ break;
317
+ }
318
+ }
319
+ if(!matched){
320
+ throw 'Error parsing selector, parsing failed at "' + q + '"';
321
+ }
322
+ }
323
+ if(mm[1]){
324
+ fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
325
+ q = q.replace(mm[1], "");
326
+ }
327
+ }
328
+ fn[fn.length] = "return nodup(n);\n}";
329
+ eval(fn.join(""));
330
+ return f;
331
+ },
332
+
333
+ select : function(path, root, type){
334
+ if(!root || root == document){
335
+ root = document;
336
+ }
337
+ if(typeof root == "string"){
338
+ root = document.getElementById(root);
339
+ }
340
+ var paths = path.split(",");
341
+ var results = [];
342
+ for(var i = 0, len = paths.length; i < len; i++){
343
+ var p = paths[i].replace(trimRe, "");
344
+ if(!cache[p]){
345
+ cache[p] = Castanaut.DomQuery.compile(p);
346
+ if(!cache[p]){
347
+ throw p + " is not a valid selector";
348
+ }
349
+ }
350
+ var result = cache[p](root);
351
+ if(result && result != document){
352
+ results = results.concat(result);
353
+ }
354
+ }
355
+ if(paths.length > 1){
356
+ return nodup(results);
357
+ }
358
+ return results;
359
+ },
360
+
361
+ selectNode : function(path, root){
362
+ return Castanaut.DomQuery.select(path, root)[0];
363
+ },
364
+
365
+ selectValue : function(path, root, defaultValue){
366
+ path = path.replace(trimRe, "");
367
+ if(!valueCache[path]){
368
+ valueCache[path] = Castanaut.DomQuery.compile(path, "select");
369
+ }
370
+ var n = valueCache[path](root);
371
+ n = n[0] ? n[0] : n;
372
+ var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
373
+ return ((v === null||v === undefined||v==='') ? defaultValue : v);
374
+ },
375
+
376
+ selectNumber : function(path, root, defaultValue){
377
+ var v = Castanaut.DomQuery.selectValue(path, root, defaultValue || 0);
378
+ return parseFloat(v);
379
+ },
380
+
381
+ is : function(el, ss){
382
+ if(typeof el == "string"){
383
+ el = document.getElementById(el);
384
+ }
385
+ var isArray = (el instanceof Array);
386
+ var result = Castanaut.DomQuery.filter(isArray ? el : [el], ss);
387
+ return isArray ? (result.length == el.length) : (result.length > 0);
388
+ },
389
+
390
+ filter : function(els, ss, nonMatches){
391
+ ss = ss.replace(trimRe, "");
392
+ if(!simpleCache[ss]){
393
+ simpleCache[ss] = Castanaut.DomQuery.compile(ss, "simple");
394
+ }
395
+ var result = simpleCache[ss](els);
396
+ return nonMatches ? quickDiff(result, els) : result;
397
+ },
398
+
399
+ matchers : [{
400
+ re: /^\.([\w-]+)/,
401
+ select: 'n = byClassName(n, null, " {1} ");'
402
+ }, {
403
+ re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
404
+ select: 'n = byPseudo(n, "{1}", "{2}");'
405
+ },{
406
+ re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
407
+ select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
408
+ }, {
409
+ re: /^#([\w-]+)/,
410
+ select: 'n = byId(n, null, "{1}");'
411
+ },{
412
+ re: /^@([\w-]+)/,
413
+ select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
414
+ }
415
+ ],
416
+
417
+ operators : {
418
+ "=" : function(a, v){
419
+ return a == v;
420
+ },
421
+ "!=" : function(a, v){
422
+ return a != v;
423
+ },
424
+ "^=" : function(a, v){
425
+ return a && a.substr(0, v.length) == v;
426
+ },
427
+ "$=" : function(a, v){
428
+ return a && a.substr(a.length-v.length) == v;
429
+ },
430
+ "*=" : function(a, v){
431
+ return a && a.indexOf(v) !== -1;
432
+ },
433
+ "%=" : function(a, v){
434
+ return (a % v) == 0;
435
+ },
436
+ "|=" : function(a, v){
437
+ return a && (a == v || a.substr(0, v.length+1) == v+'-');
438
+ },
439
+ "~=" : function(a, v){
440
+ return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
441
+ }
442
+ },
443
+
444
+ pseudos : {
445
+ "first-child" : function(c){
446
+ var r = [], ri = -1, n;
447
+ for(var i = 0, ci; ci = n = c[i]; i++){
448
+ while((n = n.previousSibling) && n.nodeType != 1);
449
+ if(!n){
450
+ r[++ri] = ci;
451
+ }
452
+ }
453
+ return r;
454
+ },
455
+
456
+ "last-child" : function(c){
457
+ var r = [], ri = -1, n;
458
+ for(var i = 0, ci; ci = n = c[i]; i++){
459
+ while((n = n.nextSibling) && n.nodeType != 1);
460
+ if(!n){
461
+ r[++ri] = ci;
462
+ }
463
+ }
464
+ return r;
465
+ },
466
+
467
+ "nth-child" : function(c, a) {
468
+ var r = [], ri = -1;
469
+ var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
470
+ var f = (m[1] || 1) - 0, l = m[2] - 0;
471
+ for(var i = 0, n; n = c[i]; i++){
472
+ var pn = n.parentNode;
473
+ if (batch != pn._batch) {
474
+ var j = 0;
475
+ for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
476
+ if(cn.nodeType == 1){
477
+ cn.nodeIndex = ++j;
478
+ }
479
+ }
480
+ pn._batch = batch;
481
+ }
482
+ if (f == 1) {
483
+ if (l == 0 || n.nodeIndex == l){
484
+ r[++ri] = n;
485
+ }
486
+ } else if ((n.nodeIndex + l) % f == 0){
487
+ r[++ri] = n;
488
+ }
489
+ }
490
+
491
+ return r;
492
+ },
493
+
494
+ "only-child" : function(c){
495
+ var r = [], ri = -1;;
496
+ for(var i = 0, ci; ci = c[i]; i++){
497
+ if(!prev(ci) && !next(ci)){
498
+ r[++ri] = ci;
499
+ }
500
+ }
501
+ return r;
502
+ },
503
+
504
+ "empty" : function(c){
505
+ var r = [], ri = -1;
506
+ for(var i = 0, ci; ci = c[i]; i++){
507
+ var cns = ci.childNodes, j = 0, cn, empty = true;
508
+ while(cn = cns[j]){
509
+ ++j;
510
+ if(cn.nodeType == 1 || cn.nodeType == 3){
511
+ empty = false;
512
+ break;
513
+ }
514
+ }
515
+ if(empty){
516
+ r[++ri] = ci;
517
+ }
518
+ }
519
+ return r;
520
+ },
521
+
522
+ "contains" : function(c, v){
523
+ var r = [], ri = -1;
524
+ for(var i = 0, ci; ci = c[i]; i++){
525
+ if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
526
+ r[++ri] = ci;
527
+ }
528
+ }
529
+ return r;
530
+ },
531
+
532
+ "nodeValue" : function(c, v){
533
+ var r = [], ri = -1;
534
+ for(var i = 0, ci; ci = c[i]; i++){
535
+ if(ci.firstChild && ci.firstChild.nodeValue == v){
536
+ r[++ri] = ci;
537
+ }
538
+ }
539
+ return r;
540
+ },
541
+
542
+ "checked" : function(c){
543
+ var r = [], ri = -1;
544
+ for(var i = 0, ci; ci = c[i]; i++){
545
+ if(ci.checked == true){
546
+ r[++ri] = ci;
547
+ }
548
+ }
549
+ return r;
550
+ },
551
+
552
+ "not" : function(c, ss){
553
+ return Castanaut.DomQuery.filter(c, ss, true);
554
+ },
555
+
556
+ "odd" : function(c){
557
+ return this["nth-child"](c, "odd");
558
+ },
559
+
560
+ "even" : function(c){
561
+ return this["nth-child"](c, "even");
562
+ },
563
+
564
+ "nth" : function(c, a){
565
+ return c[a-1] || [];
566
+ },
567
+
568
+ "first" : function(c){
569
+ return c[0] || [];
570
+ },
571
+
572
+ "last" : function(c){
573
+ return c[c.length-1] || [];
574
+ },
575
+
576
+ "has" : function(c, ss){
577
+ var s = Castanaut.DomQuery.select;
578
+ var r = [], ri = -1;
579
+ for(var i = 0, ci; ci = c[i]; i++){
580
+ if(s(ss, ci).length > 0){
581
+ r[++ri] = ci;
582
+ }
583
+ }
584
+ return r;
585
+ },
586
+
587
+ "next" : function(c, ss){
588
+ var is = Castanaut.DomQuery.is;
589
+ var r = [], ri = -1;
590
+ for(var i = 0, ci; ci = c[i]; i++){
591
+ var n = next(ci);
592
+ if(n && is(n, ss)){
593
+ r[++ri] = ci;
594
+ }
595
+ }
596
+ return r;
597
+ },
598
+
599
+ "prev" : function(c, ss){
600
+ var is = Castanaut.DomQuery.is;
601
+ var r = [], ri = -1;
602
+ for(var i = 0, ci; ci = c[i]; i++){
603
+ var n = prev(ci);
604
+ if(n && is(n, ss)){
605
+ r[++ri] = ci;
606
+ }
607
+ }
608
+ return r;
609
+ }
610
+ }
611
+ };
612
+ }();