typingpool 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/Rakefile +23 -0
  2. data/bin/tp-assign +240 -0
  3. data/bin/tp-collect +50 -0
  4. data/bin/tp-config +114 -0
  5. data/bin/tp-finish +101 -0
  6. data/bin/tp-make +169 -0
  7. data/bin/tp-review +175 -0
  8. data/lib/typingpool/amazon.rb +732 -0
  9. data/lib/typingpool/app.rb +634 -0
  10. data/lib/typingpool/config.rb +344 -0
  11. data/lib/typingpool/error.rb +22 -0
  12. data/lib/typingpool/filer.rb +396 -0
  13. data/lib/typingpool/project.rb +593 -0
  14. data/lib/typingpool/template.rb +175 -0
  15. data/lib/typingpool/templates/assignment/amazon-init.js +38 -0
  16. data/lib/typingpool/templates/assignment/interview/nameless.html.erb +13 -0
  17. data/lib/typingpool/templates/assignment/interview/noisy.html.erb +12 -0
  18. data/lib/typingpool/templates/assignment/interview/partials/voices.html.erb +10 -0
  19. data/lib/typingpool/templates/assignment/interview/phone.html.erb +12 -0
  20. data/lib/typingpool/templates/assignment/interview.html.erb +11 -0
  21. data/lib/typingpool/templates/assignment/main.css +20 -0
  22. data/lib/typingpool/templates/assignment/partials/entry.html.erb +19 -0
  23. data/lib/typingpool/templates/assignment/partials/footer.html.erb +3 -0
  24. data/lib/typingpool/templates/assignment/partials/header.html.erb +11 -0
  25. data/lib/typingpool/templates/assignment/partials/labeling-example.html.erb +4 -0
  26. data/lib/typingpool/templates/assignment/partials/labeling.html.erb +5 -0
  27. data/lib/typingpool/templates/assignment/partials/length-description.html.erb +6 -0
  28. data/lib/typingpool/templates/assignment/partials/voices.html.erb +10 -0
  29. data/lib/typingpool/templates/assignment/speech.html.erb +11 -0
  30. data/lib/typingpool/templates/config.yml +21 -0
  31. data/lib/typingpool/templates/project/audio/chunks/.empty_directory +0 -0
  32. data/lib/typingpool/templates/project/audio/originals/.empty_directory +0 -0
  33. data/lib/typingpool/templates/project/data/.empty_directory +0 -0
  34. data/lib/typingpool/templates/project/etc/ About these files - read me.txt +8 -0
  35. data/lib/typingpool/templates/project/etc/audio-compat.js +25 -0
  36. data/lib/typingpool/templates/project/etc/player/audio-player.js +4 -0
  37. data/lib/typingpool/templates/project/etc/player/license.txt +19 -0
  38. data/lib/typingpool/templates/project/etc/player/player.swf +0 -0
  39. data/lib/typingpool/templates/project/etc/transcript.css +49 -0
  40. data/lib/typingpool/templates/transcript.html.erb +23 -0
  41. data/lib/typingpool/test/fixtures/amazon-question-html.html +95 -0
  42. data/lib/typingpool/test/fixtures/amazon-question-url.txt +1 -0
  43. data/lib/typingpool/test/fixtures/audio/mp3/interview.1.mp3 +0 -0
  44. data/lib/typingpool/test/fixtures/audio/mp3/interview.2.mp3 +0 -0
  45. data/lib/typingpool/test/fixtures/audio/wma/VN620007.WMA +0 -0
  46. data/lib/typingpool/test/fixtures/audio/wma/VN620052.WMA +0 -0
  47. data/lib/typingpool/test/fixtures/config-1 +20 -0
  48. data/lib/typingpool/test/fixtures/config-2 +25 -0
  49. data/lib/typingpool/test/fixtures/not_yaml.txt +4 -0
  50. data/lib/typingpool/test/fixtures/template-2.html.erb +10 -0
  51. data/lib/typingpool/test/fixtures/template-3.html.erb +22 -0
  52. data/lib/typingpool/test/fixtures/template.html.erb +10 -0
  53. data/lib/typingpool/test/fixtures/tp_collect_id.txt +1 -0
  54. data/lib/typingpool/test/fixtures/tp_collect_sandbox-assignment.csv +8 -0
  55. data/lib/typingpool/test/fixtures/tp_review_id.txt +1 -0
  56. data/lib/typingpool/test/fixtures/tp_review_sandbox-assignment.csv +8 -0
  57. data/lib/typingpool/test/fixtures/transcript-chunks.csv +226 -0
  58. data/lib/typingpool/test/fixtures/utf8_transcript.txt +7 -0
  59. data/lib/typingpool/test/fixtures/vcr/tp-collect-1.yml +2712 -0
  60. data/lib/typingpool/test/fixtures/vcr/tp-collect-2.yml +2718 -0
  61. data/lib/typingpool/test/fixtures/vcr/tp-collect-3.yml +2768 -0
  62. data/lib/typingpool/test/fixtures/vcr/tp-review-1.yml +570 -0
  63. data/lib/typingpool/test/fixtures/vcr/tp-review-2.yml +351 -0
  64. data/lib/typingpool/test.rb +418 -0
  65. data/lib/typingpool/transcript.rb +181 -0
  66. data/lib/typingpool/utility.rb +272 -0
  67. data/lib/typingpool.rb +500 -0
  68. data/test/make_amazon_question_fixture.rb +24 -0
  69. data/test/make_tp_collect_fixture_1.rb +26 -0
  70. data/test/make_tp_collect_fixture_2.rb +16 -0
  71. data/test/make_tp_collect_fixture_3.rb +15 -0
  72. data/test/make_tp_collect_fixture_4.rb +17 -0
  73. data/test/make_tp_review_fixture_1.rb +26 -0
  74. data/test/make_tp_review_fixture_2.rb +30 -0
  75. data/test/make_transcript_chunks_fixture.rb +53 -0
  76. data/test/test_integration_script_1_tp_config.rb +108 -0
  77. data/test/test_integration_script_2_tp_make.rb +119 -0
  78. data/test/test_integration_script_3_tp_assign.rb +152 -0
  79. data/test/test_integration_script_4_tp_review.rb +72 -0
  80. data/test/test_integration_script_5_tp_collect.rb +44 -0
  81. data/test/test_integration_script_6_tp_finish.rb +123 -0
  82. data/test/test_unit_amazon.rb +153 -0
  83. data/test/test_unit_config.rb +94 -0
  84. data/test/test_unit_filer.rb +202 -0
  85. data/test/test_unit_project.rb +168 -0
  86. data/test/test_unit_project_local.rb +68 -0
  87. data/test/test_unit_project_remote.rb +157 -0
  88. data/test/test_unit_template.rb +111 -0
  89. data/test/test_unit_transcript.rb +77 -0
  90. metadata +234 -0
