rb-scpt 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/bin/rb-scpt-1.0.1.gem +0 -0
  3. data/extconf.rb +12 -12
  4. data/rb-scpt.gemspec +10 -10
  5. data/sample/AB_export_vcard.rb +16 -16
  6. data/sample/AB_list_people_with_emails.rb +4 -4
  7. data/sample/Add_iCal_event.rb +12 -12
  8. data/sample/Create_daily_iCal_todos.rb +28 -28
  9. data/sample/Export_Address_Book_phone_numbers.rb +52 -52
  10. data/sample/Hello_world.rb +10 -10
  11. data/sample/List_iTunes_playlist_names.rb +3 -3
  12. data/sample/Make_Mail_message.rb +24 -24
  13. data/sample/Open_file_in_TextEdit.rb +5 -5
  14. data/sample/Organize_Mail_messages.rb +46 -46
  15. data/sample/Print_folder_tree.rb +5 -5
  16. data/sample/Select_all_HTML_files.rb +6 -6
  17. data/sample/Set_iChat_status.rb +12 -12
  18. data/sample/Simple_Finder_GUI_Scripting.rb +6 -6
  19. data/sample/Stagger_Finder_windows.rb +9 -9
  20. data/sample/TextEdit_demo.rb +71 -71
  21. data/sample/iTunes_top40_to_html.rb +28 -30
  22. data/src/SendThreadSafe.c +293 -293
  23. data/src/SendThreadSafe.h +108 -108
  24. data/src/lib/_aem/aemreference.rb +997 -998
  25. data/src/lib/_aem/codecs.rb +609 -610
  26. data/src/lib/_aem/connect.rb +197 -197
  27. data/src/lib/_aem/encodingsupport.rb +67 -67
  28. data/src/lib/_aem/findapp.rb +75 -75
  29. data/src/lib/_aem/mactypes.rb +241 -242
  30. data/src/lib/_aem/send.rb +268 -268
  31. data/src/lib/_aem/typewrappers.rb +52 -52
  32. data/src/lib/_appscript/defaultterminology.rb +266 -266
  33. data/src/lib/_appscript/referencerenderer.rb +230 -233
  34. data/src/lib/_appscript/reservedkeywords.rb +106 -106
  35. data/src/lib/_appscript/safeobject.rb +125 -125
  36. data/src/lib/_appscript/terminology.rb +448 -449
  37. data/src/lib/aem.rb +238 -238
  38. data/src/lib/kae.rb +1487 -1487
  39. data/src/lib/osax.rb +647 -647
  40. data/src/lib/rb-scpt.rb +1065 -1065
  41. data/src/rbae.c +595 -595
  42. data/test/test_aemreference.rb +104 -107
  43. data/test/test_appscriptcommands.rb +131 -134
  44. data/test/test_appscriptreference.rb +96 -99
  45. data/test/test_codecs.rb +166 -168
  46. data/test/test_findapp.rb +13 -16
  47. data/test/test_mactypes.rb +70 -72
  48. data/test/test_osax.rb +46 -48
  49. data/test/testall.sh +4 -4
  50. metadata +8 -7
@@ -8,652 +8,652 @@ require "rb-scpt"
8
8
 
9
9
  module OSAX
10
10
 
