ajax-cat 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/ajax-cat.gemspec +72 -0
- data/lib/.DS_Store +0 -0
- data/lib/ajax-cat.rb +5 -0
- data/lib/public/AjaxCatList.coffee +63 -0
- data/lib/public/AjaxCatTranslation.coffee +147 -0
- data/lib/public/Suggestions.coffee +87 -0
- data/lib/public/TranslationTable.coffee +98 -0
- data/lib/public/Utils.coffee +46 -0
- data/lib/public/ajax-cat.coffee +2 -0
- data/lib/public/ajax-cat.js +628 -0
- data/lib/public/bootstrap.css +3990 -0
- data/lib/public/bootstrap.js +1726 -0
- data/lib/public/index.html +150 -0
- data/lib/public/index.js +3 -0
- data/lib/public/jquery.js +4 -0
- data/lib/public/style.css +97 -0
- data/lib/public/translation.html +102 -0
- data/lib/public/translation.js +3 -0
- metadata +25 -8
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/ajax-cat.gemspec
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
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 = "ajax-cat"
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ondrej Odchazel"]
|
12
|
+
s.date = "2012-04-26"
|
13
|
+
s.description = "computer-aided translation backed by machine translation"
|
14
|
+
s.email = "odchazel@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"ajax-cat.gemspec",
|
28
|
+
"lib/.DS_Store",
|
29
|
+
"lib/ajax-cat.rb",
|
30
|
+
"lib/public/AjaxCatList.coffee",
|
31
|
+
"lib/public/AjaxCatTranslation.coffee",
|
32
|
+
"lib/public/Suggestions.coffee",
|
33
|
+
"lib/public/TranslationTable.coffee",
|
34
|
+
"lib/public/Utils.coffee",
|
35
|
+
"lib/public/ajax-cat.coffee",
|
36
|
+
"lib/public/ajax-cat.js",
|
37
|
+
"lib/public/bootstrap.css",
|
38
|
+
"lib/public/bootstrap.js",
|
39
|
+
"lib/public/index.html",
|
40
|
+
"lib/public/index.js",
|
41
|
+
"lib/public/jquery.js",
|
42
|
+
"lib/public/style.css",
|
43
|
+
"lib/public/translation.html",
|
44
|
+
"lib/public/translation.js",
|
45
|
+
"test/helper.rb",
|
46
|
+
"test/test_ajax-cat.rb"
|
47
|
+
]
|
48
|
+
s.homepage = "http://github.com/hypertornado/ajax-cat"
|
49
|
+
s.licenses = ["MIT"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = "1.8.17"
|
52
|
+
s.summary = "computer-aided translation backed by machine translation"
|
53
|
+
|
54
|
+
if s.respond_to? :specification_version then
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
59
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
60
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
61
|
+
else
|
62
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
63
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
68
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
69
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
data/lib/.DS_Store
ADDED
Binary file
|
data/lib/ajax-cat.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
class AjaxCatList
|
2
|
+
|
3
|
+
constructor: ->
|
4
|
+
$('#new-translation-modal').hide()
|
5
|
+
$('#new-translation').on('click',@new_translation)
|
6
|
+
$('#create-new-translation').on('click',@create_new_translation)
|
7
|
+
@show_translations()
|
8
|
+
$(".example").click(
|
9
|
+
(event) =>
|
10
|
+
text = $(event.currentTarget).data("text")
|
11
|
+
$('#new-translation-text').val(text)
|
12
|
+
return false
|
13
|
+
)
|
14
|
+
|
15
|
+
new_translation: =>
|
16
|
+
$('#new-translation-name').val('Name')
|
17
|
+
$('#new-translation-text').val('')
|
18
|
+
$('#new-translation-modal').modal('show')
|
19
|
+
$('#new-translation-text').focus()
|
20
|
+
|
21
|
+
show_translations: =>
|
22
|
+
$("#translation-list").html('')
|
23
|
+
return unless localStorage['ac-data']
|
24
|
+
ids = JSON.parse(localStorage['ac-data'])
|
25
|
+
for i in ids
|
26
|
+
doc = JSON.parse(localStorage[i])
|
27
|
+
$("#translation-list").append("<tr><td width='100%'><a href='/translation.html##{doc.id}'>#{doc.name}</a></td><td><button data-id='#{doc.id}' class='btn btn-danger btn-mini delete-button'>delete</button></td></tr>")
|
28
|
+
$(".delete-button").click(
|
29
|
+
(event) =>
|
30
|
+
id = $(event.currentTarget).data("id")
|
31
|
+
if confirm("Delete this translation?")
|
32
|
+
@delete_document(id)
|
33
|
+
@show_translations()
|
34
|
+
)
|
35
|
+
|
36
|
+
delete_document: (id) =>
|
37
|
+
return unless localStorage['ac-data']
|
38
|
+
ids = JSON.parse(localStorage['ac-data'])
|
39
|
+
new_ids = []
|
40
|
+
for i in ids
|
41
|
+
new_ids.push(i) unless i == id
|
42
|
+
localStorage.removeItem(id)
|
43
|
+
localStorage.setItem('ac-data', JSON.stringify(new_ids))
|
44
|
+
|
45
|
+
|
46
|
+
create_new_translation: =>
|
47
|
+
text = $('#new-translation-text').val()
|
48
|
+
if localStorage['ac-data']
|
49
|
+
docs = JSON.parse(localStorage['ac-data'])
|
50
|
+
else
|
51
|
+
docs = []
|
52
|
+
doc = {}
|
53
|
+
doc.id = Utils.random_string()
|
54
|
+
doc['name'] = $('#new-translation-name').val()
|
55
|
+
doc['pair'] = $('#new-translation-pair').val()
|
56
|
+
doc.source = Utils.split_source(text)
|
57
|
+
doc.target = new Array(doc.source.length)
|
58
|
+
docs.push(doc.id)
|
59
|
+
localStorage.setItem('ac-data', JSON.stringify(docs))
|
60
|
+
localStorage.setItem(doc.id, JSON.stringify(doc))
|
61
|
+
$('#new-translation-modal').modal('hide')
|
62
|
+
@show_translations()
|
63
|
+
|
@@ -0,0 +1,147 @@
|
|
1
|
+
class AjaxCatTranslation
|
2
|
+
|
3
|
+
cur_position: false
|
4
|
+
pair: "en-cs"
|
5
|
+
#host: "10.10.24.118"
|
6
|
+
host: "localhost"
|
7
|
+
|
8
|
+
constructor: ->
|
9
|
+
@host = window.location.hostname
|
10
|
+
@suggestions = new Suggestions(@)
|
11
|
+
$('#translation-preview-modal').hide()
|
12
|
+
$('#myTab').tab('show')
|
13
|
+
hash = window.location.hash.substr(1)
|
14
|
+
data = localStorage.getItem(hash)
|
15
|
+
unless data
|
16
|
+
alert "No such document."
|
17
|
+
return
|
18
|
+
@doc = JSON.parse(data)
|
19
|
+
@pair = @doc.pair
|
20
|
+
$('h1').html("#{@doc.name} <small>#{@pair}</small>")
|
21
|
+
$("title").html("#{@doc.name} - AJAX-CAT")
|
22
|
+
i = 0
|
23
|
+
for s in @doc.source
|
24
|
+
$("#source-top").append("<span class='ac-element ac-source' data-position='#{i}'>#{s}</span>")
|
25
|
+
$("#source-bottom").append("<span class='ac-element ac-source' data-position='#{i}'>#{s}</span>")
|
26
|
+
i += 1
|
27
|
+
i = 0
|
28
|
+
for t in @doc.target
|
29
|
+
t = "" unless t
|
30
|
+
$("#target-top").append("<span class='ac-element ac-target' data-position='#{i}'>#{t}</span>")
|
31
|
+
$("#target-bottom").append("<span class='ac-element ac-target' data-position='#{i}'>#{t}</span>")
|
32
|
+
i += 1
|
33
|
+
$('.ac-element').click(
|
34
|
+
(event) =>
|
35
|
+
position = $(event.target).data('position')
|
36
|
+
@change_position(position)
|
37
|
+
)
|
38
|
+
@length = $("#source-top").children().length
|
39
|
+
@change_position(0)
|
40
|
+
@bind_events()
|
41
|
+
@resize()
|
42
|
+
|
43
|
+
resize: =>
|
44
|
+
width = $(window).width()
|
45
|
+
$("#translation-table-container").width(width - 60)
|
46
|
+
|
47
|
+
bind_events: =>
|
48
|
+
$("#target-sentence").on('keydown'
|
49
|
+
(event) =>
|
50
|
+
switch event.which
|
51
|
+
when 13 #enter
|
52
|
+
@suggestions.take_suggestion()
|
53
|
+
when 32 #space
|
54
|
+
text = $("#target-sentence").val()
|
55
|
+
text = Utils.trim text
|
56
|
+
if text.length > 0
|
57
|
+
ar = text.split(/[ ]+/)
|
58
|
+
word = ar[(ar.length - 1)]
|
59
|
+
@table.mark_words(word)
|
60
|
+
when 33 #pgup
|
61
|
+
@change_position(@cur_position - 1) if @cur_position > 0
|
62
|
+
return false
|
63
|
+
when 34 #pgdown
|
64
|
+
@change_position(@cur_position + 1) if (@cur_position + 1) < @length
|
65
|
+
return false
|
66
|
+
when 38 #up
|
67
|
+
@suggestions.up()
|
68
|
+
when 40 #down
|
69
|
+
@suggestions.down()
|
70
|
+
else
|
71
|
+
$(window).trigger('loadSuggestions')
|
72
|
+
|
73
|
+
)
|
74
|
+
|
75
|
+
$("#preview").click(
|
76
|
+
=>
|
77
|
+
@save_target()
|
78
|
+
@show_preview()
|
79
|
+
return false
|
80
|
+
)
|
81
|
+
|
82
|
+
$("#save").click(
|
83
|
+
=>
|
84
|
+
@save_target()
|
85
|
+
alert "Translation was saved into your browser."
|
86
|
+
)
|
87
|
+
|
88
|
+
show_preview: =>
|
89
|
+
$("#source").text(@doc.source.join(''))
|
90
|
+
$("#target").text(@doc.target.join(''))
|
91
|
+
$('#translation-preview-modal').modal('show')
|
92
|
+
|
93
|
+
save_target: =>
|
94
|
+
$("#target-top .ac-target[data-position=#{@cur_position}]").text($("#target-sentence").val())
|
95
|
+
$("#target-bottom .ac-target[data-position=#{@cur_position}]").text($("#target-sentence").val())
|
96
|
+
|
97
|
+
tar = []
|
98
|
+
for el in $("#target-top .ac-target")
|
99
|
+
tar.push($(el).text())
|
100
|
+
@doc.target = tar
|
101
|
+
localStorage.setItem(@doc.id, JSON.stringify(@doc))
|
102
|
+
|
103
|
+
|
104
|
+
load_translation_table: (sentence) =>
|
105
|
+
sentence = Utils.tokenize(sentence)
|
106
|
+
if sentence.match(/^[\ \t]*$/)
|
107
|
+
$("#translation-table-container").html("")
|
108
|
+
@suggestions.clear()
|
109
|
+
return
|
110
|
+
$("#translation-table-container").text("")
|
111
|
+
$.ajax "http://"+@host+":8888/table"
|
112
|
+
data:
|
113
|
+
pair: @pair
|
114
|
+
q: sentence
|
115
|
+
success: (data) =>
|
116
|
+
@table = new TranslationTable(@, data)
|
117
|
+
$("#translation-table-container").html(@table.get_table())
|
118
|
+
$(window).trigger('loadSuggestions')
|
119
|
+
error: =>
|
120
|
+
#alert "failed to load translation table"
|
121
|
+
|
122
|
+
change_position: (position) =>
|
123
|
+
@save_target() if @cur_position != false
|
124
|
+
$("#source-top").children().slice(0,position).show()
|
125
|
+
$("#source-top").children().slice(position,@length).hide()
|
126
|
+
$("#source-bottom").children().slice(0,position + 1).hide()
|
127
|
+
$("#source-bottom").children().slice(position + 1,@length).show()
|
128
|
+
|
129
|
+
$("#target-top").children().slice(0,position).show()
|
130
|
+
$("#target-top").children().slice(position,@length).hide()
|
131
|
+
$("#target-bottom").children().slice(0,position + 1).hide()
|
132
|
+
$("#target-bottom").children().slice(position + 1,@length).show()
|
133
|
+
|
134
|
+
$("#source-sentence").text($("#source-top .ac-source[data-position=#{position}]").text())
|
135
|
+
$("#target-sentence").val($("#target-top .ac-target[data-position=#{position}]").text())
|
136
|
+
@cur_position = position
|
137
|
+
$("#target-sentence").focus()
|
138
|
+
@load_translation_table($("#source-top .ac-source[data-position=#{position}]").text())
|
139
|
+
|
140
|
+
add_words: (words, change_covered = false) =>
|
141
|
+
text = $("#target-sentence").val()
|
142
|
+
text = Utils.trim(text)
|
143
|
+
words = Utils.trim(words)
|
144
|
+
text = Utils.trim(text)
|
145
|
+
text += " #{words} "
|
146
|
+
$("#target-sentence").val(text)
|
147
|
+
$("#target-sentence").click()
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class Suggestions
|
2
|
+
|
3
|
+
constructor: (@translation, limit = 5) ->
|
4
|
+
i = 0
|
5
|
+
while i < limit
|
6
|
+
sug = $("<div>"
|
7
|
+
class: 'ac-suggestion'
|
8
|
+
'data-suggestion-position': i
|
9
|
+
click: =>
|
10
|
+
@take_suggestion()
|
11
|
+
mouseover: (event) =>
|
12
|
+
target = $(event.currentTarget)
|
13
|
+
if target.hasClass('suggestion-enabled')
|
14
|
+
@set_position(target.data('suggestion-position'))
|
15
|
+
)
|
16
|
+
$("#suggestions-container").append(sug)
|
17
|
+
i += 1
|
18
|
+
|
19
|
+
$(window).bind('loadSuggestions'
|
20
|
+
=>
|
21
|
+
@load_suggestions()
|
22
|
+
$("#target-sentence").focus()
|
23
|
+
)
|
24
|
+
|
25
|
+
clear: =>
|
26
|
+
$(".ac-suggestion").text("")
|
27
|
+
$(".ac-suggestion").removeClass('suggestion-enabled')
|
28
|
+
$(".ac-suggestion").removeClass('suggestion-active')
|
29
|
+
|
30
|
+
load_suggestions: =>
|
31
|
+
@clear()
|
32
|
+
sentence = $("#source-sentence").text()
|
33
|
+
sentence = Utils.tokenize(sentence)
|
34
|
+
translated = Utils.tokenize($("#source-target").text())
|
35
|
+
covered = @translation.table.covered_vector()
|
36
|
+
$.ajax "http://"+@translation.host+":8888/suggestion"
|
37
|
+
data:
|
38
|
+
pair: @translation.pair
|
39
|
+
q: Utils.tokenize(sentence)
|
40
|
+
covered: covered
|
41
|
+
translated: translated
|
42
|
+
success: (data) =>
|
43
|
+
data = JSON.parse(data)
|
44
|
+
@process_suggestions(data)
|
45
|
+
error: =>
|
46
|
+
#alert "failed to load suggestions"
|
47
|
+
|
48
|
+
take_suggestion: =>
|
49
|
+
return if (@get_position() == false)
|
50
|
+
text = $(".suggestion-active span").text()
|
51
|
+
@translation.add_words(text)
|
52
|
+
@translation.table.mark_words(text)
|
53
|
+
|
54
|
+
|
55
|
+
process_suggestions: (data) =>
|
56
|
+
i = 0
|
57
|
+
for suggestion in data.suggestions
|
58
|
+
translation = $("#target-sentence").val()
|
59
|
+
el = $(".ac-suggestion").slice(i, (i + 1))
|
60
|
+
el.html("#{translation} <span>#{suggestion}</span>")
|
61
|
+
el.addClass('suggestion-enabled')
|
62
|
+
i += 1
|
63
|
+
|
64
|
+
positions: =>
|
65
|
+
return $(".suggestion-enabled").length
|
66
|
+
|
67
|
+
get_position: =>
|
68
|
+
el = $(".suggestion-active")
|
69
|
+
return false unless el.length
|
70
|
+
return el.data('suggestion-position')
|
71
|
+
|
72
|
+
set_position: (position) =>
|
73
|
+
$(".suggestion-active").removeClass('suggestion-active')
|
74
|
+
$(".suggestion-enabled[data-suggestion-position=#{position}]").addClass('suggestion-active')
|
75
|
+
|
76
|
+
up: =>
|
77
|
+
position = @get_position()
|
78
|
+
return if position == false
|
79
|
+
@set_position(position - 1)
|
80
|
+
|
81
|
+
down: =>
|
82
|
+
position = @get_position()
|
83
|
+
if position == false
|
84
|
+
@set_position(0)
|
85
|
+
return
|
86
|
+
if (position + 1) < @positions()
|
87
|
+
@set_position(position + 1)
|
@@ -0,0 +1,98 @@
|
|
1
|
+
class TranslationTable
|
2
|
+
|
3
|
+
constructor: (@translation, data) ->
|
4
|
+
@data = JSON.parse(data)
|
5
|
+
|
6
|
+
get_table: =>
|
7
|
+
ret = $("<table>"
|
8
|
+
class: 'translation-table'
|
9
|
+
)
|
10
|
+
ret.append(@get_header())
|
11
|
+
for row in @data.target
|
12
|
+
ret.append(@get_row(row))
|
13
|
+
return ret
|
14
|
+
|
15
|
+
get_header: =>
|
16
|
+
ret = $("<tr>")
|
17
|
+
i = 0
|
18
|
+
for word in @data.source
|
19
|
+
src = $("<th>"
|
20
|
+
class: 'ac-source'
|
21
|
+
html: word
|
22
|
+
"data-position": i
|
23
|
+
click: (event) =>
|
24
|
+
position = parseInt($(event.currentTarget).data('position'))
|
25
|
+
if @position_marked(position)
|
26
|
+
@unmark_position(position)
|
27
|
+
else
|
28
|
+
@mark_position(position)
|
29
|
+
$(window).trigger('loadSuggestions')
|
30
|
+
)
|
31
|
+
ret.append(src)
|
32
|
+
i += 1
|
33
|
+
return ret
|
34
|
+
|
35
|
+
get_row: (row) =>
|
36
|
+
ret = $("<tr>")
|
37
|
+
i = 0
|
38
|
+
for word in row
|
39
|
+
len = parseInt(word.s)
|
40
|
+
if word.empty
|
41
|
+
ret.append("<td colspan='#{word.s}' class='ac-empty'></td>")
|
42
|
+
else
|
43
|
+
cell = $("<td colspan='#{word.s}' class='ac-word'></td>")
|
44
|
+
content = $("<div>"
|
45
|
+
'data-position-from': i
|
46
|
+
'data-position-to': (i + len - 1)
|
47
|
+
html: word.t
|
48
|
+
click: (event) =>
|
49
|
+
i = parseInt($(event.currentTarget).data('position-from'))
|
50
|
+
while i <= parseInt($(event.currentTarget).data('position-to'))
|
51
|
+
@mark_position(i)
|
52
|
+
i += 1
|
53
|
+
@translation.add_words($(event.currentTarget).text())
|
54
|
+
$(window).trigger('loadSuggestions')
|
55
|
+
)
|
56
|
+
cell.append(content)
|
57
|
+
ret.append(cell)
|
58
|
+
i += len
|
59
|
+
return ret
|
60
|
+
|
61
|
+
position_marked: (position) =>
|
62
|
+
return true if $("th.ac-source").slice(position, (position + 1)).hasClass('ac-selected')
|
63
|
+
return false
|
64
|
+
|
65
|
+
mark_words: (words) =>
|
66
|
+
words = "#{words} "
|
67
|
+
for el in $(".ac-word div")
|
68
|
+
el_text = Utils.trim $(el).text()
|
69
|
+
el_text = "#{el_text} "
|
70
|
+
if ((words.indexOf(el_text) == 0) and (words.length >= el_text.length))
|
71
|
+
#console.log "#{words}, #{el_text}, #{words.length} , #{el_text.length}"
|
72
|
+
@mark_interval($(el).data('position-from'), $(el).data('position-to'))
|
73
|
+
$(window).trigger('loadSuggestions')
|
74
|
+
words = words.substr(el_text.length)
|
75
|
+
words = Utils.trim(words)
|
76
|
+
words = "#{words} "
|
77
|
+
return unless words.length > 0
|
78
|
+
|
79
|
+
mark_interval: (from, to) =>
|
80
|
+
i = from
|
81
|
+
while i <= to
|
82
|
+
@mark_position(i)
|
83
|
+
i += 1
|
84
|
+
|
85
|
+
mark_position: (position) =>
|
86
|
+
$("th.ac-source").slice(position, (position + 1)).addClass('ac-selected')
|
87
|
+
|
88
|
+
unmark_position: (position) =>
|
89
|
+
$("th.ac-source").slice(position, (position + 1)).removeClass('ac-selected')
|
90
|
+
|
91
|
+
covered_vector: =>
|
92
|
+
ret = ""
|
93
|
+
for el in $("th.ac-source")
|
94
|
+
if $(el).hasClass('ac-selected')
|
95
|
+
ret += "1"
|
96
|
+
else
|
97
|
+
ret += "0"
|
98
|
+
return ret
|