dashcode-converter 0.0.1
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/.gitignore +1 -0
- data/README.markdown +18 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/dashcode-converter +51 -0
- data/dashcode-converter.tmproj +122 -0
- data/lib/dashcode-converter.rb +45 -0
- data/lib/dashcode-converter/controller.rb +33 -0
- data/lib/dashcode-converter/nib.rb +93 -0
- data/lib/dashcode-converter/nib/collection-view.rb +41 -0
- data/lib/dashcode-converter/nib/nib-item.rb +80 -0
- data/lib/dashcode-converter/nib/view.rb +51 -0
- data/lib/dashcode-converter/project.rb +144 -0
- metadata +111 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
out/*
|
data/README.markdown
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Dashcode Converter #
|
2
|
+
|
3
|
+
This tool converts Apple Dashcode projects into NIBs for use with the [Coherent Javascript framework](http://github.com/jeffwatkins/coherent/).
|
4
|
+
|
5
|
+
## Installation ##
|
6
|
+
|
7
|
+
Like all Ruby Gems, installation is really simple:
|
8
|
+
|
9
|
+
$ gem install dashcode-converter
|
10
|
+
|
11
|
+
## Usage ##
|
12
|
+
|
13
|
+
You can pass `dashcode-converter` either the path to a Dashcode project file (ending in .dcproj) or a folder that contains a Dashcode project file. For example:
|
14
|
+
|
15
|
+
$ dashcode-converter ~/Projects/RssReader.dcproj
|
16
|
+
|
17
|
+
This will create a Coherent NIB package in the `out` folder with the same name as the project. The NIB package will contain an HTML file, CSS file, copies of any images used by the project's HTML and CSS files, a controller class file, and a NIB definition.
|
18
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "dashcode-converter"
|
8
|
+
gem.summary = "Tool to convert Apple Dashcode projects into Coherent NIBs"
|
9
|
+
gem.description = ""
|
10
|
+
gem.email = "jeff@metrocat.org"
|
11
|
+
gem.homepage = "http://github.com/jeffwatkins/dashcode-converter"
|
12
|
+
gem.authors = ["Jeff Watkins"]
|
13
|
+
gem.add_dependency('json', '>= 1.4.3')
|
14
|
+
gem.add_dependency('nokogiri', '>=1.4.3.1')
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
# require 'rake/testtask'
|
23
|
+
# Rake::TestTask.new(:test) do |test|
|
24
|
+
# test.libs << 'lib' << 'test'
|
25
|
+
# test.pattern = 'test/**/test_*.rb'
|
26
|
+
# test.verbose = true
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# begin
|
30
|
+
# require 'rcov/rcovtask'
|
31
|
+
# Rcov::RcovTask.new do |test|
|
32
|
+
# test.libs << 'test'
|
33
|
+
# test.pattern = 'test/**/test_*.rb'
|
34
|
+
# test.verbose = true
|
35
|
+
# end
|
36
|
+
# rescue LoadError
|
37
|
+
# task :rcov do
|
38
|
+
# abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
|
42
|
+
task :push => "gemcutter:release"
|
43
|
+
|
44
|
+
task :test => :check_dependencies
|
45
|
+
|
46
|
+
task :default => :build
|
47
|
+
|
48
|
+
require 'rake/rdoctask'
|
49
|
+
Rake::RDocTask.new do |rdoc|
|
50
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "dashcode-converter #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
module DashcodeConverter
|
5
|
+
|
6
|
+
LIB_DIR= File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
7
|
+
VENDOR_DIR= File.expand_path(File.join(File.dirname(__FILE__), "..", "vendor"))
|
8
|
+
APP_NAME= File.basename($0)
|
9
|
+
|
10
|
+
$:.unshift(LIB_DIR)
|
11
|
+
|
12
|
+
options= {
|
13
|
+
:namespace=>nil,
|
14
|
+
:output_folder=>File.expand_path('out')
|
15
|
+
}
|
16
|
+
|
17
|
+
optparser= OptionParser.new do |opts|
|
18
|
+
opts.banner= "Usage: #{APP_NAME} [options] PROJECT"
|
19
|
+
opts.on('--namespace NAMESPACE', "Define the namespace to contain classes and functions.") do |namespace|
|
20
|
+
options[:namespace]= namespace
|
21
|
+
end
|
22
|
+
opts.on('--dest FOLDER', "Specifies where should the output JSIB folder be located.") do |output_folder|
|
23
|
+
options[:output_folder]= output_folder
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
optparser.parse!
|
28
|
+
|
29
|
+
require 'dashcode-converter'
|
30
|
+
|
31
|
+
project_files= []
|
32
|
+
|
33
|
+
ARGV.each { |arg|
|
34
|
+
arg= File.expand_path(arg)
|
35
|
+
project_files += Dir.glob(File.join(arg, "*.dcproj")) if File.directory?(arg)
|
36
|
+
project_files << arg if File.fnmatch("*.dcproj", arg)
|
37
|
+
}
|
38
|
+
|
39
|
+
if project_files.empty?
|
40
|
+
puts optparser
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
project_files.each { |project_path|
|
45
|
+
project= Project.new(project_path, options[:output_folder])
|
46
|
+
project.namespace= options[:namespace]
|
47
|
+
project.convert
|
48
|
+
}
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>currentDocument</key>
|
6
|
+
<string>out/dash2coherent.jsnib/dash2coherent.js</string>
|
7
|
+
<key>documents</key>
|
8
|
+
<array>
|
9
|
+
<dict>
|
10
|
+
<key>expanded</key>
|
11
|
+
<true/>
|
12
|
+
<key>name</key>
|
13
|
+
<string>dashcode-converter</string>
|
14
|
+
<key>regexFolderFilter</key>
|
15
|
+
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
16
|
+
<key>sourceDirectory</key>
|
17
|
+
<string></string>
|
18
|
+
</dict>
|
19
|
+
</array>
|
20
|
+
<key>fileHierarchyDrawerWidth</key>
|
21
|
+
<integer>270</integer>
|
22
|
+
<key>metaData</key>
|
23
|
+
<dict>
|
24
|
+
<key>README.markdown</key>
|
25
|
+
<dict>
|
26
|
+
<key>caret</key>
|
27
|
+
<dict>
|
28
|
+
<key>column</key>
|
29
|
+
<integer>0</integer>
|
30
|
+
<key>line</key>
|
31
|
+
<integer>18</integer>
|
32
|
+
</dict>
|
33
|
+
<key>firstVisibleColumn</key>
|
34
|
+
<integer>0</integer>
|
35
|
+
<key>firstVisibleLine</key>
|
36
|
+
<integer>0</integer>
|
37
|
+
</dict>
|
38
|
+
<key>lib/dashcode-converter/nib/collection-view.rb</key>
|
39
|
+
<dict>
|
40
|
+
<key>caret</key>
|
41
|
+
<dict>
|
42
|
+
<key>column</key>
|
43
|
+
<integer>15</integer>
|
44
|
+
<key>line</key>
|
45
|
+
<integer>31</integer>
|
46
|
+
</dict>
|
47
|
+
<key>firstVisibleColumn</key>
|
48
|
+
<integer>0</integer>
|
49
|
+
<key>firstVisibleLine</key>
|
50
|
+
<integer>0</integer>
|
51
|
+
</dict>
|
52
|
+
<key>lib/dashcode-converter/nib/nib-item.rb</key>
|
53
|
+
<dict>
|
54
|
+
<key>caret</key>
|
55
|
+
<dict>
|
56
|
+
<key>column</key>
|
57
|
+
<integer>0</integer>
|
58
|
+
<key>line</key>
|
59
|
+
<integer>51</integer>
|
60
|
+
</dict>
|
61
|
+
<key>firstVisibleColumn</key>
|
62
|
+
<integer>0</integer>
|
63
|
+
<key>firstVisibleLine</key>
|
64
|
+
<integer>23</integer>
|
65
|
+
</dict>
|
66
|
+
<key>out/dash2coherent.jsnib/Dash2coherentController.js</key>
|
67
|
+
<dict>
|
68
|
+
<key>caret</key>
|
69
|
+
<dict>
|
70
|
+
<key>column</key>
|
71
|
+
<integer>0</integer>
|
72
|
+
<key>line</key>
|
73
|
+
<integer>0</integer>
|
74
|
+
</dict>
|
75
|
+
<key>firstVisibleColumn</key>
|
76
|
+
<integer>0</integer>
|
77
|
+
<key>firstVisibleLine</key>
|
78
|
+
<integer>0</integer>
|
79
|
+
</dict>
|
80
|
+
<key>out/dash2coherent.jsnib/dash2coherent.css</key>
|
81
|
+
<dict>
|
82
|
+
<key>caret</key>
|
83
|
+
<dict>
|
84
|
+
<key>column</key>
|
85
|
+
<integer>9</integer>
|
86
|
+
<key>line</key>
|
87
|
+
<integer>384</integer>
|
88
|
+
</dict>
|
89
|
+
<key>firstVisibleColumn</key>
|
90
|
+
<integer>0</integer>
|
91
|
+
<key>firstVisibleLine</key>
|
92
|
+
<integer>196</integer>
|
93
|
+
</dict>
|
94
|
+
<key>out/dash2coherent.jsnib/dash2coherent.js</key>
|
95
|
+
<dict>
|
96
|
+
<key>caret</key>
|
97
|
+
<dict>
|
98
|
+
<key>column</key>
|
99
|
+
<integer>0</integer>
|
100
|
+
<key>line</key>
|
101
|
+
<integer>0</integer>
|
102
|
+
</dict>
|
103
|
+
<key>firstVisibleColumn</key>
|
104
|
+
<integer>0</integer>
|
105
|
+
<key>firstVisibleLine</key>
|
106
|
+
<integer>0</integer>
|
107
|
+
</dict>
|
108
|
+
</dict>
|
109
|
+
<key>openDocuments</key>
|
110
|
+
<array>
|
111
|
+
<string>out/dash2coherent.jsnib/dash2coherent.js</string>
|
112
|
+
<string>out/dash2coherent.jsnib/dash2coherent.css</string>
|
113
|
+
<string>out/dash2coherent.jsnib/Dash2coherentController.js</string>
|
114
|
+
<string>lib/dashcode-converter/nib/nib-item.rb</string>
|
115
|
+
<string>lib/dashcode-converter/nib/collection-view.rb</string>
|
116
|
+
</array>
|
117
|
+
<key>showFileHierarchyDrawer</key>
|
118
|
+
<false/>
|
119
|
+
<key>windowFrame</key>
|
120
|
+
<string>{{318, 0}, {1122, 878}}</string>
|
121
|
+
</dict>
|
122
|
+
</plist>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'erb'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module DashcodeConverter
|
7
|
+
|
8
|
+
JSON_PARAMS= {
|
9
|
+
:indent=> " ",
|
10
|
+
:object_nl=> "\n",
|
11
|
+
:array_nl=> "\n",
|
12
|
+
:space=> " "
|
13
|
+
}
|
14
|
+
|
15
|
+
INDENT= " "
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class JavascriptCode < String
|
20
|
+
def to_json(*options)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Kernel
|
26
|
+
# A convenience factory method
|
27
|
+
def JavascriptCode(str)
|
28
|
+
JavascriptCode.new(str)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class String
|
33
|
+
def remove_indent
|
34
|
+
match= self.match(/(^\s+)/)
|
35
|
+
return self unless match
|
36
|
+
self.gsub(/^#{match[1]}/, '').strip
|
37
|
+
end
|
38
|
+
def indent(str)
|
39
|
+
self.gsub(/^/, str)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'dashcode-converter/controller'
|
44
|
+
require 'dashcode-converter/nib'
|
45
|
+
require 'dashcode-converter/project'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module DashcodeConverter
|
2
|
+
|
3
|
+
class Controller
|
4
|
+
|
5
|
+
DECL_TEMPLATE= <<-EOF
|
6
|
+
/*import coherent*/
|
7
|
+
|
8
|
+
<%=namespace%>.<%=name%>= Class.create(coherent.ViewController, {
|
9
|
+
|
10
|
+
<%=@methods.join(",\n").indent(INDENT)%>
|
11
|
+
|
12
|
+
});
|
13
|
+
EOF
|
14
|
+
|
15
|
+
attr_reader :name, :namespace
|
16
|
+
|
17
|
+
def initialize(name, namespace=nil)
|
18
|
+
@name= "#{name.capitalize}Controller"
|
19
|
+
@namespace= namespace || name
|
20
|
+
@methods= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_action_method(name)
|
24
|
+
@methods << "#{name}: function(sender)\n{\n}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def declaration
|
28
|
+
return @declaration if @declaration
|
29
|
+
@declaration= ERB.new(DECL_TEMPLATE.remove_indent).result binding
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module DashcodeConverter
|
2
|
+
|
3
|
+
module Nib
|
4
|
+
|
5
|
+
CLASSNAME_LOOKUP= {
|
6
|
+
"Text" => "View",
|
7
|
+
"PushButton" => "Button",
|
8
|
+
"List" => "CollectionView"
|
9
|
+
}
|
10
|
+
|
11
|
+
class Nib
|
12
|
+
|
13
|
+
DECL_TEMPLATE= <<-EOF
|
14
|
+
/*import coherent*/
|
15
|
+
NIB('<%=name%>', {
|
16
|
+
|
17
|
+
<%=items_array.join(",\n\n").indent(INDENT)%>,
|
18
|
+
|
19
|
+
'owner': {
|
20
|
+
view: REF('<%=name%>')
|
21
|
+
}
|
22
|
+
});
|
23
|
+
EOF
|
24
|
+
|
25
|
+
attr_reader :name, :items, :views, :owner
|
26
|
+
|
27
|
+
def initialize(name, owner)
|
28
|
+
@name= name
|
29
|
+
@owner= owner
|
30
|
+
@items= {}
|
31
|
+
@views= []
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_view(view)
|
35
|
+
items[view.name]= view
|
36
|
+
views << view
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_view_from_path(path, name=@name)
|
40
|
+
view= View.new(name, parse(path), self)
|
41
|
+
add_view(view)
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_datasources_from_path(path)
|
45
|
+
datasources= parse(path)
|
46
|
+
datasources.each { |name, datasource|
|
47
|
+
items[name]= NibItem.new(name, datasource, self)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def declaration
|
52
|
+
return @declaration if @declaration
|
53
|
+
|
54
|
+
items_array= items.map { |key, value| value.declaration }
|
55
|
+
|
56
|
+
@declaration= ERB.new(DECL_TEMPLATE.remove_indent).result binding
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse(path)
|
60
|
+
in_json= false
|
61
|
+
json= "{\n"
|
62
|
+
text= File.read(path)
|
63
|
+
text.each { |line|
|
64
|
+
if in_json
|
65
|
+
json << line
|
66
|
+
next
|
67
|
+
end
|
68
|
+
|
69
|
+
next unless (line =~ /dashcodePartSpecs/ || line =~ /dashcodeDataSources/)
|
70
|
+
in_json= true
|
71
|
+
}
|
72
|
+
JSON.parse(json.gsub(/;$/,''))
|
73
|
+
end
|
74
|
+
|
75
|
+
def unique_name(basename)
|
76
|
+
index=0
|
77
|
+
name= basename
|
78
|
+
while items.include?(name)
|
79
|
+
index+=1
|
80
|
+
name= "#{basename}#{index}"
|
81
|
+
end
|
82
|
+
name
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
require 'dashcode-converter/nib/nib-item'
|
92
|
+
require 'dashcode-converter/nib/view'
|
93
|
+
require 'dashcode-converter/nib/collection-view'
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module DashcodeConverter
|
2
|
+
|
3
|
+
module Nib
|
4
|
+
|
5
|
+
class NibItem
|
6
|
+
|
7
|
+
def adjust_declaration_for_CollectionView(decl)
|
8
|
+
if decl.include?("dataArrayBinding")
|
9
|
+
decl.delete("dataArray")
|
10
|
+
decl["contentBinding"]= decl.delete("dataArrayBinding")
|
11
|
+
end
|
12
|
+
|
13
|
+
decl.delete("useDataSource")
|
14
|
+
decl.delete("sampleRows")
|
15
|
+
decl.delete("labelElementId")
|
16
|
+
decl.delete("listStyle")
|
17
|
+
decl["content"]= decl.delete("dataArray") if decl.include?("dataArray")
|
18
|
+
decl
|
19
|
+
end
|
20
|
+
|
21
|
+
# Need to fix up the bindings for CollectionViews because Coherent before
|
22
|
+
# version 3.0 used a whacky star notation for binding to values in the
|
23
|
+
# list view.
|
24
|
+
def fixup_html_for_CollectionView(html)
|
25
|
+
template= View.new(nib.unique_name("ItemTemplate"), nil, nib)
|
26
|
+
template.is_template= true
|
27
|
+
nib.add_view(template)
|
28
|
+
@spec["viewTemplate"]= JavascriptCode("REF('#{template.name}')")
|
29
|
+
|
30
|
+
html.css("[id]").each { |node|
|
31
|
+
next unless (item= view.items_by_id["##{node["id"]}"])
|
32
|
+
view.remove_item(item)
|
33
|
+
template.add_item(item)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module DashcodeConverter
|
2
|
+
module Nib
|
3
|
+
|
4
|
+
class NibItem
|
5
|
+
|
6
|
+
attr_reader :name, :nib
|
7
|
+
attr_accessor :view
|
8
|
+
|
9
|
+
def initialize(name, spec, nib)
|
10
|
+
@name= name
|
11
|
+
@nib= nib
|
12
|
+
@view= nil
|
13
|
+
@spec= from_spec(spec)
|
14
|
+
end
|
15
|
+
|
16
|
+
def declaration
|
17
|
+
return @declaration if @declaration
|
18
|
+
|
19
|
+
values= @spec.to_json(JSON_PARAMS)
|
20
|
+
@declaration= "'#{name}': #{@classname}(#{values})"
|
21
|
+
end
|
22
|
+
|
23
|
+
def fixup_html(html)
|
24
|
+
if 'coherent.Button'==@classname
|
25
|
+
html.name='button'
|
26
|
+
end
|
27
|
+
if @spec.include?('text')
|
28
|
+
html.content= @spec.delete('text')
|
29
|
+
end
|
30
|
+
|
31
|
+
# allow class specific fixups
|
32
|
+
if self.respond_to?("fixup_html_for_#{@fixupname}")
|
33
|
+
self.send("fixup_html_for_#{@fixupname}", html)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def from_spec(spec)
|
38
|
+
nib_item= {}
|
39
|
+
spec.each { |key, value|
|
40
|
+
next if ["view", "Class", "propertyValues"].include?(key)
|
41
|
+
|
42
|
+
# translate Dashcode's onclick parameter into a target/action pair
|
43
|
+
if "onclick"==key
|
44
|
+
key="action"
|
45
|
+
nib_item["target"]= "owner"
|
46
|
+
nib.owner.add_action_method(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
nib_item[key]= value
|
50
|
+
}
|
51
|
+
nib_item.merge!(spec['propertyValues']) if spec['propertyValues']
|
52
|
+
|
53
|
+
nib_item.each { |key, value|
|
54
|
+
# rewrite relative bindings to use representedObject which is the
|
55
|
+
# way this works in Coherent 3.0.
|
56
|
+
if key =~ /Binding$/ && value.include?("keypath")
|
57
|
+
value["keypath"]= value["keypath"].gsub(/^\*\./, "representedObject.")
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
parts= (spec['view']||spec['Class']).split(".")
|
62
|
+
if 2==parts.length
|
63
|
+
parts[0]= "coherent" if parts[0]=="DC"
|
64
|
+
parts[1]= CLASSNAME_LOOKUP[parts[1]] if CLASSNAME_LOOKUP.include?(parts[1])
|
65
|
+
end
|
66
|
+
|
67
|
+
@fixupname= parts[-1]
|
68
|
+
|
69
|
+
if self.respond_to?("adjust_declaration_for_#{@fixupname}")
|
70
|
+
nib_item= self.send("adjust_declaration_for_#{@fixupname}", nib_item)
|
71
|
+
end
|
72
|
+
|
73
|
+
@classname= parts.join(".")
|
74
|
+
nib_item
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module DashcodeConverter
|
2
|
+
module Nib
|
3
|
+
|
4
|
+
class View
|
5
|
+
|
6
|
+
DECL_TEMPLATE= <<-EOF
|
7
|
+
'<%=name%>': <%=is_template ? "VIEW_TEMPLATE" : "VIEW"%>({
|
8
|
+
<%=views.join(",\n\n").indent(INDENT)%>
|
9
|
+
})
|
10
|
+
EOF
|
11
|
+
|
12
|
+
attr_reader :name, :nib, :items, :items_by_id
|
13
|
+
attr_accessor :is_template
|
14
|
+
|
15
|
+
def initialize(name, spec, nib)
|
16
|
+
@name= name
|
17
|
+
@nib= nib
|
18
|
+
@items= []
|
19
|
+
@items_by_id= {}
|
20
|
+
from_spec(spec) if spec
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_item(item)
|
24
|
+
@items_by_id.delete(item.name)
|
25
|
+
@items.delete(item)
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_item(item)
|
29
|
+
item.view= self
|
30
|
+
@items_by_id[item.name]= item
|
31
|
+
@items << item
|
32
|
+
end
|
33
|
+
|
34
|
+
def from_spec(spec)
|
35
|
+
spec.each { |id, part_spec|
|
36
|
+
item= NibItem.new("##{id}", part_spec, nib)
|
37
|
+
add_item(item)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def declaration
|
42
|
+
return @declaration if @declaration
|
43
|
+
|
44
|
+
views= items.map { |item| item.declaration }
|
45
|
+
@declaration= ERB.new(DECL_TEMPLATE.remove_indent).result binding
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module DashcodeConverter
|
2
|
+
|
3
|
+
CSS_IMAGE_URL_REGEX= /url\("?(.*\.(jpg|png|gif))"?\)/
|
4
|
+
BUNDLE_EXTENSION= "jsnib"
|
5
|
+
|
6
|
+
class Project
|
7
|
+
|
8
|
+
attr_accessor :namespace
|
9
|
+
|
10
|
+
def initialize(project_bundle, output_folder)
|
11
|
+
@project_bundle= File.expand_path(project_bundle)
|
12
|
+
@name= File.basename(@project_bundle, ".*")
|
13
|
+
@namespace= @name
|
14
|
+
@output_folder= File.expand_path(File.join(output_folder, "#{@name}.#{BUNDLE_EXTENSION}"))
|
15
|
+
@parts_spec_path= File.join(@project_bundle, "project", "safari", "Parts", "setup.js")
|
16
|
+
@datasources_spec_path= File.join(@project_bundle, "project", "Parts", "datasources.js")
|
17
|
+
@css_path= File.join(@project_bundle, "project", "safari", "main.css")
|
18
|
+
@markup_path= File.join(@project_bundle, "project", "index.html")
|
19
|
+
@images_folder= File.join(@output_folder, "images")
|
20
|
+
FileUtils.mkdir_p(@output_folder)
|
21
|
+
end
|
22
|
+
|
23
|
+
def path_relative_to_folder(path, folder)
|
24
|
+
path= File.expand_path(path)
|
25
|
+
outputFolder= File.expand_path(folder).to_s
|
26
|
+
|
27
|
+
# Remove leading slash and split into parts
|
28
|
+
file_parts= path.slice(1..-1).split('/');
|
29
|
+
output_parts= outputFolder.slice(1..-1).split('/');
|
30
|
+
|
31
|
+
common_prefix_length= 0
|
32
|
+
|
33
|
+
file_parts.each_index { |i|
|
34
|
+
common_prefix_length= i
|
35
|
+
break if file_parts[i]!=output_parts[i]
|
36
|
+
}
|
37
|
+
|
38
|
+
return '../'*(output_parts.length-common_prefix_length) + file_parts[common_prefix_length..-1].join('/')
|
39
|
+
end
|
40
|
+
|
41
|
+
def relative_path(path)
|
42
|
+
path_relative_to_folder(path, @output_folder)
|
43
|
+
end
|
44
|
+
|
45
|
+
def doc
|
46
|
+
return @doc if @doc
|
47
|
+
|
48
|
+
html= File.read(@markup_path)
|
49
|
+
@doc= Nokogiri::HTML(html)
|
50
|
+
end
|
51
|
+
|
52
|
+
def controller
|
53
|
+
return @controller if @controller
|
54
|
+
@controller= Controller.new(@name, @namespace)
|
55
|
+
end
|
56
|
+
|
57
|
+
def nib
|
58
|
+
return @nib if @nib
|
59
|
+
|
60
|
+
@nib= Nib::Nib.new(@name, controller)
|
61
|
+
@nib.add_view_from_path(@parts_spec_path)
|
62
|
+
@nib.add_datasources_from_path(@datasources_spec_path)
|
63
|
+
@nib
|
64
|
+
end
|
65
|
+
|
66
|
+
def css
|
67
|
+
text= File.read(@css_path)
|
68
|
+
dirname= File.dirname(@css_path)
|
69
|
+
text.gsub!(CSS_IMAGE_URL_REGEX) { |match|
|
70
|
+
image_file= File.join(dirname, $1)
|
71
|
+
|
72
|
+
if (!File.exists?(image_file))
|
73
|
+
match
|
74
|
+
else
|
75
|
+
new_image_file= File.join(@images_folder, File.basename(image_file))
|
76
|
+
FileUtils.mkdir_p(File.dirname(new_image_file))
|
77
|
+
FileUtils.cp image_file, new_image_file
|
78
|
+
"url(\"#{relative_path(new_image_file)}\")"
|
79
|
+
end
|
80
|
+
}
|
81
|
+
text
|
82
|
+
end
|
83
|
+
|
84
|
+
def fixup_html
|
85
|
+
base= doc.css("base")
|
86
|
+
base= base ? base.attribute("href") : nil
|
87
|
+
|
88
|
+
content= doc.css("body > *:first-child")[0]
|
89
|
+
content.traverse { |node|
|
90
|
+
next unless node.attributes
|
91
|
+
node.attributes.each { |attr, value|
|
92
|
+
if 0==attr.index("apple-")
|
93
|
+
node.remove_attribute(attr)
|
94
|
+
end
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
dirname= File.dirname(@markup_path)
|
99
|
+
dirname= File.join(dirname, base) if base
|
100
|
+
|
101
|
+
content.css("[src]").each { |node|
|
102
|
+
image_file= File.join(dirname, node.attribute('src'))
|
103
|
+
new_image_file= File.join(@images_folder, File.basename(image_file))
|
104
|
+
FileUtils.mkdir_p(File.dirname(new_image_file))
|
105
|
+
FileUtils.cp image_file, new_image_file
|
106
|
+
node["src"]= relative_path(new_image_file)
|
107
|
+
}
|
108
|
+
|
109
|
+
nib.views.each { |view|
|
110
|
+
# Use a copy of the view's items, because the iterator isn't stable if
|
111
|
+
# items are removed while iterating.
|
112
|
+
items= view.items.clone
|
113
|
+
items.each { |item|
|
114
|
+
html= doc.css(item.name)[0]
|
115
|
+
item.fixup_html(html)
|
116
|
+
}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def convert
|
121
|
+
fixup_html
|
122
|
+
|
123
|
+
FileUtils.mkdir_p(@output_folder)
|
124
|
+
FileUtils.mkdir_p(@images_folder)
|
125
|
+
|
126
|
+
Dir.chdir(@output_folder) do
|
127
|
+
File.open("#{@name.capitalize}Controller.js", "w") { |controller_file|
|
128
|
+
controller_file << controller.declaration
|
129
|
+
}
|
130
|
+
File.open("#{@name}.js", "w") { |nib_file|
|
131
|
+
nib_file << nib.declaration
|
132
|
+
}
|
133
|
+
File.open("#{@name}.css", "w") { |css_file|
|
134
|
+
css_file << css
|
135
|
+
}
|
136
|
+
File.open("#{@name}.html", "w") { |html_file|
|
137
|
+
html_file << doc.css("body > *:first-child")[0].serialize
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dashcode-converter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jeff Watkins
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-12 00:00:00 -07:00
|
19
|
+
default_executable: dashcode-converter
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: json
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 1
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 4
|
33
|
+
- 3
|
34
|
+
version: 1.4.3
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: nokogiri
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 113
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 4
|
49
|
+
- 3
|
50
|
+
- 1
|
51
|
+
version: 1.4.3.1
|
52
|
+
type: :runtime
|
53
|
+
version_requirements: *id002
|
54
|
+
description: ""
|
55
|
+
email: jeff@metrocat.org
|
56
|
+
executables:
|
57
|
+
- dashcode-converter
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files:
|
61
|
+
- README.markdown
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- README.markdown
|
65
|
+
- Rakefile
|
66
|
+
- VERSION
|
67
|
+
- bin/dashcode-converter
|
68
|
+
- dashcode-converter.tmproj
|
69
|
+
- lib/dashcode-converter.rb
|
70
|
+
- lib/dashcode-converter/controller.rb
|
71
|
+
- lib/dashcode-converter/nib.rb
|
72
|
+
- lib/dashcode-converter/nib/collection-view.rb
|
73
|
+
- lib/dashcode-converter/nib/nib-item.rb
|
74
|
+
- lib/dashcode-converter/nib/view.rb
|
75
|
+
- lib/dashcode-converter/project.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://github.com/jeffwatkins/dashcode-converter
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options:
|
82
|
+
- --charset=UTF-8
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
requirements: []
|
104
|
+
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 1.3.7
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: Tool to convert Apple Dashcode projects into Coherent NIBs
|
110
|
+
test_files: []
|
111
|
+
|