11
-
12
- ######################################################################
13
- # PRIVATE
14
- ######################################################################
15
-
16
- require 'rexml/document'
17
- require "ae"
18
- require "kae"
19
- require "aem"
20
- require "_appscript/reservedkeywords" # names of all existing methods on ASReference::Application
21
-
22
- StandardAdditionsEnums = [
23
- ["stop", "\000\000\000\000"],
24
- ["note", "\000\000\000\001"],
25
- ["caution", "\000\000\000\002"]]
26
-
27
- UTF8ToMacRoman = {
28
- 0 => 0,
29
- 1 => 1,
30
- 2 => 2,
31
- 3 => 3,
32
- 4 => 4,
33
- 5 => 5,
34
- 6 => 6,
35
- 7 => 7,
36
- 8 => 8,
37
- 9 => 9,
38
- 10 => 10,
39
- 11 => 11,
40
- 12 => 12,
41
- 13 => 13,
42
- 14 => 14,
43
- 15 => 15,
44
- 16 => 16,
45
- 17 => 17,
46
- 18 => 18,
47
- 19 => 19,
48
- 20 => 20,
49
- 21 => 21,
50
- 22 => 22,
51
- 23 => 23,
52
- 24 => 24,
53
- 25 => 25,
54
- 26 => 26,
55
- 27 => 27,
56
- 28 => 28,
57
- 29 => 29,
58
- 30 => 30,
59
- 31 => 31,
60
- 32 => 32,
61
- 33 => 33,
62
- 34 => 34,
63
- 35 => 35,
64
- 36 => 36,
65
- 37 => 37,
66
- 38 => 38,
67
- 39 => 39,
68
- 40 => 40,
69
- 41 => 41,
70
- 42 => 42,
71
- 43 => 43,
72
- 44 => 44,
73
- 45 => 45,
74
- 46 => 46,
75
- 47 => 47,
76
- 48 => 48,
77
- 49 => 49,
78
- 50 => 50,
79
- 51 => 51,
80
- 52 => 52,
81
- 53 => 53,
82
- 54 => 54,
83
- 55 => 55,
84
- 56 => 56,
85
- 57 => 57,
86
- 58 => 58,
87
- 59 => 59,
88
- 60 => 60,
89
- 61 => 61,
90
- 62 => 62,
91
- 63 => 63,
92
- 64 => 64,
93
- 65 => 65,
94
- 66 => 66,
95
- 67 => 67,
96
- 68 => 68,
97
- 69 => 69,
98
- 70 => 70,
99
- 71 => 71,
100
- 72 => 72,
101
- 73 => 73,
102
- 74 => 74,
103
- 75 => 75,
104
- 76 => 76,
105
- 77 => 77,
106
- 78 => 78,
107
- 79 => 79,
108
- 80 => 80,
109
- 81 => 81,
110
- 82 => 82,
111
- 83 => 83,
112
- 84 => 84,
113
- 85 => 85,
114
- 86 => 86,
115
- 87 => 87,
116
- 88 => 88,
117
- 89 => 89,
118
- 90 => 90,
119
- 91 => 91,
120
- 92 => 92,
121
- 93 => 93,
122
- 94 => 94,
123
- 95 => 95,
124
- 96 => 96,
125
- 97 => 97,
126
- 98 => 98,
127
- 99 => 99,
128
- 100 => 100,
129
- 101 => 101,
130
- 102 => 102,
131
- 103 => 103,
132
- 104 => 104,
133
- 105 => 105,
134
- 106 => 106,
135
- 107 => 107,
136
- 108 => 108,
137
- 109 => 109,
138
- 110 => 110,
139
- 111 => 111,
140
- 112 => 112,
141
- 113 => 113,
142
- 114 => 114,
143
- 115 => 115,
144
- 116 => 116,
145
- 117 => 117,
146
- 118 => 118,
147
- 119 => 119,
148
- 120 => 120,
149
- 121 => 121,
150
- 122 => 122,
151
- 123 => 123,
152
- 124 => 124,
153
- 125 => 125,
154
- 126 => 126,
155
- 127 => 127,
156
- 196 => 128,
157
- 197 => 129,
158
- 199 => 130,
159
- 201 => 131,
160
- 209 => 132,
161
- 214 => 133,
162
- 220 => 134,
163
- 225 => 135,
164
- 224 => 136,
165
- 226 => 137,
166
- 228 => 138,
167
- 227 => 139,
168
- 229 => 140,
169
- 231 => 141,
170
- 233 => 142,
171
- 232 => 143,
172
- 234 => 144,
173
- 235 => 145,
174
- 237 => 146,
175
- 236 => 147,
176
- 238 => 148,
177
- 239 => 149,
178
- 241 => 150,
179
- 243 => 151,
180
- 242 => 152,
181
- 244 => 153,
182
- 246 => 154,
183
- 245 => 155,
184
- 250 => 156,
185
- 249 => 157,
186
- 251 => 158,
187
- 252 => 159,
188
- 8224 => 160,
189
- 176 => 161,
190
- 162 => 162,
191
- 163 => 163,
192
- 167 => 164,
193
- 8226 => 165,
194
- 182 => 166,
195
- 223 => 167,
196
- 174 => 168,
197
- 169 => 169,
198
- 8482 => 170,
199
- 180 => 171,
200
- 168 => 172,
201
- 8800 => 173,
202
- 198 => 174,
203
- 216 => 175,
204
- 8734 => 176,
205
- 177 => 177,
206
- 8804 => 178,
207
- 8805 => 179,
208
- 165 => 180,
209
- 181 => 181,
210
- 8706 => 182,
211
- 8721 => 183,
212
- 8719 => 184,
213
- 960 => 185,
214
- 8747 => 186,
215
- 170 => 187,
216
- 186 => 188,
217
- 937 => 189,
218
- 230 => 190,
219
- 248 => 191,
220
- 191 => 192,
221
- 161 => 193,
222
- 172 => 194,
223
- 8730 => 195,
224
- 402 => 196,
225
- 8776 => 197,
226
- 8710 => 198,
227
- 171 => 199,
228
- 187 => 200,
229
- 8230 => 201,
230
- 160 => 202,
231
- 192 => 203,
232
- 195 => 204,
233
- 213 => 205,
234
- 338 => 206,
235
- 339 => 207,
236
- 8211 => 208,
237
- 8212 => 209,
238
- 8220 => 210,
239
- 8221 => 211,
240
- 8216 => 212,
241
- 8217 => 213,
242
- 247 => 214,
243
- 9674 => 215,
244
- 255 => 216,
245
- 376 => 217,
246
- 8260 => 218,
247
- 8364 => 219,
248
- 8249 => 220,
249
- 8250 => 221,
250
- 64257 => 222,
251
- 64258 => 223,
252
- 8225 => 224,
253
- 183 => 225,
254
- 8218 => 226,
255
- 8222 => 227,
256
- 8240 => 228,
257
- 194 => 229,
258
- 202 => 230,
259
- 193 => 231,
260
- 203 => 232,
261
- 200 => 233,
262
- 205 => 234,
263
- 206 => 235,
264
- 207 => 236,
265
- 204 => 237,
266
- 211 => 238,
267
- 212 => 239,
268
- 63743 => 240,
269
- 210 => 241,
270
- 218 => 242,
271
- 219 => 243,
272
- 217 => 244,
273
- 305 => 245,
274
- 710 => 246,
275
- 732 => 247,
276
- 175 => 248,
277
- 728 => 249,
278
- 729 => 250,
279
- 730 => 251,
280
- 184 => 252,
281
- 733 => 253,
282
- 731 => 254,
283
- 711 => 255}
284
-
285
- class SdefParser
286
- @@_name_cache = {}
287
- LegalFirst = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
288
- LegalRest = LegalFirst + '0123456789'
289
- @@_reserved_keywords = {} # ersatz set
290
- ReservedKeywords.each { |name| @@_reserved_keywords[name] = nil }
291
-
292
- attr_reader :properties, :elements, :classes, :enumerators
293
-
294
- def commands
295
- return @commands.values
296
- end
297
-
298
- def initialize
299
- # terminology tables; order is significant where synonym definitions occur
300
- @commands = {}
301
- @properties = []
302
- @elements = []
303
- @classes = []
304
- @enumerators = []
305
- end
306
-
307
- def _name(s)
308
- # Read a MacRoman-encoded Pascal keyword string.
309
- if not @@_name_cache.has_key?(s)
310
- legal = LegalFirst
311
- res = ''
312
- s.split(//).each do |c|
313
- if legal[c]
314
- res += c
315
- else
316
- case c
317
- when ' ', '-', '/'
318
- res += '_'
319
- when '&'
320
- res += 'and'
321
- else
322
- if res == ''
323
- res = '_'
324
- end
325
- res += "0x#{c.unpack('HXh')}"
326
- end
327
- end
328
- legal = LegalRest
329
- end
330
- if res[0, 3] == 'AS_' or @@_reserved_keywords.has_key?(res) or res[0, 1] == '_'
331
- res += '_'
332
- end
333
- @@_name_cache[s] = res
334
- end
335
- return @@_name_cache[s]
336
- end
337
-
338
- def _code(s)
339
- return s.unpack('U*').collect do |c| # unpack UTF8-encoded byte string
340
- OSAX::UTF8ToMacRoman.fetch(c)
341
- end .pack('C*') # pack as MacRoman-encoded byte string (four- or eight-char code)
342
- end
343
-
344
- def _addnamecode(node, collection)
345
- name = _name(node.attributes['name'])
346
- code = _code(node.attributes['code'])
347
- if name != '' and code.size == 4 and not collection.include?([name, code])
348
- collection.push([name, code])
349
- end
350
- end
351
-
352
- def _addcommand(node)
353
- name = _name(node.attributes['name'])
354
- code = _code(node.attributes['code'])
355
- parameters = []
356
- # Note: overlapping command definitions (e.g. 'path to') should be processed as follows:
357
- # - If their names and codes are the same, only the last definition is used; other definitions are ignored and will not compile.
358
- # - If their names are the same but their codes are different, only the first definition is used; other definitions are ignored and will not compile.
359
- if name != '' and code.size == 8 and (not @commands.has_key?(name) or @commands[name][1] == code)
360
- @commands[name] = [name, code, parameters]
361
- node.each_element('parameter') do |pnode|
362
- _addnamecode(pnode, parameters)
363
- end
364
- end
365
- end
366
-
367
- def parse(xml)
368
- # Extract name-code mappings from an sdef.
369
- #
370
- # xml : String -- sdef data
371
- xml = REXML::Document.new(xml)
372
- xml.root.each_element('suite/*') do |node|
373
- begin
374
- if ['command', 'event'].include?(node.name)
375
- _addcommand(node)
376
- elsif ['class', 'record-type', 'value-type'].include?(node.name)
377
- _addnamecode(node, @classes)
378
- node.each_element('property') do |prop|
379
- _addnamecode(prop, @properties)
380
- end
381
- if node.name == 'class' # elements
382
- name = node.attributes['plural']
383
- if name == nil
384
- name = node.attributes['name']
385
- name = "#{name}s" if name != nil
386
- end
387
- name = _name(name)
388
- code = _code(node.attributes['code'])
389
- if name != '' and code.size == 4
390
- @elements.push([name, code])
391
- end
392
- end
393
- elsif node.name == 'enumeration'
394
- node.each_element('enumerator') do |enum|
395
- _addnamecode(enum, @enumerators)
396
- end
397
- end
398
- rescue # ignore problem definitions
399
- end
400
- end
401
- end
402
-
403
- def parse_file(path)
404
- # Extract name-code mappings from an sdef.
405
- #
406
- # path : String -- path to .sdef file
407
- parse(AE.copy_scripting_definition(path))
408
- end
409
-
410
- end
411
-
412
- #######
413
- # cache; stores osax paths and previously parsed terminology (if any) by osax name
414
-
415
- OSAXCache = {}
416
- OSAXNames = []
417
-
418
- #######
419
- # modified AppData class
420
-
421
- class OSAXData < Appscript::AppData
422
-
423
- def initialize(constructor, identifier, terms)
424
- super(AEM::Application, constructor, identifier, terms)
425
- end
426
-
427
- def connect
428
- super
429
- begin
430
- @target.event('ascrgdut').send(60 * 60) # make sure target application has loaded event handlers for all installed OSAXen
431
- rescue AEM::EventError => e
432
- if e.number != -1708 # ignore 'event not handled' error
433
- raise
434
- end
435
- end
436
- end
437
-
438
- end
439
-
440
- @_standard_additions = nil
441
-
442
- def OSAX._init_caches
443
- se = AEM::Application.by_path(FindApp.by_id('com.apple.systemevents'))
444
- ['flds', 'fldl', 'fldu'].each do |domain_code|
445
- osaxen = AEM.app.property(domain_code).property('$scr').elements('file').by_filter(
446
- AEM.its.property('asty').eq('osax').or(AEM.its.property('extn').eq('osax')))
447
- if se.event('coredoex', {'----' => osaxen.property('pnam')}).send # domain has ScriptingAdditions folder
448
- names = se.event('coregetd', {'----' => osaxen.property('pnam')}).send
449
- paths = se.event('coregetd', {'----' => osaxen.property('posx')}).send
450
- names.zip(paths).each do |name, path|
451
- name = name.sub(/(?i)\.osax$/, '') # remove name extension, if any
452
- OSAXNames.push(name)
453
- OSAXCache[name.downcase] = [path, nil]
454
- end
455
- end
456
- end
457
- OSAXNames.sort!.uniq!
458
- end
459
-
460
- ######################################################################
461
- # PUBLIC
462
- ######################################################################
463
-
464
- def OSAX.scripting_additions
465
- # list names of all currently installed scripting additions
466
- OSAX._init_caches if OSAXNames == []
467
- return OSAXNames.clone
468
- end
469
-
470
- def OSAX.osax(name=nil, app_name=nil)
471
- # Convenience method for creating a new ScriptingAddition instance.
472
- # name : String | nil -- scripting addition's name; nil = 'StandardAdditions'
473
- # app_name : String | nil -- target application's name/path, or nil for current application
474
- # Result : ScriptingAddition
475
- #
476
- # If both arguments are nil, a ScriptingAddition object for StandardAdditions is created
477
- # and returned. This object is cached for efficiency and returned in subsequent calls;
478
- # thus clients can conveniently write (e.g):
479
- #
480
- # osax.some_command
481
- # osax.another_command
482
- #
483
- # instead of:
484
- #
485
- # sa = osax
486
- # sa.some_command
487
- # sa.another_command
488
- #
489
- # without the additional overhead of creating a new ScriptingAddition object each time.
490
- #
491
- if name == nil and app_name == nil
492
- if @_standard_additions == nil
493
- @_standard_additions = ScriptingAddition.new('StandardAdditions')
494
- end
495
- addition = @_standard_additions
496
- else
497
- if name == nil
498
- name = 'StandardAdditions'
499
- end
500
- addition = ScriptingAddition.new(name)
501
- if app_name
502
- addition = addition.by_name(app_name)
503
- end
504
- end
505
- return addition
506
- end
507
-
508
- # allow methods to be included via 'include OSAX'
509
-
510
- def scripting_additions
511
- return OSAX.scripting_additions
512
- end
513
-
514
- def osax(*args)
515
- return OSAX.osax(*args)
516
- end
517
-
518
- #######
519
-
520
- class ScriptingAddition < Appscript::Reference
521
- # Represents a single scripting addition.
522
-
523
- def initialize(name, terms=nil)
524
- # name: string -- a scripting addition's name, e.g. "StandardAdditions";
525
- # basically its filename minus the '.osax' suffix
526
- #
527
- # terms : module or nil -- an optional terminology glue module,
528
- # as exported by Terminology.dump; if given, ScriptingAddition
529
- # will use this instead of retrieving the terminology dynamically
530
- #
531
- # Note that name is case-insensitive and an '.osax' suffix is ignored if given.
532
- @_osax_name = name
533
- if not terms
534
- osax_name = name.downcase.sub(/(?i)\.osax$/, '')
535
- OSAX._init_caches if OSAXCache == {}
536
- path, terminology_tables = OSAXCache[osax_name]
537
- if not path
538
- raise ArgumentError, "Scripting addition not found: #{name.inspect}"
539
- end
540
- if not terminology_tables
541
- sp = OSAX::SdefParser.new
542
- sp.parse_file(path)
543
- if osax_name == 'standardadditions'
544
- OSAX::StandardAdditionsEnums.each { |o| sp.enumerators.push(o)}
545
- end
546
- terminology_tables = Terminology.tables_for_parsed_sdef(sp)
547
- OSAXCache[osax_name][1] = terminology_tables
548
- end
549
- @_terms = terminology_tables
550
- terms = OSAXData.new(:current, nil, @_terms)
551
- elsif not terms.is_a?(OSAX::OSAXData) # assume it's a glue module
552
- terminology_tables = Terminology.tables_for_module(terms)
553
- @_terms = terminology_tables
554
- terms = OSAXData.new(:current, nil, @_terms)
555
- end
556
- super(terms, AEM.app)
557
- end
558
-
559
- def to_s
560
- return "#<OSAX::ScriptingAddition name=#{@_osax_name.inspect} target=#{@AS_app_data.target.inspect}>"
561
- end
562
-
563
- alias_method :inspect, :to_s
564
-
565
- ##
566
-
567
- def method_missing(name, *args)
568
- begin
569
- super
570
- rescue Appscript::CommandError => e
571
- if e.to_i == -1713 # 'No user interaction allowed' error (e.g. user tried to send a 'display dialog' command to a non-GUI ruby process), so convert the target process to a full GUI process and try again
572
- AE.transform_process_to_foreground_application
573
- activate
574
- super
575
- else
576
- raise
577
- end
578
- end
579
- end
580
-
581
- # A client-created scripting addition is automatically targetted at the current application.
582
- # Clients can specify another application as target by calling one of the following methods:
583
-
584
- def by_name(name)
585
- # name : string -- name or full path to application
586
- return ScriptingAddition.new(@_osax_name,
587
- OSAXData.new(:by_path, FindApp.by_name(name), @_terms))
588
- end
589
-
590
- def by_id(id)
591
- # id : string -- bundle id of application
592
- return ScriptingAddition.new(@_osax_name,
593
- OSAXData.new(:by_path, FindApp.by_id(id), @_terms))
594
- end
595
-
596
- def by_creator(creator)
597
- # creator : string -- four-character creator code of application
598
- return ScriptingAddition.new(@_osax_name,
599
- OSAXData.new(:by_path, FindApp.by_creator(creator), @_terms))
600
- end
601
-
602
- def by_pid(pid)
603
- # pid : integer -- Unix process id
604
- return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_pid, pid, @_terms))
605
- end
606
-
607
- def by_url(url)
608
- # url : string -- eppc URL of application
609
- return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_url, url, @_terms))
610
- end
611
-
612
- def by_aem_app(aem_app)
613
- # aem_app : AEM::Application -- an AEM::Application instance
614
- return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_aem_app, aem_app, @_terms))
615
- end
616
-
617
- def current
618
- return ScriptingAddition.new(@_osax_name, OSAXData.new(:current, nil, @_terms))
619
- end
620
- end
621
-
622
- #######
623
-
624
- def OSAX.dump(osax_name, module_name, out_path)
625
- # Export scripting addition terminology tables as a Ruby module
626
- # osaxname : string -- name of installed scripting addition
627
- # module_name : string -- name of generated module (must be a valid Ruby constant)
628
- # out_path : string -- module file to write
629
- #
630
- # Generates a Ruby module containing a scripting addition's basic terminology
631
- # (names and codes).
632
- #
633
- # Call the #dump method to dump faulty sdefs to Ruby module, e.g.:
634
- #
635
- # dump('MyOSAX', 'MyOSAXGlue', '/path/to/site-packages/myosaxglue.py')
636
- #
637
- # Patch any errors by hand, then import the patched module into your script
638
- # and pass it to OSAX.osax via its 'terms' argument, e.g.:
639
- #
640
- # require 'osax'
641
- # require 'MyOSAXGlue'
642
- #
643
- # myapp = OSAX.osax('MyOSAX', terms => MyOSAXGlue)
644
- #
645
- OSAX._init_caches if OSAXNames == []
646
- original_name = osax_name
647
- osax_name = osax_name.downcase
648
- m = /^(.+).osax$/.match(osax_name)
649
- osax_name = m[1] if m != nil
650
- osax_path, terms = OSAXCache.fetch(osax_name) do
651
- raise ArgumentError, "Scripting addition not found: #{original_name.inspect}"
652
- end
653
- sp = OSAX::SdefParser.new
654
- sp.parsefile(osax_path)
655
- Terminology.dump_tables([sp.classes, sp.enumerators, sp.properties, sp.elements, sp.commands],
656
- module_name, osax_path, out_path)
657
- end
11
+
12
+ ######################################################################
13
+ # PRIVATE
14
+ ######################################################################
15
+
16
+ require 'rexml/document'
17
+ require "ae"
18
+ require "kae"
19
+ require "aem"
20
+ require "_appscript/reservedkeywords" # names of all existing methods on ASReference::Application
21
+
22
+ StandardAdditionsEnums = [
23
+ ["stop", "\000\000\000\000"],
24
+ ["note", "\000\000\000\001"],
25
+ ["caution", "\000\000\000\002"]]
26
+
27
+ UTF8ToMacRoman = {
28
+ 0 => 0,
29
+ 1 => 1,
30
+ 2 => 2,
31
+ 3 => 3,
32
+ 4 => 4,
33
+ 5 => 5,
34
+ 6 => 6,
35
+ 7 => 7,
36
+ 8 => 8,
37
+ 9 => 9,
38
+ 10 => 10,
39
+ 11 => 11,
40
+ 12 => 12,
41
+ 13 => 13,
42
+ 14 => 14,
43
+ 15 => 15,
44
+ 16 => 16,
45
+ 17 => 17,
46
+ 18 => 18,
47
+ 19 => 19,
48
+ 20 => 20,
49
+ 21 => 21,
50
+ 22 => 22,
51
+ 23 => 23,
52
+ 24 => 24,
53
+ 25 => 25,
54
+ 26 => 26,
55
+ 27 => 27,
56
+ 28 => 28,
57
+ 29 => 29,
58
+ 30 => 30,
59
+ 31 => 31,
60
+ 32 => 32,
61
+ 33 => 33,
62
+ 34 => 34,
63
+ 35 => 35,
64
+ 36 => 36,
65
+ 37 => 37,
66
+ 38 => 38,
67
+ 39 => 39,
68
+ 40 => 40,
69
+ 41 => 41,
70
+ 42 => 42,
71
+ 43 => 43,
72
+ 44 => 44,
73
+ 45 => 45,
74
+ 46 => 46,
75
+ 47 => 47,
76
+ 48 => 48,
77
+ 49 => 49,
78
+ 50 => 50,
79
+ 51 => 51,
80
+ 52 => 52,
81
+ 53 => 53,
82
+ 54 => 54,
83
+ 55 => 55,
84
+ 56 => 56,
85
+ 57 => 57,
86
+ 58 => 58,
87
+ 59 => 59,
88
+ 60 => 60,
89
+ 61 => 61,
90
+ 62 => 62,
91
+ 63 => 63,
92
+ 64 => 64,
93
+ 65 => 65,
94
+ 66 => 66,
95
+ 67 => 67,
96
+ 68 => 68,
97
+ 69 => 69,
98
+ 70 => 70,
99
+ 71 => 71,
100
+ 72 => 72,
101
+ 73 => 73,
102
+ 74 => 74,
103
+ 75 => 75,
104
+ 76 => 76,
105
+ 77 => 77,
106
+ 78 => 78,
107
+ 79 => 79,
108
+ 80 => 80,
109
+ 81 => 81,
110
+ 82 => 82,
111
+ 83 => 83,
112
+ 84 => 84,
113
+ 85 => 85,
114
+ 86 => 86,
115
+ 87 => 87,
116
+ 88 => 88,
117
+ 89 => 89,
118
+ 90 => 90,
119
+ 91 => 91,
120
+ 92 => 92,
121
+ 93 => 93,
122
+ 94 => 94,
123
+ 95 => 95,
124
+ 96 => 96,
125
+ 97 => 97,
126
+ 98 => 98,
127
+ 99 => 99,
128
+ 100 => 100,
129
+ 101 => 101,
130
+ 102 => 102,
131
+ 103 => 103,
132
+ 104 => 104,
133
+ 105 => 105,
134
+ 106 => 106,
135
+ 107 => 107,
136
+ 108 => 108,
137
+ 109 => 109,
138
+ 110 => 110,
139
+ 111 => 111,
140
+ 112 => 112,
141
+ 113 => 113,
142
+ 114 => 114,
143
+ 115 => 115,
144
+ 116 => 116,
145
+ 117 => 117,
146
+ 118 => 118,
147
+ 119 => 119,
148
+ 120 => 120,
149
+ 121 => 121,
150
+ 122 => 122,
151
+ 123 => 123,
152
+ 124 => 124,
153
+ 125 => 125,
154
+ 126 => 126,
155
+ 127 => 127,
156
+ 196 => 128,
157
+ 197 => 129,
158
+ 199 => 130,
159
+ 201 => 131,
160
+ 209 => 132,
161
+ 214 => 133,
162
+ 220 => 134,
163
+ 225 => 135,
164
+ 224 => 136,
165
+ 226 => 137,
166
+ 228 => 138,
167
+ 227 => 139,
168
+ 229 => 140,
169
+ 231 => 141,
170
+ 233 => 142,
171
+ 232 => 143,
172
+ 234 => 144,
173
+ 235 => 145,
174
+ 237 => 146,
175
+ 236 => 147,
176
+ 238 => 148,
177
+ 239 => 149,
178
+ 241 => 150,
179
+ 243 => 151,
180
+ 242 => 152,
181
+ 244 => 153,
182
+ 246 => 154,
183
+ 245 => 155,
184
+ 250 => 156,
185
+ 249 => 157,
186
+ 251 => 158,
187
+ 252 => 159,
188
+ 8224 => 160,
189
+ 176 => 161,
190
+ 162 => 162,
191
+ 163 => 163,
192
+ 167 => 164,
193
+ 8226 => 165,
194
+ 182 => 166,
195
+ 223 => 167,
196
+ 174 => 168,
197
+ 169 => 169,
198
+ 8482 => 170,
199
+ 180 => 171,
200
+ 168 => 172,
201
+ 8800 => 173,
202
+ 198 => 174,
203
+ 216 => 175,
204
+ 8734 => 176,
205
+ 177 => 177,
206
+ 8804 => 178,
207
+ 8805 => 179,
208
+ 165 => 180,
209
+ 181 => 181,
210
+ 8706 => 182,
211
+ 8721 => 183,
212
+ 8719 => 184,
213
+ 960 => 185,
214
+ 8747 => 186,
215
+ 170 => 187,
216
+ 186 => 188,
217
+ 937 => 189,
218
+ 230 => 190,
219
+ 248 => 191,
220
+ 191 => 192,
221
+ 161 => 193,
222
+ 172 => 194,
223
+ 8730 => 195,
224
+ 402 => 196,
225
+ 8776 => 197,
226
+ 8710 => 198,
227
+ 171 => 199,
228
+ 187 => 200,
229
+ 8230 => 201,
230
+ 160 => 202,
231
+ 192 => 203,
232
+ 195 => 204,
233
+ 213 => 205,
234
+ 338 => 206,
235
+ 339 => 207,
236
+ 8211 => 208,
237
+ 8212 => 209,
238
+ 8220 => 210,
239
+ 8221 => 211,
240
+ 8216 => 212,
241
+ 8217 => 213,
242
+ 247 => 214,
243
+ 9674 => 215,
244
+ 255 => 216,
245
+ 376 => 217,
246
+ 8260 => 218,
247
+ 8364 => 219,
248
+ 8249 => 220,
249
+ 8250 => 221,
250
+ 64257 => 222,
251
+ 64258 => 223,
252
+ 8225 => 224,
253
+ 183 => 225,
254
+ 8218 => 226,
255
+ 8222 => 227,
256
+ 8240 => 228,
257
+ 194 => 229,
258
+ 202 => 230,
259
+ 193 => 231,
260
+ 203 => 232,
261
+ 200 => 233,
262
+ 205 => 234,
263
+ 206 => 235,
264
+ 207 => 236,
265
+ 204 => 237,
266
+ 211 => 238,
267
+ 212 => 239,
268
+ 63743 => 240,
269
+ 210 => 241,
270
+ 218 => 242,
271
+ 219 => 243,
272
+ 217 => 244,
273
+ 305 => 245,
274
+ 710 => 246,
275
+ 732 => 247,
276
+ 175 => 248,
277
+ 728 => 249,
278
+ 729 => 250,
279
+ 730 => 251,
280
+ 184 => 252,
281
+ 733 => 253,
282
+ 731 => 254,
283
+ 711 => 255}
284
+
285
+ class SdefParser
286
+ @@_name_cache = {}
287
+ LegalFirst = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
288
+ LegalRest = LegalFirst + '0123456789'
289
+ @@_reserved_keywords = {} # ersatz set
290
+ ReservedKeywords.each { |name| @@_reserved_keywords[name] = nil }
291
+
292
+ attr_reader :properties, :elements, :classes, :enumerators
293
+
294
+ def commands
295
+ return @commands.values
296
+ end
297
+
298
+ def initialize
299
+ # terminology tables; order is significant where synonym definitions occur
300
+ @commands = {}
301
+ @properties = []
302
+ @elements = []
303
+ @classes = []
304
+ @enumerators = []
305
+ end
306
+
307
+ def _name(s)
308
+ # Read a MacRoman-encoded Pascal keyword string.
309
+ if not @@_name_cache.has_key?(s)
310
+ legal = LegalFirst
311
+ res = ''
312
+ s.split(//).each do |c|
313
+ if legal[c]
314
+ res += c
315
+ else
316
+ case c
317
+ when ' ', '-', '/'
318
+ res += '_'
319
+ when '&'
320
+ res += 'and'
321
+ else
322
+ if res == ''
323
+ res = '_'
324
+ end
325
+ res += "0x#{c.unpack('HXh')}"
326
+ end
327
+ end
328
+ legal = LegalRest
329
+ end
330
+ if res[0, 3] == 'AS_' or @@_reserved_keywords.has_key?(res) or res[0, 1] == '_'
331
+ res += '_'
332
+ end
333
+ @@_name_cache[s] = res
334
+ end
335
+ return @@_name_cache[s]
336
+ end
337
+
338
+ def _code(s)
339
+ return s.unpack('U*').collect do |c| # unpack UTF8-encoded byte string
340
+ OSAX::UTF8ToMacRoman.fetch(c)
341
+ end .pack('C*') # pack as MacRoman-encoded byte string (four- or eight-char code)
342
+ end
343
+
344
+ def _addnamecode(node, collection)
345
+ name = _name(node.attributes['name'])
346
+ code = _code(node.attributes['code'])
347
+ if name != '' and code.size == 4 and not collection.include?([name, code])
348
+ collection.push([name, code])
349
+ end
350
+ end
351
+
352
+ def _addcommand(node)
353
+ name = _name(node.attributes['name'])
354
+ code = _code(node.attributes['code'])
355
+ parameters = []
356
+ # Note: overlapping command definitions (e.g. 'path to') should be processed as follows:
357
+ # - If their names and codes are the same, only the last definition is used; other definitions are ignored and will not compile.
358
+ # - If their names are the same but their codes are different, only the first definition is used; other definitions are ignored and will not compile.
359
+ if name != '' and code.size == 8 and (not @commands.has_key?(name) or @commands[name][1] == code)
360
+ @commands[name] = [name, code, parameters]
361
+ node.each_element('parameter') do |pnode|
362
+ _addnamecode(pnode, parameters)
363
+ end
364
+ end
365
+ end
366
+
367
+ def parse(xml)
368
+ # Extract name-code mappings from an sdef.
369
+ #
370
+ # xml : String -- sdef data
371
+ xml = REXML::Document.new(xml)
372
+ xml.root.each_element('suite/*') do |node|
373
+ begin
374
+ if ['command', 'event'].include?(node.name)
375
+ _addcommand(node)
376
+ elsif ['class', 'record-type', 'value-type'].include?(node.name)
377
+ _addnamecode(node, @classes)
378
+ node.each_element('property') do |prop|
379
+ _addnamecode(prop, @properties)
380
+ end
381
+ if node.name == 'class' # elements
382
+ name = node.attributes['plural']
383
+ if name == nil
384
+ name = node.attributes['name']
385
+ name = "#{name}s" if name != nil
386
+ end
387
+ name = _name(name)
388
+ code = _code(node.attributes['code'])
389
+ if name != '' and code.size == 4
390
+ @elements.push([name, code])
391
+ end
392
+ end
393
+ elsif node.name == 'enumeration'
394
+ node.each_element('enumerator') do |enum|
395
+ _addnamecode(enum, @enumerators)
396
+ end
397
+ end
398
+ rescue # ignore problem definitions
399
+ end
400
+ end
401
+ end
402
+
403
+ def parse_file(path)
404
+ # Extract name-code mappings from an sdef.
405
+ #
406
+ # path : String -- path to .sdef file
407
+ parse(AE.copy_scripting_definition(path))
408
+ end
409
+
410
+ end
411
+
412
+ #######
413
+ # cache; stores osax paths and previously parsed terminology (if any) by osax name
414
+
415
+ OSAXCache = {}
416
+ OSAXNames = []
417
+
418
+ #######
419
+ # modified AppData class
420
+
421
+ class OSAXData < Appscript::AppData
422
+
423
+ def initialize(constructor, identifier, terms)
424
+ super(AEM::Application, constructor, identifier, terms)
425
+ end
426
+
427
+ def connect
428
+ super
429
+ begin
430
+ @target.event('ascrgdut').send(60 * 60) # make sure target application has loaded event handlers for all installed OSAXen
431
+ rescue AEM::EventError => e
432
+ if e.number != -1708 # ignore 'event not handled' error
433
+ raise
434
+ end
435
+ end
436
+ end
437
+
438
+ end
439
+
440
+ @_standard_additions = nil
441
+
442
+ def OSAX._init_caches
443
+ se = AEM::Application.by_path(FindApp.by_id('com.apple.systemevents'))
444
+ ['flds', 'fldl', 'fldu'].each do |domain_code|
445
+ osaxen = AEM.app.property(domain_code).property('$scr').elements('file').by_filter(
446
+ AEM.its.property('asty').eq('osax').or(AEM.its.property('extn').eq('osax')))
447
+ if se.event('coredoex', {'----' => osaxen.property('pnam')}).send # domain has ScriptingAdditions folder
448
+ names = se.event('coregetd', {'----' => osaxen.property('pnam')}).send
449
+ paths = se.event('coregetd', {'----' => osaxen.property('posx')}).send
450
+ names.zip(paths).each do |name, path|
451
+ name = name.sub(/(?i)\.osax$/, '') # remove name extension, if any
452
+ OSAXNames.push(name)
453
+ OSAXCache[name.downcase] = [path, nil]
454
+ end
455
+ end
456
+ end
457
+ OSAXNames.sort!.uniq!
458
+ end
459
+
460
+ ######################################################################
461
+ # PUBLIC
462
+ ######################################################################
463
+
464
+ def OSAX.scripting_additions
465
+ # list names of all currently installed scripting additions
466
+ OSAX._init_caches if OSAXNames == []
467
+ return OSAXNames.clone
468
+ end
469
+
470
+ def OSAX.osax(name=nil, app_name=nil)
471
+ # Convenience method for creating a new ScriptingAddition instance.
472
+ # name : String | nil -- scripting addition's name; nil = 'StandardAdditions'
473
+ # app_name : String | nil -- target application's name/path, or nil for current application
474
+ # Result : ScriptingAddition
475
+ #
476
+ # If both arguments are nil, a ScriptingAddition object for StandardAdditions is created
477
+ # and returned. This object is cached for efficiency and returned in subsequent calls;
478
+ # thus clients can conveniently write (e.g):
479
+ #
480
+ # osax.some_command
481
+ # osax.another_command
482
+ #
483
+ # instead of:
484
+ #
485
+ # sa = osax
486
+ # sa.some_command
487
+ # sa.another_command
488
+ #
489
+ # without the additional overhead of creating a new ScriptingAddition object each time.
490
+ #
491
+ if name == nil and app_name == nil
492
+ if @_standard_additions == nil
493
+ @_standard_additions = ScriptingAddition.new('StandardAdditions')
494
+ end
495
+ addition = @_standard_additions
496
+ else
497
+ if name == nil
498
+ name = 'StandardAdditions'
499
+ end
500
+ addition = ScriptingAddition.new(name)
501
+ if app_name
502
+ addition = addition.by_name(app_name)
503
+ end
504
+ end
505
+ return addition
506
+ end
507
+
508
+ # allow methods to be included via 'include OSAX'
509
+
510
+ def scripting_additions
511
+ return OSAX.scripting_additions
512
+ end
513
+
514
+ def osax(*args)
515
+ return OSAX.osax(*args)
516
+ end
517
+
518
+ #######
519
+
520
+ class ScriptingAddition < Appscript::Reference
521
+ # Represents a single scripting addition.
522
+
523
+ def initialize(name, terms=nil)
524
+ # name: string -- a scripting addition's name, e.g. "StandardAdditions";
525
+ # basically its filename minus the '.osax' suffix
526
+ #
527
+ # terms : module or nil -- an optional terminology glue module,
528
+ # as exported by Terminology.dump; if given, ScriptingAddition
529
+ # will use this instead of retrieving the terminology dynamically
530
+ #
531
+ # Note that name is case-insensitive and an '.osax' suffix is ignored if given.
532
+ @_osax_name = name
533
+ if not terms
534
+ osax_name = name.downcase.sub(/(?i)\.osax$/, '')
535
+ OSAX._init_caches if OSAXCache == {}
536
+ path, terminology_tables = OSAXCache[osax_name]
537
+ if not path
538
+ raise ArgumentError, "Scripting addition not found: #{name.inspect}"
539
+ end
540
+ if not terminology_tables
541
+ sp = OSAX::SdefParser.new
542
+ sp.parse_file(path)
543
+ if osax_name == 'standardadditions'
544
+ OSAX::StandardAdditionsEnums.each { |o| sp.enumerators.push(o)}
545
+ end
546
+ terminology_tables = Terminology.tables_for_parsed_sdef(sp)
547
+ OSAXCache[osax_name][1] = terminology_tables
548
+ end
549
+ @_terms = terminology_tables
550
+ terms = OSAXData.new(:current, nil, @_terms)
551
+ elsif not terms.is_a?(OSAX::OSAXData) # assume it's a glue module
552
+ terminology_tables = Terminology.tables_for_module(terms)
553
+ @_terms = terminology_tables
554
+ terms = OSAXData.new(:current, nil, @_terms)
555
+ end
556
+ super(terms, AEM.app)
557
+ end
558
+
559
+ def to_s
560
+ return "#<OSAX::ScriptingAddition name=#{@_osax_name.inspect} target=#{@AS_app_data.target.inspect}>"
561
+ end
562
+
563
+ alias_method :inspect, :to_s
564
+
565
+ ##
566
+
567
+ def method_missing(name, *args)
568
+ begin
569
+ super
570
+ rescue Appscript::CommandError => e
571
+ if e.to_i == -1713 # 'No user interaction allowed' error (e.g. user tried to send a 'display dialog' command to a non-GUI ruby process), so convert the target process to a full GUI process and try again
572
+ AE.transform_process_to_foreground_application
573
+ activate
574
+ super
575
+ else
576
+ raise
577
+ end
578
+ end
579
+ end
580
+
581
+ # A client-created scripting addition is automatically targetted at the current application.
582
+ # Clients can specify another application as target by calling one of the following methods:
583
+
584
+ def by_name(name)
585
+ # name : string -- name or full path to application
586
+ return ScriptingAddition.new(@_osax_name,
587
+ OSAXData.new(:by_path, FindApp.by_name(name), @_terms))
588
+ end
589
+
590
+ def by_id(id)
591
+ # id : string -- bundle id of application
592
+ return ScriptingAddition.new(@_osax_name,
593
+ OSAXData.new(:by_path, FindApp.by_id(id), @_terms))
594
+ end
595
+
596
+ def by_creator(creator)
597
+ # creator : string -- four-character creator code of application
598
+ return ScriptingAddition.new(@_osax_name,
599
+ OSAXData.new(:by_path, FindApp.by_creator(creator), @_terms))
600
+ end
601
+
602
+ def by_pid(pid)
603
+ # pid : integer -- Unix process id
604
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_pid, pid, @_terms))
605
+ end
606
+
607
+ def by_url(url)
608
+ # url : string -- eppc URL of application
609
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_url, url, @_terms))
610
+ end
611
+
612
+ def by_aem_app(aem_app)
613
+ # aem_app : AEM::Application -- an AEM::Application instance
614
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_aem_app, aem_app, @_terms))
615
+ end
616
+
617
+ def current
618
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:current, nil, @_terms))
619
+ end
620
+ end
621
+
622
+ #######
623
+
624
+ def OSAX.dump(osax_name, module_name, out_path)
625
+ # Export scripting addition terminology tables as a Ruby module
626
+ # osaxname : string -- name of installed scripting addition
627
+ # module_name : string -- name of generated module (must be a valid Ruby constant)
628
+ # out_path : string -- module file to write
629
+ #
630
+ # Generates a Ruby module containing a scripting addition's basic terminology
631
+ # (names and codes).
632
+ #
633
+ # Call the #dump method to dump faulty sdefs to Ruby module, e.g.:
634
+ #
635
+ # dump('MyOSAX', 'MyOSAXGlue', '/path/to/site-packages/myosaxglue.py')
636
+ #
637
+ # Patch any errors by hand, then import the patched module into your script
638
+ # and pass it to OSAX.osax via its 'terms' argument, e.g.:
639
+ #
640
+ # require 'osax'
641
+ # require 'MyOSAXGlue'
642
+ #
643
+ # myapp = OSAX.osax('MyOSAX', terms => MyOSAXGlue)
644
+ #
645
+ OSAX._init_caches if OSAXNames == []
646
+ original_name = osax_name
647
+ osax_name = osax_name.downcase
648
+ m = /^(.+).osax$/.match(osax_name)
649
+ osax_name = m[1] if m != nil
650
+ osax_path, terms = OSAXCache.fetch(osax_name) do
651
+ raise ArgumentError, "Scripting addition not found: #{original_name.inspect}"
652
+ end
653
+ sp = OSAX::SdefParser.new
654
+ sp.parsefile(osax_path)
655
+ Terminology.dump_tables([sp.classes, sp.enumerators, sp.properties, sp.elements, sp.commands],
656
+ module_name, osax_path, out_path)
657
+ end
658
658
 
659
659
  end