@@ -0,0 +1,175 @@
1
+ module Typingpool
2
+ #Model class that wraps ERB and adds a few Typingpool-specific
3
+ #capabilities: The ability to look in an array of search paths for a
4
+ #particular relative path, neccesary to support the Config#template
5
+ #dir on top of the built-in app template dir. Also makes it easy to
6
+ #pass in a hash and render the template against that hash, rather
7
+ #than against all the variables in the current namespace.
8
+ class Template
9
+ require 'erb'
10
+ class << self
11
+ #Constructor. Takes a relative template path and an optional
12
+ #config file. Default config is Config.file.
13
+ def from_config(path, config=Config.file)
14
+ validate_config(config)
15
+ new(path, look_in_from_config(config))
16
+ end
17
+
18
+ #private
19
+
20
+ def look_in_from_config(config)
21
+ look_in = [File.join(Utility.lib_dir, 'templates'), '']
22
+ look_in.unshift(config.templates) if config.templates
23
+ look_in
24
+ end
25
+
26
+ def validate_config(config)
27
+ if config.templates
28
+ File.exists?(config.templates) or raise Error::File::NotExists, "No such templates dir: #{config.templates}"
29
+ File.directory?(config.templates) or raise Error::File::NotExists, "Templates dir not a directory: #{config.templates}"
30
+ end
31
+ end
32
+ end #class << self
33
+
34
+ #An array of base paths to be searched when we're given a relative
35
+ #path to the template. Normally this includes the user's
36
+ #Config#template attribute, if any, followed by the built-in app
37
+ #template dir.
38
+ attr_reader :look_in
39
+
40
+ #Constructor. Takes a relative path and an array of base paths to
41
+ #search for relative template paths. See look_in docs. Template
42
+ #should be an ERB template.
43
+ def initialize(path, look_in)
44
+ @path = path
45
+ @look_in = look_in
46
+ full_path or raise Error, "Could not find template path '#{path}' in #{look_in.join(',')}"
47
+ end
48
+
49
+ #Takes a hash to pass to an ERB template and returns the text from
50
+ #rendering the template against that hash (the hash becomes the
51
+ #top-level namespace of the template, so the keys are accessed
52
+ #just as you'd normally access a variable in an ERB template).
53
+ def render(hash)
54
+ render_with_binding(Env.new(hash, self).get_binding)
55
+ end
56
+
57
+ #Like render, but takes a binding instead of hash
58
+ def render_with_binding(binding)
59
+ ERB.new(read, nil, '<>').result(binding)
60
+ end
61
+
62
+ #Returns the raw text of the template, unrendered.
63
+ def read
64
+ IO.read(full_path)
65
+ end
66
+
67
+ #Returns the path to the template after searching the various
68
+ #look_in dirs for the relative path. Returns nil if the template
69
+ #cannot be located.
70
+ def full_path
71
+ look_in.each do |dir|
72
+ extensions.each do |ext|
73
+ path = File.join(dir, [@path, ext].join)
74
+ if File.exists?(path) && File.file?(path)
75
+ return path
76
+ end
77
+ end
78
+ end
79
+ return
80
+ end
81
+
82
+ protected
83
+
84
+ def extensions
85
+ ['.html.erb', '.erb', '']
86
+ end
87
+
88
+
89
+ #A Template::Assignment works just like a regular template, except
90
+ #that within each transcript dir (Config#transcript and the
91
+ #built-in app template dir) we search within a subdir called
92
+ #'assignment' first, then, after all the 'assignment' subdirs have
93
+ #been search, we look in the original template dirs.
94
+ class Assignment < Template
95
+ def self.look_in_from_config(*args)
96
+ look_in = super(*args)
97
+ look_in.unshift(look_in.reject{|dir| dir.empty? }.map{|dir| File.join(dir, 'assignment') })
98
+ look_in.flatten
99
+ end
100
+ end #Assignment
101
+
102
+ #This subclass provides two utility methods to all templates:
103
+ #read, for including the text of another template, and render, for
104
+ #rendering another template. Read takes a relative path,
105
+ #documented below. Render is passed the same hash as the parent
106
+ #template, merged with an optional override hash, as documented
107
+ #below.
108
+ #
109
+ #This subclass also makes it easier to use a hash as the top-level
110
+ #variable namespace when rendering ERB templates.
111
+ class Env
112
+
113
+ #Construtor. Takes a hash to be passed to the template and a
114
+ #template (ERB).
115
+ def initialize(hash, template)
116
+ @hash = hash
117
+ @template = template
118
+ end
119
+
120
+ #Method passed into each template. Takes a relative path and
121
+ #returns the text of the file at that path.
122
+ #
123
+ #The relative path is resolved as in look_in above, with the
124
+ #following difference: the current directory and each parent
125
+ #directory of the active template is searched first, up to the
126
+ #root transcript directory (either Config#template, the built-in
127
+ #app template dir, or any dir that has been manually added to
128
+ #look_in).
129
+ def read(path)
130
+ @template.class.new(path, localized_look_in).read.strip
131
+ end
132
+
133
+ #Method passed into each template. Takes a reltive path and
134
+ #returns the *rendered* text of the ERB template at that
135
+ #path. Can also take an optional hash, which will be merged into
136
+ #the parent template's hash and passed to the included
137
+ #template. If the optional hash it not passed, the parent
138
+ #template's hash will be passed to the included template
139
+ #unmodified.
140
+ #
141
+ #The relative path is resolved as described in the docs for
142
+ #Template::Env#read.
143
+ def render(path, hash={})
144
+ @template.class.new(path, localized_look_in).render(@hash.merge(hash)).strip
145
+ end
146
+
147
+ def get_binding
148
+ binding()
149
+ end
150
+
151
+ protected
152
+
153
+ def localized_look_in
154
+ look_in = []
155
+ path = @template.full_path
156
+ until @template.look_in.include? path = File.dirname(path)
157
+ look_in.push(path)
158
+ end
159
+ look_in.push(path, (@template.look_in - [path])).flatten
160
+ end
161
+
162
+ def method_missing(key, value=nil)
163
+ if value
164
+ key = key.to_s.sub(/=$/, '')
165
+ @hash[key.to_sym] = value
166
+ end
167
+ if @hash.has_key? key
168
+ @hash[key]
169
+ elsif @hash.has_key? key.to_s
170
+ @hash[key.to_s]
171
+ end
172
+ end
173
+ end #Env
174
+ end #Template
175
+ end #Typingpool
@@ -0,0 +1,38 @@
1
+
2
+ function setAmazonAssignmentId() {
3
+ var params = window.location.search.substring(1).split('&');
4
+ var i = 0;
5
+ for (i = 0; i < params.length; i++) {
6
+ var param = params[i].split('=');
7
+ var key = param[0];
8
+ var value = param[1];
9
+ if (key === 'assignmentId') {
10
+ if (value === 'ASSIGNMENT_ID_NOT_AVAILABLE') {
11
+ var input = document.getElementById('disable_on_preview');
12
+ if (input) {
13
+ input.setAttribute('disabled', 'disabled');
14
+ var span = document.createElement('span');
15
+ span.setAttribute('class', 'disabled_message');
16
+ span.appendChild(document.createTextNode(' Disabled because you are previewing this HIT.'));
17
+ var inputSibling = input.nextSibling;
18
+ if (inputSibling) {
19
+ input.parentNode.insertBefore(span, inputSibling);
20
+ }
21
+ else {
22
+ input.parentNode.appendChild(span);
23
+ }
24
+ }
25
+ }
26
+ else {
27
+ document.getElementById('assignmentId').value = value;
28
+ }
29
+ }
30
+ else if (key === 'turkSubmitTo') {
31
+ var form = document.getElementById('turkForm');
32
+ if (form) {
33
+ form.setAttribute('action', decodeURIComponent(value) + '/mturk/externalSubmit');
34
+ }
35
+ }
36
+ }
37
+ }
38
+
@@ -0,0 +1,13 @@
1
+ <% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' interview' %>
2
+ <%= render 'partials/header' %>
3
+ <p id="description">I split up an interview into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
4
+
5
+ <h3>Labeling</h3>
6
+ <p>Label each speaker with a gender and, if needed, a number. Use the labels "MALE:", "FEMALE:", "MALE 2:", "FEMALE 2:", "MALE 3:", "FEMALE 3:", etc., like this:
7
+
8
+ <% self.voice1 = 'MALE' %><% self.voice2 = 'MALE 2' %>
9
+ <%= render 'partials/labeling-example' %>
10
+
11
+ <%= render 'partials/entry' %>
12
+
13
+ <%= render 'partials/footer' %>
@@ -0,0 +1,12 @@
1
+ <% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' interview (background noise)' %>
2
+ <%= render 'partials/header' %>
3
+ <p id="description">I split up an interview into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part. There is background noise.</p>
4
+
5
+ <%= render 'partials/voices' %>
6
+
7
+ <%= render 'partials/labeling' %>
8
+
9
+ <% self.important = ['The interview takes place in a noisy environment with audible chatter. You should transcribe <strong>only</strong> the comments from the people involved in the interview.'] %>
10
+ <%= render 'partials/entry' %>
11
+
12
+ <%= render 'partials/footer' %>
@@ -0,0 +1,10 @@
1
+ <% if voices_count > 0 %>
2
+ <h3>Names</h3>
3
+ <p>There are <%= voices_count %> speakers:
4
+ <ul>
5
+ <% 1.upto(voices_count) do |i| %>
6
+ <li><strong><%= send("voice#{i}") %></strong><% if send("voice#{i}title").to_s.empty? %><% if i == 1 %>, the person asking the questions<% end %><% else %>, <%= send("voice#{i}title") %><% end %>.
7
+ <% end %>
8
+ </ul>
9
+ <% end %>
10
+
@@ -0,0 +1,12 @@
1
+ <% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' phone call' %>
2
+ <%= render 'partials/header' %>
3
+
4
+ <p id="description">I split up a telephone conversation into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
5
+
6
+ <h3>Labeling</h3>
7
+ <p>There are two speakers. The person asking the questions is <strong><%= voice1 %></strong>. The person answering the questions, on the distant end of the telephone line, is <strong><%= voice2 %></strong><% unless voice2title.to_s.empty? %>, <%= voice2title %><% end %>. Label each speaker with a colon, like this:</p>
8
+ <%= render 'partials/labeling-example' %>
9
+
10
+ <%= render 'partials/entry' %>
11
+
12
+ <%= render 'partials/footer' %>
@@ -0,0 +1,11 @@
1
+ <% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' interview' %>
2
+ <%= render 'partials/header' %>
3
+ <p id="description">I split up an interview into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
4
+
5
+ <%= render 'interview/partials/voices' %>
6
+
7
+ <%= render 'partials/labeling' %>
8
+
9
+ <%= render 'partials/entry' %>
10
+
11
+ <%= render 'partials/footer' %>
@@ -0,0 +1,20 @@
1
+
2
+ body {
3
+ color: black;
4
+ background: white;
5
+ font-family: Georgia, Times, "Times New Roman", serif;
6
+ }
7
+
8
+ h1, h2, h3, h4, h5, .disabled_message {
9
+ font-family: Helvetica, Arial, sans-serif;
10
+ }
11
+
12
+ p {
13
+ line-height: 140%;
14
+ }
15
+
16
+ .disabled_message {
17
+ color: gray;
18
+ font-size: small;
19
+ }
20
+
@@ -0,0 +1,19 @@
1
+ <h3>Important</h3>
2
+ <ul>
3
+ <li><strong>Unusual words</strong> you might hear: <strong><%= unusual %></strong></li>
4
+ <li><strong>Don't</strong> type short or filler responses like <strong>uh</strong>, <strong>you know</strong>, <strong>I mean</strong>, <strong>ya</strong>, <strong>OK</strong>, <strong>Right</strong>, <strong>I see</strong>, <strong>Oh</strong>, and <strong>Ah</strong>.</li>
5
+ <li>For <strong>inaudible</strong> words, type <strong>??</strong></li>
6
+ <% if important %>
7
+ <% important.each do |note| %>
8
+ <li><%= note %></li>
9
+ <% end %>
10
+ <% end %>
11
+ </ul>
12
+
13
+ <h3>Instructions</h3>
14
+ <p>Please <strong>transcribe</strong> this <%= render 'length-description' %> MP3: <a target="_blank" href="<%= audio_url %>"><%= audio_url %></a></p>
15
+ <form action="https://www.mturk.com/mturk/externalSubmit" method="POST" id="turkForm">
16
+ <p><input name="assignmentId" type="hidden" id="assignmentId"><input name="typingpool_url" type="hidden" value="<%= audio_url %>" /><input name="typingpool_project_id" type="hidden" value="<%= project_id %>" />Enter your transcription below:</p>
17
+ <p><textarea name="transcription" cols="80" rows="15"></textarea></p>
18
+ <p><input type="submit" value="Submit" id="disable_on_preview"></p>
19
+ </form>
@@ -0,0 +1,3 @@
1
+
2
+ </body>
3
+ </html>
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <script><%= read 'amazon-init.js' %></script>
6
+ <style><%= read 'main.css' %></style>
7
+ <title><%= title %></title>
8
+ </head>
9
+
10
+ <body onload="setAmazonAssignmentId()">
11
+ <h2><%= title %></h2>
@@ -0,0 +1,4 @@
1
+ <blockquote>
2
+ <p><%= voice1 %>:&nbsp;Do you think it was a mistake to set up Saturn as its own Corporation?</p>
3
+ <p><%= voice2 %>:&nbsp;In hindsight, you can look at it that way, and I think a lot of people do, quite honestly. They don't think GM needed another brand. But at the time this was all developed. It sure seemed like a good idea.</p>
4
+ </blockquote>
@@ -0,0 +1,5 @@
1
+
2
+ <h3>Labeling</h3>
3
+ <p>Label each speaker with a colon, like this:</p>
4
+ <% voice1 ||= 'Male 1' %><% voice2 ||= 'Male 2' %>
5
+ <%= render 'labeling-example' %>
@@ -0,0 +1,6 @@
1
+ <% length_description = [] %>
2
+ <% length_description.push("#{chunk_hours} hour") if chunk_hours %>
3
+ <% length_description.push("#{chunk_minutes} minute") if chunk_minutes %>
4
+ <% length_description.push("#{chunk_seconds} second") if chunk_seconds %>
5
+ <% length_description = length_description.join(' ') %>
6
+ <%= length_description %>
@@ -0,0 +1,10 @@
1
+ <% if voices_count > 0 %>
2
+ <h3>Names</h3>
3
+ <p>There are <%= voices_count %> speakers:
4
+ <ul>
5
+ <% 1.upto(voices_count) do |i| %>
6
+ <li><strong><%= send("voice#{i}") %></strong><% unless send("voice#{i}title").to_s.empty? %>, <%= send("voice#{i}title") %><% end %>.
7
+ <% end %>
8
+ </ul>
9
+ <% end %>
10
+
@@ -0,0 +1,11 @@
1
+ <% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' speech' %>
2
+
3
+ <%= render 'partials/header' %>
4
+ <p id="description">I split up a speech into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
5
+
6
+ <h3>Formatting</h3>
7
+ <p>There should only be one speaker. Simply type what the person says. No need to label. If the speaker changes, type [SPEAKER CHANGES].</p>
8
+
9
+ <%= render 'partials/entry' %>
10
+
11
+ <%= render 'partials/footer' %>
@@ -0,0 +1,21 @@
1
+ amazon:
2
+ key:
3
+ secret:
4
+ bucket:
5
+ transcripts:
6
+ templates:
7
+ cache: ~/.typingpool.cache
8
+ assign:
9
+ confirm: yes
10
+ reward: 0.75
11
+ deadline: 3h
12
+ approval: 1d
13
+ lifetime: 2d
14
+ qualify:
15
+ - approval_rate >= 95
16
+ - hits_approved >= 100
17
+ keywords:
18
+ - transcription
19
+ - audio
20
+ - mp3
21
+
@@ -0,0 +1,8 @@
1
+ Deleting any files in this folder, in the data folder, or in the
2
+ audio/chunks folder will make it impossible for typingpool to function
3
+ properly.
4
+
5
+ You may safely delete files in the audio/originals folder.
6
+
7
+ You may safely edit transcript.html, etc/transcript.css, and
8
+ data/subtitle.txt.
@@ -0,0 +1,25 @@
1
+ function audioCompat() {
2
+ var audioTag = window.document.createElement('audio');
3
+ //Check for HTML5 audio tag compatibility
4
+ if (!(audioTag.canPlayType && audioTag.canPlayType('audio/mpeg') && (audioTag.canPlayType('audio/mpeg') != 'no'))){
5
+ //not compatible - fallback to Flash
6
+ AudioPlayer.setup("etc/player/player.swf", { width: 290 });
7
+ var audioTags = window.document.getElementsByTagName('audio');
8
+ var audioTagMeta = [];
9
+ for (var i = 0; i < audioTags.length; i++) {
10
+ var tag = audioTags[i];
11
+ if (! tag.id){
12
+ tag.id = '_typingpool_audio_' + i;
13
+ }
14
+ audioTagMeta.push({'id':tag.id, 'src':tag.src});
15
+ }
16
+ for (var i = 0; i < audioTagMeta.length; i++) {
17
+ var tagMeta = audioTagMeta[i];
18
+ AudioPlayer.embed(tagMeta.id, {'soundFile': tagMeta.src, 'noinfo': 'yes'});
19
+ }
20
+ }
21
+ }
22
+
23
+ if (window.addEventListener){ window.addEventListener('load', audioCompat, false) }
24
+ else if (document.addEventListener){ document.addEventListener('load', audioCompat, false) }
25
+ else if (window.attachEvent){ window.attachEvent('onload', audioCompat) }
@@ -0,0 +1,4 @@
1
+ /* SWFObject v2.2 <http://code.google.com/p/swfobject/>
2
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
3
+ */
4
+ var audioplayer_swfobject=function(){var d="undefined",R="object",s="Shockwave Flash",w="ShockwaveFlash.ShockwaveFlash",Q="application/x-shockwave-flash",r="SWFObjectExprInst",X="onreadystatechange",o=window,J=document,T=navigator,t=false,u=[H],O=[],n=[],i=[],L,q,e,b,j=false,A=false,N,g,M=true,m=function(){var AA=typeof J.getElementById!=d&&typeof J.getElementsByTagName!=d&&typeof J.createElement!=d,AH=T.userAgent.toLowerCase(),y=T.platform.toLowerCase(),AE=y?/win/.test(y):/win/.test(AH),AC=y?/mac/.test(y):/mac/.test(AH),AF=/webkit/.test(AH)?parseFloat(AH.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,x=!+"\v1",AG=[0,0,0],AB=null;if(typeof T.plugins!=d&&typeof T.plugins[s]==R){AB=T.plugins[s].description;if(AB&&!(typeof T.mimeTypes!=d&&T.mimeTypes[Q]&&!T.mimeTypes[Q].enabledPlugin)){t=true;x=false;AB=AB.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AG[0]=parseInt(AB.replace(/^(.*)\..*$/,"$1"),10);AG[1]=parseInt(AB.replace(/^.*\.(.*)\s.*$/,"$1"),10);AG[2]=/[a-zA-Z]/.test(AB)?parseInt(AB.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof o.ActiveXObject!=d){try{var AD=new ActiveXObject(w);if(AD){AB=AD.GetVariable("$version");if(AB){x=true;AB=AB.split(" ")[1].split(",");AG=[parseInt(AB[0],10),parseInt(AB[1],10),parseInt(AB[2],10)]}}}catch(z){}}}return{w3:AA,pv:AG,wk:AF,ie:x,win:AE,mac:AC}}(),K=function(){if(!m.w3){return }if((typeof J.readyState!=d&&J.readyState=="complete")||(typeof J.readyState==d&&(J.getElementsByTagName("body")[0]||J.body))){F()}if(!j){if(typeof J.addEventListener!=d){J.addEventListener("DOMContentLoaded",F,false)}if(m.ie&&m.win){J.attachEvent(X,function(){if(J.readyState=="complete"){J.detachEvent(X,arguments.callee);F()}});if(o==top){(function(){if(j){return }try{J.documentElement.doScroll("left")}catch(x){setTimeout(arguments.callee,0);return }F()})()}}if(m.wk){(function(){if(j){return }if(!/loaded|complete/.test(J.readyState)){setTimeout(arguments.callee,0);return }F()})()}S(F)}}();function F(){if(j){return }try{var z=J.getElementsByTagName("body")[0].appendChild(c("span"));z.parentNode.removeChild(z)}catch(AA){return }j=true;var x=u.length;for(var y=0;y<x;y++){u[y]()}}function k(x){if(j){x()}else{u[u.length]=x}}function S(y){if(typeof o.addEventListener!=d){o.addEventListener("load",y,false)}else{if(typeof J.addEventListener!=d){J.addEventListener("load",y,false)}else{if(typeof o.attachEvent!=d){I(o,"onload",y)}else{if(typeof o.onload=="function"){var x=o.onload;o.onload=function(){x();y()}}else{o.onload=y}}}}}function H(){if(t){v()}else{h()}}function v(){var x=J.getElementsByTagName("body")[0];var AA=c(R);AA.setAttribute("type",Q);var z=x.appendChild(AA);if(z){var y=0;(function(){if(typeof z.GetVariable!=d){var AB=z.GetVariable("$version");if(AB){AB=AB.split(" ")[1].split(",");m.pv=[parseInt(AB[0],10),parseInt(AB[1],10),parseInt(AB[2],10)]}}else{if(y<10){y++;setTimeout(arguments.callee,10);return }}x.removeChild(AA);z=null;h()})()}else{h()}}function h(){var AG=O.length;if(AG>0){for(var AF=0;AF<AG;AF++){var y=O[AF].id;var AB=O[AF].callbackFn;var AA={success:false,id:y};if(m.pv[0]>0){var AE=C(y);if(AE){if(f(O[AF].swfVersion)&&!(m.wk&&m.wk<312)){W(y,true);if(AB){AA.success=true;AA.ref=Z(y);AB(AA)}}else{if(O[AF].expressInstall&&a()){var AI={};AI.data=O[AF].expressInstall;AI.width=AE.getAttribute("width")||"0";AI.height=AE.getAttribute("height")||"0";if(AE.getAttribute("class")){AI.styleclass=AE.getAttribute("class")}if(AE.getAttribute("align")){AI.align=AE.getAttribute("align")}var AH={};var x=AE.getElementsByTagName("param");var AC=x.length;for(var AD=0;AD<AC;AD++){if(x[AD].getAttribute("name").toLowerCase()!="movie"){AH[x[AD].getAttribute("name")]=x[AD].getAttribute("value")}}p(AI,AH,y,AB)}else{P(AE);if(AB){AB(AA)}}}}}else{W(y,true);if(AB){var z=Z(y);if(z&&typeof z.SetVariable!=d){AA.success=true;AA.ref=z}AB(AA)}}}}}function Z(AA){var x=null;var y=C(AA);if(y&&y.nodeName=="OBJECT"){if(typeof y.SetVariable!=d){x=y}else{var z=y.getElementsByTagName(R)[0];if(z){x=z}}}return x}function a(){return !A&&f("6.0.65")&&(m.win||m.mac)&&!(m.wk&&m.wk<312)}function p(AA,AB,x,z){A=true;e=z||null;b={success:false,id:x};var AE=C(x);if(AE){if(AE.nodeName=="OBJECT"){L=G(AE);q=null}else{L=AE;q=x}AA.id=r;if(typeof AA.width==d||(!/%$/.test(AA.width)&&parseInt(AA.width,10)<310)){AA.width="310"}if(typeof AA.height==d||(!/%$/.test(AA.height)&&parseInt(AA.height,10)<137)){AA.height="137"}J.title=J.title.slice(0,47)+" - Flash Player Installation";var AD=m.ie&&m.win?"ActiveX":"PlugIn",AC="MMredirectURL="+o.location.toString().replace(/&/g,"%26")+"&MMplayerType="+AD+"&MMdoctitle="+J.title;if(typeof AB.flashvars!=d){AB.flashvars+="&"+AC}else{AB.flashvars=AC}if(m.ie&&m.win&&AE.readyState!=4){var y=c("div");x+="SWFObjectNew";y.setAttribute("id",x);AE.parentNode.insertBefore(y,AE);AE.style.display="none";(function(){if(AE.readyState==4){AE.parentNode.removeChild(AE)}else{setTimeout(arguments.callee,10)}})()}U(AA,AB,x)}}function P(y){if(m.ie&&m.win&&y.readyState!=4){var x=c("div");y.parentNode.insertBefore(x,y);x.parentNode.replaceChild(G(y),x);y.style.display="none";(function(){if(y.readyState==4){y.parentNode.removeChild(y)}else{setTimeout(arguments.callee,10)}})()}else{y.parentNode.replaceChild(G(y),y)}}function G(AB){var AA=c("div");if(m.win&&m.ie){AA.innerHTML=AB.innerHTML}else{var y=AB.getElementsByTagName(R)[0];if(y){var AC=y.childNodes;if(AC){var x=AC.length;for(var z=0;z<x;z++){if(!(AC[z].nodeType==1&&AC[z].nodeName=="PARAM")&&!(AC[z].nodeType==8)){AA.appendChild(AC[z].cloneNode(true))}}}}}return AA}function U(AI,AG,y){var x,AA=C(y);if(m.wk&&m.wk<312){return x}if(AA){if(typeof AI.id==d){AI.id=y}if(m.ie&&m.win){var AH="";for(var AE in AI){if(AI[AE]!=Object.prototype[AE]){if(AE.toLowerCase()=="data"){AG.movie=AI[AE]}else{if(AE.toLowerCase()=="styleclass"){AH+=' class="'+AI[AE]+'"'}else{if(AE.toLowerCase()!="classid"){AH+=" "+AE+'="'+AI[AE]+'"'}}}}}var AF="";for(var AD in AG){if(AG[AD]!=Object.prototype[AD]){AF+='<param name="'+AD+'" value="'+AG[AD]+'" />'}}AA.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+AH+">"+AF+"</object>";n[n.length]=AI.id;x=C(AI.id)}else{var z=c(R);z.setAttribute("type",Q);for(var AC in AI){if(AI[AC]!=Object.prototype[AC]){if(AC.toLowerCase()=="styleclass"){z.setAttribute("class",AI[AC])}else{if(AC.toLowerCase()!="classid"){z.setAttribute(AC,AI[AC])}}}}for(var AB in AG){if(AG[AB]!=Object.prototype[AB]&&AB.toLowerCase()!="movie"){E(z,AB,AG[AB])}}AA.parentNode.replaceChild(z,AA);x=z}}return x}function E(z,x,y){var AA=c("param");AA.setAttribute("name",x);AA.setAttribute("value",y);z.appendChild(AA)}function Y(y){var x=C(y);if(x&&x.nodeName=="OBJECT"){if(m.ie&&m.win){x.style.display="none";(function(){if(x.readyState==4){B(y)}else{setTimeout(arguments.callee,10)}})()}else{x.parentNode.removeChild(x)}}}function B(z){var y=C(z);if(y){for(var x in y){if(typeof y[x]=="function"){y[x]=null}}y.parentNode.removeChild(y)}}function C(z){var x=null;try{x=J.getElementById(z)}catch(y){}return x}function c(x){return J.createElement(x)}function I(z,x,y){z.attachEvent(x,y);i[i.length]=[z,x,y]}function f(z){var y=m.pv,x=z.split(".");x[0]=parseInt(x[0],10);x[1]=parseInt(x[1],10)||0;x[2]=parseInt(x[2],10)||0;return(y[0]>x[0]||(y[0]==x[0]&&y[1]>x[1])||(y[0]==x[0]&&y[1]==x[1]&&y[2]>=x[2]))?true:false}function V(AC,y,AD,AB){if(m.ie&&m.mac){return }var AA=J.getElementsByTagName("head")[0];if(!AA){return }var x=(AD&&typeof AD=="string")?AD:"screen";if(AB){N=null;g=null}if(!N||g!=x){var z=c("style");z.setAttribute("type","text/css");z.setAttribute("media",x);N=AA.appendChild(z);if(m.ie&&m.win&&typeof J.styleSheets!=d&&J.styleSheets.length>0){N=J.styleSheets[J.styleSheets.length-1]}g=x}if(m.ie&&m.win){if(N&&typeof N.addRule==R){N.addRule(AC,y)}}else{if(N&&typeof J.createTextNode!=d){N.appendChild(J.createTextNode(AC+" {"+y+"}"))}}}function W(z,x){if(!M){return }var y=x?"visible":"hidden";if(j&&C(z)){C(z).style.visibility=y}else{V("#"+z,"visibility:"+y)}}function l(y){var z=/[\\\"<>\.;]/;var x=z.exec(y)!=null;return x&&typeof encodeURIComponent!=d?encodeURIComponent(y):y}var D=function(){if(m.ie&&m.win){window.attachEvent("onunload",function(){var AC=i.length;for(var AB=0;AB<AC;AB++){i[AB][0].detachEvent(i[AB][1],i[AB][2])}var z=n.length;for(var AA=0;AA<z;AA++){Y(n[AA])}for(var y in m){m[y]=null}m=null;for(var x in audioplayer_swfobject){audioplayer_swfobject[x]=null}audioplayer_swfobject=null})}}();return{registerObject:function(AB,x,AA,z){if(m.w3&&AB&&x){var y={};y.id=AB;y.swfVersion=x;y.expressInstall=AA;y.callbackFn=z;O[O.length]=y;W(AB,false)}else{if(z){z({success:false,id:AB})}}},getObjectById:function(x){if(m.w3){return Z(x)}},embedSWF:function(AB,AH,AE,AG,y,AA,z,AD,AF,AC){var x={success:false,id:AH};if(m.w3&&!(m.wk&&m.wk<312)&&AB&&AH&&AE&&AG&&y){W(AH,false);k(function(){AE+="";AG+="";var AJ={};if(AF&&typeof AF===R){for(var AL in AF){AJ[AL]=AF[AL]}}AJ.data=AB;AJ.width=AE;AJ.height=AG;var AM={};if(AD&&typeof AD===R){for(var AK in AD){AM[AK]=AD[AK]}}if(z&&typeof z===R){for(var AI in z){if(typeof AM.flashvars!=d){AM.flashvars+="&"+AI+"="+z[AI]}else{AM.flashvars=AI+"="+z[AI]}}}if(f(y)){var AN=U(AJ,AM,AH);if(AJ.id==AH){W(AH,true)}x.success=true;x.ref=AN}else{if(AA&&a()){AJ.data=AA;p(AJ,AM,AH,AC);return }else{W(AH,true)}}if(AC){AC(x)}})}else{if(AC){AC(x)}}},switchOffAutoHideShow:function(){M=false},ua:m,getFlashPlayerVersion:function(){return{major:m.pv[0],minor:m.pv[1],release:m.pv[2]}},hasFlashPlayerVersion:f,createSWF:function(z,y,x){if(m.w3){return U(z,y,x)}else{return undefined}},showExpressInstall:function(z,AA,x,y){if(m.w3&&a()){p(z,AA,x,y)}},removeSWF:function(x){if(m.w3){Y(x)}},createCSS:function(AA,z,y,x){if(m.w3){V(AA,z,y,x)}},addDomLoadEvent:k,addLoadEvent:S,getQueryParamValue:function(AA){var z=J.location.search||J.location.hash;if(z){if(/\?/.test(z)){z=z.split("?")[1]}if(AA==null){return l(z)}var y=z.split("&");for(var x=0;x<y.length;x++){if(y[x].substring(0,y[x].indexOf("="))==AA){return l(y[x].substring((y[x].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(A){var x=C(r);if(x&&L){x.parentNode.replaceChild(L,x);if(q){W(q,true);if(m.ie&&m.win){L.style.display="block"}}if(e){e(b)}}A=false}}}}();var AudioPlayer=function(){var H=[];var D;var F="";var A={};var E=-1;var G="9";function B(I){if(document.all&&!window[I]){for(var J=0;J<document.forms.length;J++){if(document.forms[J][I]){return document.forms[J][I];break}}}return document.all?window[I]:document[I]}function C(I,J,K){B(I).addListener(J,K)}return{setup:function(J,I){F=J;A=I;if(audioplayer_swfobject.hasFlashPlayerVersion(G)){audioplayer_swfobject.switchOffAutoHideShow();audioplayer_swfobject.createCSS("p.audioplayer_container span","visibility:hidden;height:24px;overflow:hidden;padding:0;border:none;")}},getPlayer:function(I){return B(I)},addListener:function(I,J,K){C(I,J,K)},embed:function(I,K){var N={};var L;var J={};var O={};var M={};for(L in A){N[L]=A[L]}for(L in K){N[L]=K[L]}if(N.transparentpagebg=="yes"){J.bgcolor="#FFFFFF";J.wmode="transparent"}else{if(N.pagebg){J.bgcolor="#"+N.pagebg}J.wmode="opaque"}J.menu="false";for(L in N){if(L=="pagebg"||L=="width"||L=="transparentpagebg"){continue}O[L]=N[L]}M.name=I;M.style="outline: none";O.playerID=I;audioplayer_swfobject.embedSWF(F,I,N.width.toString(),"24",G,false,O,J,M);H.push(I)},syncVolumes:function(I,K){E=K;for(var J=0;J<H.length;J++){if(H[J]!=I){B(H[J]).setVolume(E)}}},activate:function(I,J){if(D&&D!=I){B(D).close()}D=I},load:function(K,I,L,J){B(K).load(I,L,J)},close:function(I){B(I).close();if(I==D){D=null}},open:function(I,J){if(J==undefined){J=1}B(I).open(J==undefined?0:J-1)},getVolume:function(I){return E}}}();
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Martin Laine
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,49 @@
1
+ body {
2
+ color: black;
3
+ background: white;
4
+ font-family: Georgia, Times, "Times New Roman", serif;
5
+ }
6
+
7
+ h1, h2, h3, h4, h5 {
8
+ font-family: Helvetica, Arial, sans-serif;
9
+ font-weight: normal;
10
+ }
11
+
12
+ h1{
13
+ margin-bottom: 0.33em
14
+ }
15
+
16
+ h2{
17
+ font-size: 1.125em;
18
+ margin-top: 0.125em;
19
+ }
20
+
21
+ h3, h4 {
22
+ color:gray;
23
+ font-size: 0.875em;
24
+ }
25
+
26
+ h3{
27
+ margin-top: 0.5em;
28
+ margin-bottom: 0.125em;
29
+ line-height: auto;
30
+ }
31
+
32
+ h4{
33
+ margin-top: 0.125em;
34
+ margin-bottom: 0.125em;
35
+ }
36
+
37
+ p{
38
+ margin-top: 0.125em;
39
+ margin-bottom: 2em;
40
+ line-height: 140%;
41
+ }
42
+
43
+ a[href], a[href]:link {
44
+ color: gray;
45
+ }
46
+
47
+ a[href]:visited {
48
+ color: silver;
49
+ }
@@ -0,0 +1,23 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title><%= transcript.title %> transcript</title>
6
+ <link rel="stylesheet" href="etc/transcript.css">
7
+ <script src="etc/player/audio-player.js" type="text/javascript"></script>
8
+ <script src="etc/audio-compat.js" type="text/javascript"></script>
9
+ </head>
10
+ <body>
11
+ <h1><%= transcript.title %> transcript</h1>
12
+ <h2><% if transcript.subtitle %><%= transcript.subtitle %><% end %></h2>
13
+
14
+ <% transcript.sort.each do |chunk| %>
15
+
16
+ <h3><%= chunk.offset %></h3>
17
+ <h3><audio src="audio/chunks/<%= chunk.filename_local %>" preload="none" controls></audio></h3>
18
+ <h4><a href="https://requester.mturk.com/bulk/workers/<%= chunk.worker %>">Worker</a> | <a href="https://requester.mturk.com/mturk/manageHIT?HITId=<%= chunk.hit %>&amp;viewableEditPane=manageHIT_downloadResults">HIT</a></h4>
19
+
20
+ <p><%= chunk.body_as_html %>
21
+ <% end %>
22
+ </body>
23
+ </html>