puer 0.0.1 → 0.0.2
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/VERSION +1 -1
- data/bin/puer +58 -0
- data/lib/puer.rb +6 -1
- data/lib/puer/config.rb +24 -0
- data/lib/puer/converters.rb +36 -0
- data/lib/puer/nodes.rb +56 -0
- data/lib/puer/session.rb +129 -0
- data/lib/puer/xibtoti.rb +33 -0
- metadata +38 -33
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/bin/puer
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
|
+
require 'rubygems'
|
4
5
|
require 'rubygems' unless defined?(Gem)
|
5
6
|
require 'thread'
|
6
7
|
|
@@ -14,3 +15,60 @@ unless $LOAD_PATH.include?(lib_dir)
|
|
14
15
|
end
|
15
16
|
|
16
17
|
require 'puer'
|
18
|
+
require 'optparse'
|
19
|
+
|
20
|
+
OptionParser.new do |opts|
|
21
|
+
opts.banner =<<END
|
22
|
+
Usage: puer [options] [filename]
|
23
|
+
Usage: puer all # convert all xib files to js under current directory
|
24
|
+
END
|
25
|
+
|
26
|
+
opts.on("-w", "--[no-]warnings", "Show warnings") do |w|
|
27
|
+
@show_warnings = w
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on("-o", "--output-file name", "Specify output file") do |o|
|
31
|
+
@output_file = o
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-c", "--config-file name", "Specify config file") do |o|
|
35
|
+
@config_file = o
|
36
|
+
end
|
37
|
+
|
38
|
+
end.parse!
|
39
|
+
|
40
|
+
if ARGV.size == 1
|
41
|
+
|
42
|
+
case ARGV[0]
|
43
|
+
when "all"
|
44
|
+
Dir.glob(File.join('**','*.xib')).each do |s|
|
45
|
+
puts "#{s} is converted to #{File.basename(s, '.*')}.js "
|
46
|
+
system "puer #{s} -o #{File.basename(s, '.*')}.js"
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
else
|
50
|
+
input_file = ARGV.first
|
51
|
+
|
52
|
+
session = Session.new @config_file || File.join("#{File.dirname(__FILE__)}/../lib/puer", 'config.rb')
|
53
|
+
session.parse_file input_file
|
54
|
+
if session.has_errors?
|
55
|
+
puts "Aborted!"
|
56
|
+
puts session.full_log [:error]
|
57
|
+
else
|
58
|
+
severities = []
|
59
|
+
severities.unshift :warning if @show_warnings
|
60
|
+
log = session.full_log severities
|
61
|
+
script = js_comments_for(log) + js_for(session.out)
|
62
|
+
if @output_file
|
63
|
+
File.open(@output_file, 'w') do |file|
|
64
|
+
file.write script
|
65
|
+
end
|
66
|
+
puts log
|
67
|
+
else
|
68
|
+
puts script
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else
|
73
|
+
puts "For help, type: puer -h"
|
74
|
+
end
|
data/lib/puer.rb
CHANGED
data/lib/puer/config.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
ignore_properties 'contentStretch', 'simulatedStatusBarMetrics', 'simulatedOrientationMetrics'
|
2
|
+
ignore_classes 'IBProxyObject'
|
3
|
+
|
4
|
+
# To get another creation call than standard
|
5
|
+
# Ti.UI.create#{name}
|
6
|
+
# give a array as value: 'IBUIWindow' => ['Window', 'myWindowCall']
|
7
|
+
classes 'IBUIWindow' => 'Window',
|
8
|
+
'IBUIView' => 'View',
|
9
|
+
'IBUILabel' => 'Label',
|
10
|
+
'IBUIButton' => 'Button'
|
11
|
+
|
12
|
+
# Available types:
|
13
|
+
# val(:output)
|
14
|
+
# bool(:output) # Where '0' gives {:output => false} and '1' gives {:output => true}
|
15
|
+
# lookup(:output, {'yes' => true, 'no' => false})
|
16
|
+
# color(:output)
|
17
|
+
# font(:output)
|
18
|
+
# vextor(:x, :y) # Where '{1, 2}' => {:x => 1, :y => 2}
|
19
|
+
properties 'backgroundColor' => color(:backgroundColor),
|
20
|
+
'font' => font(:font),
|
21
|
+
'frameOrigin' => vector(:top, :bottom),
|
22
|
+
'frameSize' => vector(:height, :width),
|
23
|
+
'text' => val(:text),
|
24
|
+
'textColor' => color(:color)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Converters converts data in the xib files property bag to the NodeInfos property bag.
|
2
|
+
# The output key it will have is stored here. The converter can be created with a conversion block
|
3
|
+
# Converter.new :output {|v| Convert value v here...}
|
4
|
+
|
5
|
+
class Converter
|
6
|
+
def initialize(output, &conversion)
|
7
|
+
@output = output
|
8
|
+
@conversion = conversion || proc {|v| v}
|
9
|
+
end
|
10
|
+
|
11
|
+
def props_for(value)
|
12
|
+
converted_value = @conversion.call(value)
|
13
|
+
if converted_value
|
14
|
+
{ @output => converted_value }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# MultiConverter is used for cases when a property in the xhib file's property bag needs multiple
|
20
|
+
# properties in JS
|
21
|
+
# Example: {'frameOrigin' => "{123, 456}"} should give {:top => 123, :bottom => 456}
|
22
|
+
# Done by: MultiConverter.new([:top, :bottom], /\{(\d+), (\d+)\}/) {|v| v.to_i}
|
23
|
+
|
24
|
+
class MultiConverter < Converter
|
25
|
+
def initialize(outputs, regex, &conversion)
|
26
|
+
@outputs = outputs
|
27
|
+
@regex = regex
|
28
|
+
@conversion = conversion || proc {|v| v}
|
29
|
+
end
|
30
|
+
|
31
|
+
def props_for(value)
|
32
|
+
if match = @regex.match(value)
|
33
|
+
Hash[@outputs.zip(match.captures.map(&@conversion))]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/puer/nodes.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# NodeInfo contains the information from the xib, both hierarchy and properties
|
2
|
+
|
3
|
+
class NodeInfo
|
4
|
+
def initialize(name, node_id, node_class, subviews)
|
5
|
+
@name = name
|
6
|
+
@node_id = node_id
|
7
|
+
@node_class = node_class
|
8
|
+
@subviews = subviews
|
9
|
+
@properties = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :properties, :name, :node_class, :subviews
|
13
|
+
|
14
|
+
def self.enumerate(name)
|
15
|
+
"#{name}#{(@@name_counters ||= Hash.new {0})[name]+=1}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.for(hierarchy, data, session)
|
19
|
+
id = hierarchy['object-id'].to_s
|
20
|
+
info = data[id]
|
21
|
+
node_class = session.class_info_for info['class']
|
22
|
+
if node_class
|
23
|
+
name = hierarchy['name'] || enumerate(node_class.name.downcase)
|
24
|
+
subviews = (hierarchy['children'] || []).map {|child_hierarchy| NodeInfo.for(child_hierarchy, data, session)}.compact
|
25
|
+
node = NodeInfo.new name, id, node_class, subviews
|
26
|
+
info.each do |prop, value|
|
27
|
+
if converter = session.converter_for(prop)
|
28
|
+
props = converter.props_for(value)
|
29
|
+
if props
|
30
|
+
node.properties.merge! props
|
31
|
+
else
|
32
|
+
session.log(:error, "Could not convert #{prop}: #{value}")
|
33
|
+
end
|
34
|
+
else
|
35
|
+
session.log(:warning, "Skipped property for #{info['class']}: #{prop}") unless session.ignore_property? prop
|
36
|
+
end
|
37
|
+
end
|
38
|
+
node
|
39
|
+
else
|
40
|
+
session.log(:warning, "Skipped class #{info['class']}") unless session.ignore_class? info['class']
|
41
|
+
end
|
42
|
+
node
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# ClassInfo contains all known information on the class of the node info (Windows, Views... etc)
|
47
|
+
|
48
|
+
class ClassInfo
|
49
|
+
def initialize(name, creation_call="Ti.UI.create#{name}")
|
50
|
+
@name = name
|
51
|
+
@creation_call = creation_call
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_reader :name, :creation_call
|
55
|
+
end
|
56
|
+
|
data/lib/puer/session.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
require 'nodes'
|
3
|
+
require 'converters'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'plist'
|
6
|
+
|
7
|
+
# Stores all configurable info on how to translate the xib to JS
|
8
|
+
# The session is created from a config file.
|
9
|
+
|
10
|
+
class Session
|
11
|
+
def initialize(path)
|
12
|
+
@ignore_properties = []
|
13
|
+
@ignore_classes = []
|
14
|
+
@classes = {}
|
15
|
+
@properties = {}
|
16
|
+
@log = []
|
17
|
+
File.open path do |file|
|
18
|
+
eval file.read
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :out
|
23
|
+
|
24
|
+
def parse_file(file)
|
25
|
+
data = Plist::parse_xml( %x[ibtool #{file} --hierarchy --objects --connections] )
|
26
|
+
@out = data['com.apple.ibtool.document.hierarchy'].map {|hierarchy| NodeInfo.for hierarchy, data['com.apple.ibtool.document.objects'], self}.compact
|
27
|
+
data['com.apple.ibtool.document.connections'].each do |connection|
|
28
|
+
# TODO
|
29
|
+
end
|
30
|
+
@out
|
31
|
+
end
|
32
|
+
|
33
|
+
def full_log(severities=[])
|
34
|
+
excluded = @log.map(&:first).uniq - severities
|
35
|
+
(excluded.empty? ? '' : "There were log entries of severity: #{excluded.join ', '}\n") +
|
36
|
+
@log.map do |severity, message|
|
37
|
+
"[#{severity}] #{message}" if severities.include? severity
|
38
|
+
end.compact.join("\n")
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_errors?
|
42
|
+
@log.any? {|severity, message| severity == :error}
|
43
|
+
end
|
44
|
+
|
45
|
+
def log(severity, message)
|
46
|
+
@log.push [severity, message]
|
47
|
+
end
|
48
|
+
|
49
|
+
def ignore_class?(class_name)
|
50
|
+
@ignore_classes.include? class_name
|
51
|
+
end
|
52
|
+
|
53
|
+
def ignore_property?(property_name)
|
54
|
+
@ignore_properties.include? property_name
|
55
|
+
end
|
56
|
+
|
57
|
+
def converter_for(property_name)
|
58
|
+
@properties[property_name]
|
59
|
+
end
|
60
|
+
|
61
|
+
def class_info_for(class_name)
|
62
|
+
@classes[class_name]
|
63
|
+
end
|
64
|
+
|
65
|
+
def classes(class_hash)
|
66
|
+
class_hash.each do |key, value|
|
67
|
+
@classes[key] = ClassInfo.new(*value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def ignore_properties(*names)
|
72
|
+
@ignore_properties += names
|
73
|
+
end
|
74
|
+
|
75
|
+
def ignore_classes(*names)
|
76
|
+
@ignore_classes += names
|
77
|
+
end
|
78
|
+
|
79
|
+
def properties(properties_hash)
|
80
|
+
@properties.merge! properties_hash
|
81
|
+
end
|
82
|
+
|
83
|
+
# Color helper methods
|
84
|
+
|
85
|
+
def hex(v)
|
86
|
+
"%02x" % (v.to_f*255).round
|
87
|
+
end
|
88
|
+
|
89
|
+
def rgba(r, g, b, a)
|
90
|
+
"##{hex(r)}#{hex(g)}#{hex(b)}#{hex(a)}"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Methods for converter creation in config file
|
94
|
+
|
95
|
+
def val(name)
|
96
|
+
Converter.new(name)
|
97
|
+
end
|
98
|
+
|
99
|
+
def bool(name)
|
100
|
+
Converter.new(name) {|v| v==1}
|
101
|
+
end
|
102
|
+
|
103
|
+
def lookup(name, hash)
|
104
|
+
Converter.new(name) {|v| hash[v]}
|
105
|
+
end
|
106
|
+
|
107
|
+
def color(name)
|
108
|
+
Converter.new(name) do |v|
|
109
|
+
case v
|
110
|
+
when /NS(?:Calibrated|Device)RGBColorSpace (\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)/
|
111
|
+
rgba($1, $2, $3, $4)
|
112
|
+
when /NS(?:Calibrated|Device)WhiteColorSpace (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)/
|
113
|
+
rgba($1, $1, $1, $2)
|
114
|
+
when /NSCustomColorSpace Generic Gray colorspace (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)/
|
115
|
+
rgba($1, $1, $1, $2)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def font(name)
|
121
|
+
Converter.new(name) do |v|
|
122
|
+
{:fontFamily => v['Family'], :fontSize => v['Size']}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def vector(x, y)
|
127
|
+
MultiConverter.new([x, y], /\{(\d+), (\d+)\}/) {|v| v.to_i}
|
128
|
+
end
|
129
|
+
end
|
data/lib/puer/xibtoti.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
require 'session'
|
3
|
+
|
4
|
+
def inline_js_for(data)
|
5
|
+
|
6
|
+
case data
|
7
|
+
when Hash
|
8
|
+
'{' + data.map {|k,v| "#{k}:#{inline_js_for(v)}"}.join(',') + '}'
|
9
|
+
when String
|
10
|
+
"'#{data}'"
|
11
|
+
else
|
12
|
+
data.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def creation_call(name, class_name, info)
|
18
|
+
"#{name} = #{class_name}({\n" +
|
19
|
+
info.keys.sort.map {|key| "\t#{key}:#{inline_js_for(info[key])}"}.join(",\n") + "\n});"
|
20
|
+
end
|
21
|
+
|
22
|
+
def js_sections_for(node)
|
23
|
+
[creation_call(node.name, node.node_class.creation_call, node.properties)] +
|
24
|
+
node.subviews.map {|child| [js_sections_for(child), "#{node.name}.add(#{child.name});"]}.flatten
|
25
|
+
end
|
26
|
+
|
27
|
+
def js_for(nodes)
|
28
|
+
nodes.map {|node| js_sections_for(node)}.flatten.join("\n\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
def js_comments_for text
|
32
|
+
"" #text.map {|line| line.chomp.empty? ? line : "// #{line}"}.join + "\n"
|
33
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -75,6 +75,22 @@ dependencies:
|
|
75
75
|
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: plist
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
78
94
|
- !ruby/object:Gem::Dependency
|
79
95
|
name: rdoc
|
80
96
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,45 +236,29 @@ dependencies:
|
|
220
236
|
- !ruby/object:Gem::Version
|
221
237
|
version: 2.3.1
|
222
238
|
- !ruby/object:Gem::Dependency
|
223
|
-
name:
|
224
|
-
requirement: !ruby/object:Gem::Requirement
|
225
|
-
none: false
|
226
|
-
requirements:
|
227
|
-
- - ! '>='
|
228
|
-
- !ruby/object:Gem::Version
|
229
|
-
version: 0.9.1
|
230
|
-
type: :development
|
231
|
-
prerelease: false
|
232
|
-
version_requirements: !ruby/object:Gem::Requirement
|
233
|
-
none: false
|
234
|
-
requirements:
|
235
|
-
- - ! '>='
|
236
|
-
- !ruby/object:Gem::Version
|
237
|
-
version: 0.9.1
|
238
|
-
- !ruby/object:Gem::Dependency
|
239
|
-
name: system_timer
|
239
|
+
name: activesupport
|
240
240
|
requirement: !ruby/object:Gem::Requirement
|
241
241
|
none: false
|
242
242
|
requirements:
|
243
243
|
- - ! '>='
|
244
244
|
- !ruby/object:Gem::Version
|
245
|
-
version:
|
246
|
-
type: :
|
245
|
+
version: 3.2.8
|
246
|
+
type: :runtime
|
247
247
|
prerelease: false
|
248
248
|
version_requirements: !ruby/object:Gem::Requirement
|
249
249
|
none: false
|
250
250
|
requirements:
|
251
251
|
- - ! '>='
|
252
252
|
- !ruby/object:Gem::Version
|
253
|
-
version:
|
253
|
+
version: 3.2.8
|
254
254
|
- !ruby/object:Gem::Dependency
|
255
|
-
name:
|
255
|
+
name: grit
|
256
256
|
requirement: !ruby/object:Gem::Requirement
|
257
257
|
none: false
|
258
258
|
requirements:
|
259
259
|
- - ! '>='
|
260
260
|
- !ruby/object:Gem::Version
|
261
|
-
version:
|
261
|
+
version: '0'
|
262
262
|
type: :runtime
|
263
263
|
prerelease: false
|
264
264
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -266,9 +266,9 @@ dependencies:
|
|
266
266
|
requirements:
|
267
267
|
- - ! '>='
|
268
268
|
- !ruby/object:Gem::Version
|
269
|
-
version:
|
269
|
+
version: '0'
|
270
270
|
- !ruby/object:Gem::Dependency
|
271
|
-
name:
|
271
|
+
name: i18n
|
272
272
|
requirement: !ruby/object:Gem::Requirement
|
273
273
|
none: false
|
274
274
|
requirements:
|
@@ -284,7 +284,7 @@ dependencies:
|
|
284
284
|
- !ruby/object:Gem::Version
|
285
285
|
version: '0'
|
286
286
|
- !ruby/object:Gem::Dependency
|
287
|
-
name:
|
287
|
+
name: hirb
|
288
288
|
requirement: !ruby/object:Gem::Requirement
|
289
289
|
none: false
|
290
290
|
requirements:
|
@@ -300,7 +300,7 @@ dependencies:
|
|
300
300
|
- !ruby/object:Gem::Version
|
301
301
|
version: '0'
|
302
302
|
- !ruby/object:Gem::Dependency
|
303
|
-
name:
|
303
|
+
name: cli-colorize
|
304
304
|
requirement: !ruby/object:Gem::Requirement
|
305
305
|
none: false
|
306
306
|
requirements:
|
@@ -316,7 +316,7 @@ dependencies:
|
|
316
316
|
- !ruby/object:Gem::Version
|
317
317
|
version: '0'
|
318
318
|
- !ruby/object:Gem::Dependency
|
319
|
-
name:
|
319
|
+
name: rdoc
|
320
320
|
requirement: !ruby/object:Gem::Requirement
|
321
321
|
none: false
|
322
322
|
requirements:
|
@@ -332,7 +332,7 @@ dependencies:
|
|
332
332
|
- !ruby/object:Gem::Version
|
333
333
|
version: '0'
|
334
334
|
- !ruby/object:Gem::Dependency
|
335
|
-
name:
|
335
|
+
name: yajl-ruby
|
336
336
|
requirement: !ruby/object:Gem::Requirement
|
337
337
|
none: false
|
338
338
|
requirements:
|
@@ -348,7 +348,7 @@ dependencies:
|
|
348
348
|
- !ruby/object:Gem::Version
|
349
349
|
version: '0'
|
350
350
|
- !ruby/object:Gem::Dependency
|
351
|
-
name:
|
351
|
+
name: plist
|
352
352
|
requirement: !ruby/object:Gem::Requirement
|
353
353
|
none: false
|
354
354
|
requirements:
|
@@ -377,6 +377,11 @@ files:
|
|
377
377
|
- README.md
|
378
378
|
- VERSION
|
379
379
|
- lib/puer.rb
|
380
|
+
- lib/puer/config.rb
|
381
|
+
- lib/puer/converters.rb
|
382
|
+
- lib/puer/nodes.rb
|
383
|
+
- lib/puer/session.rb
|
384
|
+
- lib/puer/xibtoti.rb
|
380
385
|
- !binary |-
|
381
386
|
YmluL3B1ZXI=
|
382
387
|
homepage: http://github.com/eiffelqiu/puer
|
@@ -394,7 +399,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
394
399
|
version: '0'
|
395
400
|
segments:
|
396
401
|
- 0
|
397
|
-
hash:
|
402
|
+
hash: -841221982100314824
|
398
403
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
399
404
|
none: false
|
400
405
|
requirements:
|
@@ -403,7 +408,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
403
408
|
version: '0'
|
404
409
|
requirements: []
|
405
410
|
rubyforge_project:
|
406
|
-
rubygems_version: 1.8.
|
411
|
+
rubygems_version: 1.8.24
|
407
412
|
signing_key:
|
408
413
|
specification_version: 3
|
409
414
|
summary: Titanium Starter Project Generate Tools
|