cml 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/cml.gemspec +129 -0
- data/lib/cml/converters/jsawesome.rb +103 -0
- data/lib/cml/gold.rb +33 -0
- data/lib/cml/liquid_filters.rb +13 -0
- data/lib/cml/parser.rb +160 -0
- data/lib/cml/tag.rb +308 -0
- data/lib/cml/tags/checkbox.rb +77 -0
- data/lib/cml/tags/checkboxes.rb +38 -0
- data/lib/cml/tags/group.rb +25 -0
- data/lib/cml/tags/hidden.rb +17 -0
- data/lib/cml/tags/iterate.rb +86 -0
- data/lib/cml/tags/meta.rb +12 -0
- data/lib/cml/tags/multiple_text.rb +11 -0
- data/lib/cml/tags/option.rb +29 -0
- data/lib/cml/tags/radio.rb +48 -0
- data/lib/cml/tags/radios.rb +36 -0
- data/lib/cml/tags/ratings.rb +61 -0
- data/lib/cml/tags/search.rb +97 -0
- data/lib/cml/tags/select.rb +50 -0
- data/lib/cml/tags/text.rb +45 -0
- data/lib/cml/tags/textarea.rb +45 -0
- data/lib/cml/tags/thumb.rb +25 -0
- data/lib/cml/tags/unknown.rb +21 -0
- data/lib/cml.rb +15 -0
- data/spec/complex_spec.rb +127 -0
- data/spec/converters/jsawesome_spec.rb +75 -0
- data/spec/fixtures/complex.cml +23 -0
- data/spec/fixtures/html.cml +34 -0
- data/spec/fixtures/invalid.cml +10 -0
- data/spec/fixtures/script-style.cml +12 -0
- data/spec/fixtures/segfault.cml +1 -0
- data/spec/gold_spec.rb +29 -0
- data/spec/meta_spec.rb +47 -0
- data/spec/normalize_spec.rb +214 -0
- data/spec/show_data_spec.rb +308 -0
- data/spec/sorta_match.rb +41 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/tags/checkbox_spec.rb +155 -0
- data/spec/tags/checkboxes_spec.rb +171 -0
- data/spec/tags/group_spec.rb +108 -0
- data/spec/tags/hidden_spec.rb +17 -0
- data/spec/tags/iterate_spec.rb +259 -0
- data/spec/tags/meta_spec.rb +14 -0
- data/spec/tags/multiple_text_spec.rb +40 -0
- data/spec/tags/radios_spec.rb +81 -0
- data/spec/tags/ratings_spec.rb +79 -0
- data/spec/tags/search_spec.rb +132 -0
- data/spec/tags/select_spec.rb +76 -0
- data/spec/tags/tag_spec.rb +93 -0
- data/spec/tags/text_spec.rb +66 -0
- data/spec/tags/textarea_spec.rb +58 -0
- data/spec/tags/thumb_spec.rb +20 -0
- data/spec/tags/unknown_spec.rb +19 -0
- data/spec/validation_spec.rb +62 -0
- metadata +182 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Chris Van Pelt
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
= cml
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but
|
13
|
+
bump version in a commit by itself I can ignore when I pull)
|
14
|
+
* Send me a pull request. Bonus points for topic branches.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2009 Chris Van Pelt. See LICENSE for details.
|
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 = "cml"
|
8
|
+
gem.summary = "CML is CrowdFlower Markup Language"
|
9
|
+
gem.description = "CML let's you easily create form elements and custom interfaces for the CrowdFlower Croud Labor platform"
|
10
|
+
gem.email = "vanpelt@doloreslabs.com"
|
11
|
+
gem.homepage = "http://github.com/dolores/cml"
|
12
|
+
gem.authors = ["Chris Van Pelt"]
|
13
|
+
gem.add_dependency 'liquid', '>= 2.0.0'
|
14
|
+
gem.add_dependency 'nokogiri', '>= 1.4.2'
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_opts = ["--format", "specdoc"]
|
26
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
31
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
32
|
+
spec.rcov = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :refresh_builder => [:build] do
|
36
|
+
cp "pkg/cml-#{File.read("VERSION").strip}.gem", "../builder/gems/cache/"
|
37
|
+
rm_rf "../builder/gems/gems/cml-#{File.read("VERSION").strip}/"
|
38
|
+
`cd ../builder && bin/thor merb:gem:redeploy`
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :spec
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
if File.exist?('VERSION.yml')
|
46
|
+
config = YAML.load(File.read('VERSION.yml'))
|
47
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
48
|
+
else
|
49
|
+
version = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "cml #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.4.1
|
data/cml.gemspec
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{cml}
|
8
|
+
s.version = "1.4.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Chris Van Pelt"]
|
12
|
+
s.date = %q{2011-02-28}
|
13
|
+
s.description = %q{CML let's you easily create form elements and custom interfaces for the CrowdFlower Croud Labor platform}
|
14
|
+
s.email = %q{vanpelt@doloreslabs.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"cml.gemspec",
|
27
|
+
"lib/cml.rb",
|
28
|
+
"lib/cml/converters/jsawesome.rb",
|
29
|
+
"lib/cml/gold.rb",
|
30
|
+
"lib/cml/liquid_filters.rb",
|
31
|
+
"lib/cml/parser.rb",
|
32
|
+
"lib/cml/tag.rb",
|
33
|
+
"lib/cml/tags/checkbox.rb",
|
34
|
+
"lib/cml/tags/checkboxes.rb",
|
35
|
+
"lib/cml/tags/group.rb",
|
36
|
+
"lib/cml/tags/hidden.rb",
|
37
|
+
"lib/cml/tags/iterate.rb",
|
38
|
+
"lib/cml/tags/meta.rb",
|
39
|
+
"lib/cml/tags/multiple_text.rb",
|
40
|
+
"lib/cml/tags/option.rb",
|
41
|
+
"lib/cml/tags/radio.rb",
|
42
|
+
"lib/cml/tags/radios.rb",
|
43
|
+
"lib/cml/tags/ratings.rb",
|
44
|
+
"lib/cml/tags/search.rb",
|
45
|
+
"lib/cml/tags/select.rb",
|
46
|
+
"lib/cml/tags/text.rb",
|
47
|
+
"lib/cml/tags/textarea.rb",
|
48
|
+
"lib/cml/tags/thumb.rb",
|
49
|
+
"lib/cml/tags/unknown.rb",
|
50
|
+
"spec/complex_spec.rb",
|
51
|
+
"spec/converters/jsawesome_spec.rb",
|
52
|
+
"spec/fixtures/complex.cml",
|
53
|
+
"spec/fixtures/html.cml",
|
54
|
+
"spec/fixtures/invalid.cml",
|
55
|
+
"spec/fixtures/script-style.cml",
|
56
|
+
"spec/fixtures/segfault.cml",
|
57
|
+
"spec/gold_spec.rb",
|
58
|
+
"spec/meta_spec.rb",
|
59
|
+
"spec/normalize_spec.rb",
|
60
|
+
"spec/show_data_spec.rb",
|
61
|
+
"spec/sorta_match.rb",
|
62
|
+
"spec/spec_helper.rb",
|
63
|
+
"spec/tags/checkbox_spec.rb",
|
64
|
+
"spec/tags/checkboxes_spec.rb",
|
65
|
+
"spec/tags/group_spec.rb",
|
66
|
+
"spec/tags/hidden_spec.rb",
|
67
|
+
"spec/tags/iterate_spec.rb",
|
68
|
+
"spec/tags/meta_spec.rb",
|
69
|
+
"spec/tags/multiple_text_spec.rb",
|
70
|
+
"spec/tags/radios_spec.rb",
|
71
|
+
"spec/tags/ratings_spec.rb",
|
72
|
+
"spec/tags/search_spec.rb",
|
73
|
+
"spec/tags/select_spec.rb",
|
74
|
+
"spec/tags/tag_spec.rb",
|
75
|
+
"spec/tags/text_spec.rb",
|
76
|
+
"spec/tags/textarea_spec.rb",
|
77
|
+
"spec/tags/thumb_spec.rb",
|
78
|
+
"spec/tags/unknown_spec.rb",
|
79
|
+
"spec/validation_spec.rb"
|
80
|
+
]
|
81
|
+
s.homepage = %q{http://github.com/dolores/cml}
|
82
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
83
|
+
s.require_paths = ["lib"]
|
84
|
+
s.rubygems_version = %q{1.3.7}
|
85
|
+
s.summary = %q{CML is CrowdFlower Markup Language}
|
86
|
+
s.test_files = [
|
87
|
+
"spec/complex_spec.rb",
|
88
|
+
"spec/converters/jsawesome_spec.rb",
|
89
|
+
"spec/gold_spec.rb",
|
90
|
+
"spec/meta_spec.rb",
|
91
|
+
"spec/normalize_spec.rb",
|
92
|
+
"spec/show_data_spec.rb",
|
93
|
+
"spec/sorta_match.rb",
|
94
|
+
"spec/spec_helper.rb",
|
95
|
+
"spec/tags/checkbox_spec.rb",
|
96
|
+
"spec/tags/checkboxes_spec.rb",
|
97
|
+
"spec/tags/group_spec.rb",
|
98
|
+
"spec/tags/hidden_spec.rb",
|
99
|
+
"spec/tags/iterate_spec.rb",
|
100
|
+
"spec/tags/meta_spec.rb",
|
101
|
+
"spec/tags/multiple_text_spec.rb",
|
102
|
+
"spec/tags/radios_spec.rb",
|
103
|
+
"spec/tags/ratings_spec.rb",
|
104
|
+
"spec/tags/search_spec.rb",
|
105
|
+
"spec/tags/select_spec.rb",
|
106
|
+
"spec/tags/tag_spec.rb",
|
107
|
+
"spec/tags/text_spec.rb",
|
108
|
+
"spec/tags/textarea_spec.rb",
|
109
|
+
"spec/tags/thumb_spec.rb",
|
110
|
+
"spec/tags/unknown_spec.rb",
|
111
|
+
"spec/validation_spec.rb"
|
112
|
+
]
|
113
|
+
|
114
|
+
if s.respond_to? :specification_version then
|
115
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
116
|
+
s.specification_version = 3
|
117
|
+
|
118
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
119
|
+
s.add_runtime_dependency(%q<liquid>, [">= 2.0.0"])
|
120
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.2"])
|
121
|
+
else
|
122
|
+
s.add_dependency(%q<liquid>, [">= 2.0.0"])
|
123
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.2"])
|
124
|
+
end
|
125
|
+
else
|
126
|
+
s.add_dependency(%q<liquid>, [">= 2.0.0"])
|
127
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.2"])
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module CML
|
2
|
+
module Converters
|
3
|
+
class JSAwesome
|
4
|
+
def initialize(json)
|
5
|
+
parsed = JSON.parse(json)
|
6
|
+
@json = parsed[0] || []
|
7
|
+
@labels = parsed[1] || {}
|
8
|
+
@tags = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def convert
|
12
|
+
@json.map do |k,v|
|
13
|
+
if k.is_a?(Array)
|
14
|
+
(k << v).map {|k,v| process(k,v)}
|
15
|
+
else
|
16
|
+
process(k,v)
|
17
|
+
end
|
18
|
+
end.join("\n") + meta
|
19
|
+
end
|
20
|
+
|
21
|
+
def meta
|
22
|
+
(@labels["gold"] || {}).select do |k,v|
|
23
|
+
next if k =~ /^_/
|
24
|
+
!@tags.map {|t| t.name }.include?(k)
|
25
|
+
end.map do |k,v|
|
26
|
+
tag("meta", k, {:meta => true})
|
27
|
+
end.compact.join("\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
def label(k)
|
31
|
+
k = k.sub(/^[_#*^~]?\+?/,'')
|
32
|
+
l = @labels[k]
|
33
|
+
label = l.is_a?(Hash) ? l["label"] : l if l
|
34
|
+
label ||= k.capitalize.gsub(/_/,' ')
|
35
|
+
end
|
36
|
+
|
37
|
+
def tag(tag, val, opts = {})
|
38
|
+
val = Array(val)
|
39
|
+
return nil unless val[0]
|
40
|
+
parts = val[0].split("|")
|
41
|
+
key = parts[0].sub(/^[_#*^~]?\+?/,'')
|
42
|
+
label = label(parts[0])
|
43
|
+
parts << key if label != key.capitalize.gsub(/_/,' ') || opts[:meta]
|
44
|
+
attrs = opts[:meta] ? [] : ["label=\"#{label}\""]
|
45
|
+
attrs << "#{opts[:nested] ? "value" : "name"}=\"#{parts[1]}\"" if parts[1]
|
46
|
+
attrs << "checked=\"true\"" if val[1]
|
47
|
+
attrs << "validates=\"required\"" if @labels[key] && @labels[key]["required"]
|
48
|
+
opts.reject {|k,v| v.to_s.strip.length == 0 || k.is_a?(Symbol) }.each do |k,v|
|
49
|
+
attrs << "#{k}=\"#{v}\""
|
50
|
+
end
|
51
|
+
if @labels["gold"] && @labels["gold"][key]
|
52
|
+
opts["gold"] = {}
|
53
|
+
src = @labels["gold"][key]
|
54
|
+
opts["gold"]["src"] = src
|
55
|
+
opts["gold"]["regex"], opts["gold"]["flags"] = Array(@labels["gold"]["_"+src+"_regex"])
|
56
|
+
opts["gold"]["strict"] = @labels["gold"]["_"+src+"_strict"]
|
57
|
+
else
|
58
|
+
opts["gold"] = false
|
59
|
+
end
|
60
|
+
|
61
|
+
tag = Parser.tag_class(tag).new("<cml:#{tag} #{attrs.join(" ")} />")
|
62
|
+
tag.gold = opts["gold"]
|
63
|
+
opts[:children].each { |c| tag.cml.add_child(tag(*c).cml) } if opts[:children]
|
64
|
+
@tags << tag if opts[:parent]
|
65
|
+
tag
|
66
|
+
end
|
67
|
+
|
68
|
+
def process(k,v)
|
69
|
+
select_default = @labels['{}'] || "Choose Category"
|
70
|
+
cml = case v
|
71
|
+
#a select tag or group of radios/checkboxes
|
72
|
+
when Array:
|
73
|
+
if(k =~ /^[^_]/)
|
74
|
+
if $~[0] == "*"
|
75
|
+
parent = "radios"
|
76
|
+
child = "radio"
|
77
|
+
elsif $~[0] == "^"
|
78
|
+
parent = "checkboxes"
|
79
|
+
child = "checkbox"
|
80
|
+
else
|
81
|
+
parent = "select"
|
82
|
+
child = "option"
|
83
|
+
end
|
84
|
+
tag(parent, k, {:children => v.map { |c| [child, c, {:nested => child}]}, :parent => parent })
|
85
|
+
#Multiple hidden fields...
|
86
|
+
elsif(k =~ /^_/)
|
87
|
+
v.map { |c| tag("hidden", c, :parent => true)}.join("\n")
|
88
|
+
end
|
89
|
+
else
|
90
|
+
if(k =~ /^#/)
|
91
|
+
tag("textarea", k, {"default" => v, :parent => true})
|
92
|
+
elsif(k =~ /^_/)
|
93
|
+
tag("hidden", k, {"value" => v, :parent => true})
|
94
|
+
elsif(k =~ /^\^/)
|
95
|
+
tag("checkbox", k, {"default" => v, :parent => true})
|
96
|
+
else
|
97
|
+
tag("text", k, {"default" => v, :parent => true})
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/cml/gold.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module CML
|
2
|
+
class Gold
|
3
|
+
Attrs = ["exact","strict","regex","structured","matcher"]
|
4
|
+
attr_reader *(Attrs + [:src, :name])
|
5
|
+
|
6
|
+
def initialize(tag, gold)
|
7
|
+
@name = tag.name(false)
|
8
|
+
@src = (gold.attributes["gold"] || gold.attributes["src"]).to_s
|
9
|
+
@src = "#{@name}_gold" if @src =~ /^(true|\s*)$/
|
10
|
+
Attrs.each do |attribute|
|
11
|
+
if a = gold.attributes[attribute]
|
12
|
+
value = case attribute
|
13
|
+
when "regex": [a.to_s, gold.attributes["flags"].to_s, gold.attributes["no_escape"].to_s]
|
14
|
+
when "matcher": a.to_s
|
15
|
+
else
|
16
|
+
a.to_s == "true"
|
17
|
+
end
|
18
|
+
instance_variable_set "@#{attribute}", value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@structured = true if tag.iterating?
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_hash
|
25
|
+
Hash[
|
26
|
+
*[@name,@src].concat(Attrs.map do |a|
|
27
|
+
val = instance_variable_get("@#{a}")
|
28
|
+
val ? ["_#{@src}_#{a}", val] : nil
|
29
|
+
end.compact.flatten(1))
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/cml/parser.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
module CML
|
2
|
+
class Parser
|
3
|
+
attr_reader :doc, :cftags, :tags, :errors
|
4
|
+
BASE_TAGS_XPATH = ".//cml:*[not(ancestor::cml:checkboxes or ancestor::cml:ratings or ancestor::cml:select or ancestor::cml:radios or self::cml:gold or self::cml:instructions)]"
|
5
|
+
|
6
|
+
def initialize(content, opts = {})
|
7
|
+
@opts = opts.merge(:parser => self)
|
8
|
+
if content.is_a?(String)
|
9
|
+
#Because nokogiri is munging my CDATA sections, we parse it out ahead of time
|
10
|
+
@cdata = content.scan(/(<(script|style)[^>]*?>)(.*?)(<\/\2>)/m)
|
11
|
+
@doc = Parser.parse(content)
|
12
|
+
else
|
13
|
+
@cdata = []
|
14
|
+
@doc = content
|
15
|
+
end
|
16
|
+
#Pull all cml tags that aren't children
|
17
|
+
@cftags = @doc.xpath(BASE_TAGS_XPATH, "xmlns:cml"=>"http://crowdflower.com")
|
18
|
+
normalize if opts[:normalize]
|
19
|
+
@tags = @cftags.map do |t|
|
20
|
+
t = Parser.tag_class(t.name).new(t, @opts)
|
21
|
+
end.flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse(content)
|
25
|
+
#This sucks, we remove scripts, styles, and close non self closed tags
|
26
|
+
#We could potentially add CDATA clauses to them, but this is "easier"
|
27
|
+
xhtml = content.gsub(/(<(script|style)[^>]*?>)(.*?)(<\/\2>)/m, "\\1\\4").gsub(/(<(input|link|img|br|hr).*?)\/?>/,'\1/>') #base, basefont, area, meta
|
28
|
+
Nokogiri::XML("<root xmlns:cml=\"http://crowdflower.com\">#{xhtml}</root>")
|
29
|
+
end
|
30
|
+
|
31
|
+
def convert(opts = nil)
|
32
|
+
@opts.merge!(opts) if opts
|
33
|
+
cloned = @doc.dup
|
34
|
+
cloned.xpath(BASE_TAGS_XPATH, "xmlns:cml"=>"http://crowdflower.com").each_with_index do |t,i|
|
35
|
+
t.namespace = nil
|
36
|
+
t.replace(@tags[i].convert(opts))
|
37
|
+
end
|
38
|
+
cloned
|
39
|
+
end
|
40
|
+
|
41
|
+
def normalize
|
42
|
+
@cftags.each do |t|
|
43
|
+
if ["radios", "select"].include?(t.name)
|
44
|
+
child = t.name == "radios" ? "radio" : "option"
|
45
|
+
t.name = "checkboxes"
|
46
|
+
t.xpath("cml:#{child}").each { |c| c.name = "checkbox" }
|
47
|
+
elsif t.name == "meta"
|
48
|
+
t.name = "text"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def fields
|
54
|
+
@fields = {}
|
55
|
+
@tags.each do |g|
|
56
|
+
agg = g.attrs["aggregation"]
|
57
|
+
@fields[g.name] = agg.to_s if agg
|
58
|
+
end
|
59
|
+
@fields
|
60
|
+
end
|
61
|
+
|
62
|
+
def golds(rich = false, opts={})
|
63
|
+
unless @golds
|
64
|
+
@golds = @tags.map do |tag|
|
65
|
+
next unless gold = tag.gold?
|
66
|
+
Gold.new(tag, gold)
|
67
|
+
end.compact
|
68
|
+
end
|
69
|
+
|
70
|
+
if rich
|
71
|
+
golds = @golds
|
72
|
+
else
|
73
|
+
golds = rich_gold_to_hash( @golds )
|
74
|
+
if opts[:no_meta]
|
75
|
+
golds.reject! { |k,v| k =~ /^_/ }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
golds
|
80
|
+
end
|
81
|
+
|
82
|
+
def rich_gold_to_hash(golds)
|
83
|
+
golds.inject({}) do |m, gold|
|
84
|
+
m.merge(gold.to_hash)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def valid?
|
89
|
+
@errors = []
|
90
|
+
if e = @doc.errors.select {|e| e.fatal? }.last
|
91
|
+
@errors << "Malformed CML (#{e.level}). #{e.message.chomp} on line #{e.line} column #{e.column}."
|
92
|
+
end
|
93
|
+
@tags.select {|t| t.validate? && t.name =~ /^\s*$/ }.each do |t|
|
94
|
+
@errors << "#{t.to_s.split("\n")[0]} does not have a label or name specified."
|
95
|
+
end
|
96
|
+
dupes = @tags.select do |tag|
|
97
|
+
tag.validate? && @tags.select {|t| t.name == tag.name}.length > 1
|
98
|
+
end
|
99
|
+
(dupes[1..-1] || []).each do |t|
|
100
|
+
@errors << "#{t.to_s.split("\n")[0]} has a duplicated name, please specify a unique name attribute."
|
101
|
+
end
|
102
|
+
@tags.each do |t|
|
103
|
+
next if !t.children || ["group","iterate"].include?(t.tag)
|
104
|
+
dupes = t.children.select do |child|
|
105
|
+
child.validate? && t.children.select {|c| c.value == child.value}.length > 1
|
106
|
+
end
|
107
|
+
(dupes[1..-1] || []).each do |c|
|
108
|
+
@errors << "#{c} a child of #{t.to_s.split("\n")[0]} has a duplicated value, please specify a unique value attribute."
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@errors.length == 0
|
112
|
+
end
|
113
|
+
|
114
|
+
def wrap(content)
|
115
|
+
#This happens when variables are inside of href's
|
116
|
+
content = content.gsub(/%7B%7B/,'{{').gsub(/%7D%7D/,'}}')
|
117
|
+
@opts[:no_wrap] ? content : "<div class=\"cml#{" "+@opts[:class] if @opts[:class]}\" id=\"#{@opts[:prefix]}\">#{content}</div>"
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
to_html
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_cml
|
125
|
+
cml = @doc.to_xhtml.gsub(/<\/?root[^>]*?>|<\/?>/,'').gsub(/(<(input|link|img|br|hr).*?)\/?>/,'\1/>').gsub(/%7B%7B/,'{{').gsub(/%7D%7D/,'}}')
|
126
|
+
#Hack to ensure the next sub doesn't match...
|
127
|
+
@cdata.each do |matches|
|
128
|
+
cml.sub!(/(<(script|style)[^>]*>)<\/\2>/m) do |m|
|
129
|
+
"#{$1}#{matches[2].empty? ? " " : matches[2]}</#{$2}>"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
cml
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_html(opts = nil)
|
136
|
+
#We convert the entire document and strip root tags / rando empty tags ALA libxml 2.6.32
|
137
|
+
#We're also adding self closing tags
|
138
|
+
html = convert(opts).to_xhtml.gsub(/<\/?(root|group)[^>]*?>|<\/?>/,'').gsub(/(<(input|link|img|br|hr).*?)\/?>/,'\1/>').gsub(/%7B%7B/,'{{').gsub(/%7D%7D/,'}}')
|
139
|
+
#Let's re-insert that CDATA, tricky because scripts will sometimes contain single quotes...
|
140
|
+
@cdata.each do |matches|
|
141
|
+
html.sub!(/(<(script|style)[^>]*>)<\/\2>/m) do |m|
|
142
|
+
"#{$1}#{matches[2].empty? ? " " : matches[2]}</#{$2}>"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
wrap(html)
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.escape( string )
|
149
|
+
string.to_s.gsub( /&/, "&" ).
|
150
|
+
gsub( /</, "<" ).
|
151
|
+
gsub( />/, ">" ).
|
152
|
+
gsub( /"/, """ )
|
153
|
+
end
|
154
|
+
|
155
|
+
#This takes the name of the tag and converts it to the appropriate tag class
|
156
|
+
def self.tag_class(name)
|
157
|
+
CML::TagClasses[name] || CML::Tags::Unknown
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|