ajax-cat 1.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -1
- data/Gemfile.lock +4 -3
- data/README.rdoc +1 -1
- data/VERSION +1 -1
- data/ajax-cat.gemspec +10 -5
- data/ajax-cat_old.ini.json +15 -0
- data/lib/ajax-cat.rb +5 -0
- data/lib/ajax-cat/ajax_cat_server.rb +39 -16
- data/lib/ajax-cat/pairs.rb +3 -1
- data/lib/ajax-cat/public/AjaxCatList.coffee +11 -3
- data/lib/ajax-cat/public/AjaxCatTranslation.coffee +36 -12
- data/lib/ajax-cat/public/Suggestions.coffee +12 -3
- data/lib/ajax-cat/public/TranslationTable.coffee +20 -1
- data/lib/ajax-cat/public/Utils.coffee +16 -0
- data/lib/ajax-cat/public/ajax-cat.js +111 -16
- data/lib/ajax-cat/public/cookie.js +72 -0
- data/lib/ajax-cat/public/index.html +1 -1
- data/lib/ajax-cat/public/translation.html +13 -7
- data/lib/ajax-cat/request/suggestion.rb +12 -2
- data/lib/ajax-cat/views/admin.erb +65 -16
- data/lib/ajax-cat/views/experiment.erb +62 -7
- data/test/unit/test_suggestion_request.rb +18 -6
- metadata +39 -26
@@ -44,3 +44,19 @@ class Utils
|
|
44
44
|
|
45
45
|
@trim: (text) =>
|
46
46
|
return text.replace(/^\s+|\s+$/g, "")
|
47
|
+
|
48
|
+
@edit_distance: (source, target) ->
|
49
|
+
a = []
|
50
|
+
for i in [0..source.length]
|
51
|
+
a[i] = new Array(target.length + 1)
|
52
|
+
a[i][0] = i
|
53
|
+
a[0] = [0..target.length]
|
54
|
+
|
55
|
+
for i in [1..source.length]
|
56
|
+
for j in [1..target.length]
|
57
|
+
substitute_cost = a[i - 1][j - 1]
|
58
|
+
substitute_cost += 1 if (source.charAt(i - 1) != target.charAt(j - 1))
|
59
|
+
a[i][j] = Math.min(substitute_cost, a[i-1][j] + 1, a[i][j-1] + 1)
|
60
|
+
return a[source.length][target.length]
|
61
|
+
|
62
|
+
|
@@ -40,6 +40,7 @@ AjaxCatList = (function() {
|
|
40
40
|
alert("Write your email, please.");
|
41
41
|
return;
|
42
42
|
}
|
43
|
+
$.cookie("email", email);
|
43
44
|
return $.ajax("/admin/get_experiment", {
|
44
45
|
data: {
|
45
46
|
email: email,
|
@@ -48,7 +49,7 @@ AjaxCatList = (function() {
|
|
48
49
|
success: function(data) {
|
49
50
|
var id;
|
50
51
|
data = JSON.parse(data);
|
51
|
-
id = _this.add_translation(data.
|
52
|
+
id = _this.add_translation(JSON.parse(data.sentences), "EXPERIMENT #" + data.task_id + ", " + data.email, data.pair, data.task_id, data.email, data);
|
52
53
|
return window.location = "/translation.html#" + id;
|
53
54
|
},
|
54
55
|
error: function() {
|
@@ -60,6 +61,7 @@ AjaxCatList = (function() {
|
|
60
61
|
AjaxCatList.prototype.new_experiment_translation = function() {
|
61
62
|
var _this = this;
|
62
63
|
$("#new-experiment-pair").html("");
|
64
|
+
$("#new-experiment-email").val($.cookie("email"));
|
63
65
|
return $.ajax("/api/info", {
|
64
66
|
success: function(data) {
|
65
67
|
var p, _i, _len, _ref;
|
@@ -138,7 +140,7 @@ AjaxCatList = (function() {
|
|
138
140
|
return this.show_translations();
|
139
141
|
};
|
140
142
|
|
141
|
-
AjaxCatList.prototype.add_translation = function(text, name, pair, task_id, email) {
|
143
|
+
AjaxCatList.prototype.add_translation = function(text, name, pair, task_id, email, experiment_data) {
|
142
144
|
var doc, docs;
|
143
145
|
if (task_id == null) task_id = false;
|
144
146
|
if (email == null) email = false;
|
@@ -153,7 +155,12 @@ AjaxCatList = (function() {
|
|
153
155
|
doc.pair = pair;
|
154
156
|
doc.email = email;
|
155
157
|
doc.task_id = task_id;
|
156
|
-
|
158
|
+
if (jQuery.isArray(text)) {
|
159
|
+
doc.source = text;
|
160
|
+
doc.options = JSON.parse(experiment_data.options);
|
161
|
+
} else {
|
162
|
+
doc.source = Utils.split_source(text);
|
163
|
+
}
|
157
164
|
doc.target = new Array(doc.source.length);
|
158
165
|
docs.push(doc.id);
|
159
166
|
localStorage.setItem('ac-data', JSON.stringify(docs));
|
@@ -169,6 +176,10 @@ AjaxCatTranslation = (function() {
|
|
169
176
|
|
170
177
|
AjaxCatTranslation.prototype.cur_position = false;
|
171
178
|
|
179
|
+
AjaxCatTranslation.prototype.experiment = false;
|
180
|
+
|
181
|
+
AjaxCatTranslation.prototype.param_suggestion = true;
|
182
|
+
|
172
183
|
function AjaxCatTranslation() {
|
173
184
|
this.add_words = __bind(this.add_words, this);
|
174
185
|
this.change_position = __bind(this.change_position, this);
|
@@ -179,6 +190,7 @@ AjaxCatTranslation = (function() {
|
|
179
190
|
this.resize = __bind(this.resize, this);
|
180
191
|
this.log = __bind(this.log, this);
|
181
192
|
this.prepare_test = __bind(this.prepare_test, this);
|
193
|
+
this.change_experiment_sentence = __bind(this.change_experiment_sentence, this);
|
182
194
|
this.time = __bind(this.time, this);
|
183
195
|
var data, i, s, t, _i, _j, _len, _len2, _ref, _ref2,
|
184
196
|
_this = this;
|
@@ -235,16 +247,16 @@ AjaxCatTranslation = (function() {
|
|
235
247
|
return setTimeout(this.time, 10);
|
236
248
|
};
|
237
249
|
|
238
|
-
AjaxCatTranslation.prototype.
|
250
|
+
AjaxCatTranslation.prototype.change_experiment_sentence = function() {
|
239
251
|
var _this = this;
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
$("#send-experiment").click(function() {
|
252
|
+
if (this.cur_position + 1 < this.doc.source.length) {
|
253
|
+
return this.change_position(this.cur_position + 1);
|
254
|
+
} else {
|
244
255
|
$("#send-experiment").hide();
|
245
256
|
return $.ajax("/admin/save_experiment", {
|
246
257
|
data: {
|
247
|
-
|
258
|
+
"log_id": this.doc.task_id,
|
259
|
+
log: JSON.stringify(this.doc)
|
248
260
|
},
|
249
261
|
type: "post",
|
250
262
|
success: function() {
|
@@ -255,6 +267,20 @@ AjaxCatTranslation = (function() {
|
|
255
267
|
return alert("Could not save experiment.");
|
256
268
|
}
|
257
269
|
});
|
270
|
+
}
|
271
|
+
};
|
272
|
+
|
273
|
+
AjaxCatTranslation.prototype.prepare_test = function() {
|
274
|
+
var _this = this;
|
275
|
+
AjaxCatList.delete_document(this.hash);
|
276
|
+
this.experiment = true;
|
277
|
+
$("#save").hide();
|
278
|
+
$("#experiment-settings").show();
|
279
|
+
$("#top-translations").hide();
|
280
|
+
$("#bottom-translations").hide();
|
281
|
+
$("#send-experiment").show();
|
282
|
+
$("#send-experiment").click(function() {
|
283
|
+
return _this.change_experiment_sentence();
|
258
284
|
});
|
259
285
|
this.doc.log = [];
|
260
286
|
this.time();
|
@@ -273,7 +299,10 @@ AjaxCatTranslation = (function() {
|
|
273
299
|
};
|
274
300
|
if (type) new_log.type = type;
|
275
301
|
if (param) new_log.param = param;
|
276
|
-
this.doc.log.
|
302
|
+
if (this.doc.log[this.cur_position] === void 0) {
|
303
|
+
this.doc.log[this.cur_position] = [];
|
304
|
+
}
|
305
|
+
this.doc.log[this.cur_position].push(new_log);
|
277
306
|
return $("#log").append(JSON.stringify(new_log) + "<br>");
|
278
307
|
};
|
279
308
|
|
@@ -357,6 +386,7 @@ AjaxCatTranslation = (function() {
|
|
357
386
|
|
358
387
|
AjaxCatTranslation.prototype.load_translation_table = function(sentence) {
|
359
388
|
var _this = this;
|
389
|
+
if (this.table_request) this.table_request.abort();
|
360
390
|
sentence = Utils.tokenize(sentence);
|
361
391
|
if (sentence.match(/^[\ \t]*$/)) {
|
362
392
|
$("#translation-table-container").html("");
|
@@ -364,7 +394,7 @@ AjaxCatTranslation = (function() {
|
|
364
394
|
return;
|
365
395
|
}
|
366
396
|
$("#translation-table-container").text("");
|
367
|
-
return $.ajax("/api/table", {
|
397
|
+
return this.table_request = $.ajax("/api/table", {
|
368
398
|
data: {
|
369
399
|
pair: this.pair,
|
370
400
|
q: sentence
|
@@ -379,6 +409,18 @@ AjaxCatTranslation = (function() {
|
|
379
409
|
};
|
380
410
|
|
381
411
|
AjaxCatTranslation.prototype.change_position = function(position) {
|
412
|
+
if (this.experiment) {
|
413
|
+
if (!((position === 0 && this.cur_position === false) || (this.cur_position + 1 === position))) {
|
414
|
+
return;
|
415
|
+
}
|
416
|
+
this.param_suggestion = this.doc.options[position].suggestion;
|
417
|
+
$("#suggestion-panel-is-on").text(this.param_suggestion);
|
418
|
+
$("#translated-status").text("translating sentence " + (position + 1) + " out of " + this.doc.source.length);
|
419
|
+
if ((position + 1) === this.doc.source.length) {
|
420
|
+
$("#send-experiment").text("Finish experiment");
|
421
|
+
}
|
422
|
+
}
|
423
|
+
this.suggestions.clear();
|
382
424
|
if (this.cur_position !== false) this.save_target();
|
383
425
|
$("#source-top").children().slice(0, position).show();
|
384
426
|
$("#source-top").children().slice(position, this.length).hide();
|
@@ -453,6 +495,7 @@ Suggestions = (function() {
|
|
453
495
|
}
|
454
496
|
|
455
497
|
Suggestions.prototype.clear = function() {
|
498
|
+
if (this.suggestion_request) this.suggestion_request.abort();
|
456
499
|
$(".ac-suggestion").text("");
|
457
500
|
$(".ac-suggestion").removeClass('suggestion-enabled');
|
458
501
|
return $(".ac-suggestion").removeClass('suggestion-active');
|
@@ -462,11 +505,12 @@ Suggestions = (function() {
|
|
462
505
|
var covered, sentence, translated,
|
463
506
|
_this = this;
|
464
507
|
this.clear();
|
508
|
+
if (!this.translation.param_suggestion) return;
|
465
509
|
sentence = $("#source-sentence").text();
|
466
510
|
sentence = Utils.tokenize(sentence);
|
467
511
|
translated = Utils.tokenize($("#source-target").text());
|
468
512
|
covered = this.translation.table.covered_vector();
|
469
|
-
return $.ajax("/api/suggestion", {
|
513
|
+
return this.suggestion_request = $.ajax("/api/suggestion", {
|
470
514
|
data: {
|
471
515
|
pair: this.translation.pair,
|
472
516
|
q: Utils.tokenize(sentence),
|
@@ -482,11 +526,13 @@ Suggestions = (function() {
|
|
482
526
|
};
|
483
527
|
|
484
528
|
Suggestions.prototype.take_suggestion = function() {
|
485
|
-
var text;
|
529
|
+
var from, text, to;
|
486
530
|
if (this.get_position() === false) return;
|
487
531
|
text = $(".suggestion-active span").text();
|
532
|
+
from = $(".suggestion-active").data('from');
|
533
|
+
to = $(".suggestion-active").data('to');
|
488
534
|
this.translation.add_words(text);
|
489
|
-
return this.translation.table.
|
535
|
+
return this.translation.table.mark_interval(from, to);
|
490
536
|
};
|
491
537
|
|
492
538
|
Suggestions.prototype.process_suggestions = function(data) {
|
@@ -498,7 +544,9 @@ Suggestions = (function() {
|
|
498
544
|
suggestion = _ref[_i];
|
499
545
|
translation = $("#target-sentence").val();
|
500
546
|
el = $(".ac-suggestion").slice(i, i + 1);
|
501
|
-
el.html("" + translation + " <span>" + suggestion + "</span>");
|
547
|
+
el.html("" + translation + " <span>" + suggestion.text + "</span>");
|
548
|
+
el.data('from', suggestion.from);
|
549
|
+
el.data('to', suggestion.to);
|
502
550
|
el.addClass('suggestion-enabled');
|
503
551
|
_results.push(i += 1);
|
504
552
|
}
|
@@ -550,6 +598,7 @@ TranslationTable = (function() {
|
|
550
598
|
this.unmark_position = __bind(this.unmark_position, this);
|
551
599
|
this.mark_position = __bind(this.mark_position, this);
|
552
600
|
this.mark_interval = __bind(this.mark_interval, this);
|
601
|
+
this.mark_words_OLD = __bind(this.mark_words_OLD, this);
|
553
602
|
this.mark_words = __bind(this.mark_words, this);
|
554
603
|
this.position_marked = __bind(this.position_marked, this);
|
555
604
|
this.get_row = __bind(this.get_row, this);
|
@@ -643,8 +692,32 @@ TranslationTable = (function() {
|
|
643
692
|
return false;
|
644
693
|
};
|
645
694
|
|
646
|
-
TranslationTable.prototype.mark_words = function(
|
695
|
+
TranslationTable.prototype.mark_words = function(word) {
|
696
|
+
var best_element, best_result, el, element_text, maximal_acceptable_distance, result, _i, _len, _ref;
|
697
|
+
word = Utils.trim(word);
|
698
|
+
best_result = 10000;
|
699
|
+
best_element = null;
|
700
|
+
maximal_acceptable_distance = 1;
|
701
|
+
if (word.length < 5) maximal_acceptable_distance = 0;
|
702
|
+
if (word.length > 10) maximal_acceptable_distance = 2;
|
703
|
+
_ref = $(".ac-word div");
|
704
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
705
|
+
el = _ref[_i];
|
706
|
+
element_text = Utils.trim($(el).text());
|
707
|
+
result = Utils.edit_distance(word, element_text);
|
708
|
+
if (result < best_result) {
|
709
|
+
best_result = result;
|
710
|
+
best_element = $(el);
|
711
|
+
}
|
712
|
+
}
|
713
|
+
if (best_element !== null && best_result <= maximal_acceptable_distance) {
|
714
|
+
return this.mark_interval(best_element.data('position-from'), best_element.data('position-to'));
|
715
|
+
}
|
716
|
+
};
|
717
|
+
|
718
|
+
TranslationTable.prototype.mark_words_OLD = function(words) {
|
647
719
|
var el, el_text, _i, _len, _ref;
|
720
|
+
console.log("MARKING WORDS: " + words);
|
648
721
|
words = "" + words + " ";
|
649
722
|
_ref = $(".ac-word div");
|
650
723
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
@@ -761,6 +834,28 @@ Utils = (function() {
|
|
761
834
|
return text.replace(/^\s+|\s+$/g, "");
|
762
835
|
};
|
763
836
|
|
837
|
+
Utils.edit_distance = function(source, target) {
|
838
|
+
var a, i, j, substitute_cost, _i, _ref, _ref2, _ref3, _ref4, _results;
|
839
|
+
a = [];
|
840
|
+
for (i = 0, _ref = source.length; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) {
|
841
|
+
a[i] = new Array(target.length + 1);
|
842
|
+
a[i][0] = i;
|
843
|
+
}
|
844
|
+
a[0] = (function() {
|
845
|
+
_results = [];
|
846
|
+
for (var _i = 0, _ref2 = target.length; 0 <= _ref2 ? _i <= _ref2 : _i >= _ref2; 0 <= _ref2 ? _i++ : _i--){ _results.push(_i); }
|
847
|
+
return _results;
|
848
|
+
}).apply(this);
|
849
|
+
for (i = 1, _ref3 = source.length; 1 <= _ref3 ? i <= _ref3 : i >= _ref3; 1 <= _ref3 ? i++ : i--) {
|
850
|
+
for (j = 1, _ref4 = target.length; 1 <= _ref4 ? j <= _ref4 : j >= _ref4; 1 <= _ref4 ? j++ : j--) {
|
851
|
+
substitute_cost = a[i - 1][j - 1];
|
852
|
+
if (source.charAt(i - 1) !== target.charAt(j - 1)) substitute_cost += 1;
|
853
|
+
a[i][j] = Math.min(substitute_cost, a[i - 1][j] + 1, a[i][j - 1] + 1);
|
854
|
+
}
|
855
|
+
}
|
856
|
+
return a[source.length][target.length];
|
857
|
+
};
|
858
|
+
|
764
859
|
return Utils;
|
765
860
|
|
766
861
|
}).call(this);
|
@@ -0,0 +1,72 @@
|
|
1
|
+
/*jshint eqnull:true */
|
2
|
+
/*!
|
3
|
+
* jQuery Cookie Plugin v1.2
|
4
|
+
* https://github.com/carhartl/jquery-cookie
|
5
|
+
*
|
6
|
+
* Copyright 2011, Klaus Hartl
|
7
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
8
|
+
* http://www.opensource.org/licenses/mit-license.php
|
9
|
+
* http://www.opensource.org/licenses/GPL-2.0
|
10
|
+
*/
|
11
|
+
(function ($, document, undefined) {
|
12
|
+
|
13
|
+
var pluses = /\+/g;
|
14
|
+
|
15
|
+
function raw(s) {
|
16
|
+
return s;
|
17
|
+
}
|
18
|
+
|
19
|
+
function decoded(s) {
|
20
|
+
return decodeURIComponent(s.replace(pluses, ' '));
|
21
|
+
}
|
22
|
+
|
23
|
+
$.cookie = function (key, value, options) {
|
24
|
+
|
25
|
+
// key and at least value given, set cookie...
|
26
|
+
if (value !== undefined && !/Object/.test(Object.prototype.toString.call(value))) {
|
27
|
+
options = $.extend({}, $.cookie.defaults, options);
|
28
|
+
|
29
|
+
if (value === null) {
|
30
|
+
options.expires = -1;
|
31
|
+
}
|
32
|
+
|
33
|
+
if (typeof options.expires === 'number') {
|
34
|
+
var days = options.expires, t = options.expires = new Date();
|
35
|
+
t.setDate(t.getDate() + days);
|
36
|
+
}
|
37
|
+
|
38
|
+
value = String(value);
|
39
|
+
|
40
|
+
return (document.cookie = [
|
41
|
+
encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value),
|
42
|
+
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
43
|
+
options.path ? '; path=' + options.path : '',
|
44
|
+
options.domain ? '; domain=' + options.domain : '',
|
45
|
+
options.secure ? '; secure' : ''
|
46
|
+
].join(''));
|
47
|
+
}
|
48
|
+
|
49
|
+
// key and possibly options given, get cookie...
|
50
|
+
options = value || $.cookie.defaults || {};
|
51
|
+
var decode = options.raw ? raw : decoded;
|
52
|
+
var cookies = document.cookie.split('; ');
|
53
|
+
for (var i = 0, parts; (parts = cookies[i] && cookies[i].split('=')); i++) {
|
54
|
+
if (decode(parts.shift()) === key) {
|
55
|
+
return decode(parts.join('='));
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
return null;
|
60
|
+
};
|
61
|
+
|
62
|
+
$.cookie.defaults = {};
|
63
|
+
|
64
|
+
$.removeCookie = function (key, options) {
|
65
|
+
if ($.cookie(key, options) !== null) {
|
66
|
+
$.cookie(key, null, options);
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
return false;
|
70
|
+
};
|
71
|
+
|
72
|
+
})(jQuery, document);
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<link rel="stylesheet" type="text/css" href="/bootstrap.css" />
|
7
7
|
<link rel="stylesheet" type="text/css" href="/style.css" />
|
8
8
|
<script src="/jquery.js"></script>
|
9
|
-
<script src="/
|
9
|
+
<script src="/cookie.js"></script>
|
10
10
|
<script src="/bootstrap.js"></script>
|
11
11
|
<script src="/ajax-cat.js"></script>
|
12
12
|
<script src="/index.js"></script>
|
@@ -30,9 +30,6 @@
|
|
30
30
|
<li><a href="#about" id="preview">Preview</a></li>
|
31
31
|
</ul>
|
32
32
|
<p class="pull-right">
|
33
|
-
<button class="btn btn-info" id="send-experiment">
|
34
|
-
Send experiment results
|
35
|
-
</button>
|
36
33
|
<button class="btn btn-info" id="save">
|
37
34
|
Save into browser
|
38
35
|
</button>
|
@@ -47,10 +44,13 @@
|
|
47
44
|
<div class="container-fluid">
|
48
45
|
<div class="row-fluid">
|
49
46
|
<h1>Translations</h1>
|
50
|
-
<span id="time"></span>
|
47
|
+
<h2><span id="translated-status"></span> <span id="time"></span></h2>
|
48
|
+
<button class="btn btn-info btn-large" id="send-experiment">
|
49
|
+
Next sentence >
|
50
|
+
</button>
|
51
51
|
|
52
52
|
<table class=" table-bordered table-condensed" id="translation">
|
53
|
-
<tr>
|
53
|
+
<tr id="top-translations">
|
54
54
|
<td width="50%" id="source-top"></td>
|
55
55
|
<td width="50%" id="target-top"></td>
|
56
56
|
</tr>
|
@@ -62,7 +62,7 @@
|
|
62
62
|
<div id="translation-table-container"></div>
|
63
63
|
</td>
|
64
64
|
</tr>
|
65
|
-
<tr>
|
65
|
+
<tr id="bottom-translations">
|
66
66
|
<td id="source-bottom"></td>
|
67
67
|
<td id="target-bottom"></td>
|
68
68
|
</tr>
|
@@ -70,7 +70,13 @@
|
|
70
70
|
|
71
71
|
</div><!--/row-->
|
72
72
|
|
73
|
-
<div id="
|
73
|
+
<div id="experiment-settings" style="display: none;">
|
74
|
+
<h2>Current experiment settings</h2>
|
75
|
+
<table class="table">
|
76
|
+
<tr><th>Suggestion panel:</th><td id="suggestion-panel-is-on"></td></tr>
|
77
|
+
</table>
|
78
|
+
</div>
|
79
|
+
<div id="log" style="display: none;"></div>
|
74
80
|
|
75
81
|
<hr>
|
76
82
|
|
@@ -13,6 +13,7 @@ module AjaxCat
|
|
13
13
|
@translated = translated
|
14
14
|
@translated_length = tokenize(translated).length
|
15
15
|
@suggestions = []
|
16
|
+
@suggested_phrases = []
|
16
17
|
end
|
17
18
|
|
18
19
|
def prepare_moses_request
|
@@ -28,8 +29,17 @@ module AjaxCat
|
|
28
29
|
def process_line(line)
|
29
30
|
words = line.split(" ||| ")[1].strip.split(" ")
|
30
31
|
if @suggestions.length < @@rows
|
31
|
-
|
32
|
-
|
32
|
+
alignment = line.split(" ||| ")[4].strip.split(" ").first
|
33
|
+
phrase = Phrase.new(words, alignment)
|
34
|
+
suggestion = {
|
35
|
+
"text" => phrase.words,
|
36
|
+
"from" => phrase.from,
|
37
|
+
"to" => phrase.to
|
38
|
+
}
|
39
|
+
if not @suggested_phrases.member?(suggestion['text'])
|
40
|
+
@suggested_phrases.push(suggestion['text'])
|
41
|
+
@suggestions.push(suggestion)
|
42
|
+
end
|
33
43
|
end
|
34
44
|
end
|
35
45
|
|