quorum 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +35 -28
- data/HISTORY.md +4 -0
- data/app/assets/javascripts/quorum/application.js +1 -0
- data/app/assets/javascripts/quorum/jobs.js +5 -345
- data/app/assets/javascripts/quorum/quorum.js +275 -0
- data/app/assets/javascripts/quorum/template_settings.js +8 -0
- data/app/assets/javascripts/quorum/utilities.js +12 -0
- data/app/assets/stylesheets/quorum/application.css +0 -4
- data/app/assets/stylesheets/quorum/autohint.css +4 -0
- data/app/controllers/quorum/jobs_controller.rb +9 -9
- data/app/views/quorum/jobs/form/_blastn_form.html.erb +62 -62
- data/app/views/quorum/jobs/form/_blastp_form.html.erb +62 -62
- data/app/views/quorum/jobs/form/_blastx_form.html.erb +62 -62
- data/app/views/quorum/jobs/form/_tblastn_form.html.erb +62 -62
- data/app/views/quorum/jobs/show.html.erb +1 -1
- data/app/views/quorum/jobs/templates/_blast_detailed_report_template.html.erb +22 -22
- data/app/views/quorum/jobs/templates/_blast_template.html.erb +29 -29
- data/app/views/shared/_error_messages.html.erb +8 -8
- data/lib/quorum/version.rb +1 -1
- data/lib/tasks/jasmine.rake +8 -0
- data/quorum.gemspec +1 -0
- data/spec/data/seqs_not_fa.txt +16 -16
- data/spec/javascripts/fixtures/formatted_sequence.html +6 -0
- data/spec/javascripts/fixtures/quorum_search_form.html +461 -0
- data/spec/javascripts/fixtures/quorum_tabs.html +10 -0
- data/spec/javascripts/helpers/jasmine-jquery.js +288 -0
- data/spec/javascripts/jobs_spec.js +99 -0
- data/spec/javascripts/jquery/jquery-ui.min.js +791 -0
- data/spec/javascripts/jquery/jquery.min.js +4 -0
- data/spec/javascripts/jquery/jquery_ujs.js +373 -0
- data/spec/javascripts/quorum_spec.js +106 -0
- data/spec/javascripts/string_spec.js +18 -0
- data/spec/javascripts/support/jasmine.yml +84 -0
- data/spec/javascripts/support/jasmine_config.rb +23 -0
- data/spec/javascripts/support/jasmine_runner.rb +33 -0
- data/spec/requests/jobs_spec.rb +34 -33
- data/vendor/assets/javascripts/jquery.autohint.js +87 -0
- metadata +62 -26
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
quorum (0.
|
4
|
+
quorum (0.3.0)
|
5
5
|
bio-blastxmlparser (~> 1.0.1)
|
6
6
|
jquery-rails
|
7
7
|
net-ssh (~> 2.3.0)
|
@@ -12,31 +12,31 @@ PATH
|
|
12
12
|
GEM
|
13
13
|
remote: http://rubygems.org/
|
14
14
|
specs:
|
15
|
-
actionmailer (3.2.
|
16
|
-
actionpack (= 3.2.
|
15
|
+
actionmailer (3.2.1)
|
16
|
+
actionpack (= 3.2.1)
|
17
17
|
mail (~> 2.4.0)
|
18
|
-
actionpack (3.2.
|
19
|
-
activemodel (= 3.2.
|
20
|
-
activesupport (= 3.2.
|
18
|
+
actionpack (3.2.1)
|
19
|
+
activemodel (= 3.2.1)
|
20
|
+
activesupport (= 3.2.1)
|
21
21
|
builder (~> 3.0.0)
|
22
22
|
erubis (~> 2.7.0)
|
23
|
-
journey (~> 1.0.
|
23
|
+
journey (~> 1.0.1)
|
24
24
|
rack (~> 1.4.0)
|
25
25
|
rack-cache (~> 1.1)
|
26
26
|
rack-test (~> 0.6.1)
|
27
27
|
sprockets (~> 2.1.2)
|
28
|
-
activemodel (3.2.
|
29
|
-
activesupport (= 3.2.
|
28
|
+
activemodel (3.2.1)
|
29
|
+
activesupport (= 3.2.1)
|
30
30
|
builder (~> 3.0.0)
|
31
|
-
activerecord (3.2.
|
32
|
-
activemodel (= 3.2.
|
33
|
-
activesupport (= 3.2.
|
31
|
+
activerecord (3.2.1)
|
32
|
+
activemodel (= 3.2.1)
|
33
|
+
activesupport (= 3.2.1)
|
34
34
|
arel (~> 3.0.0)
|
35
35
|
tzinfo (~> 0.3.29)
|
36
|
-
activeresource (3.2.
|
37
|
-
activemodel (= 3.2.
|
38
|
-
activesupport (= 3.2.
|
39
|
-
activesupport (3.2.
|
36
|
+
activeresource (3.2.1)
|
37
|
+
activemodel (= 3.2.1)
|
38
|
+
activesupport (= 3.2.1)
|
39
|
+
activesupport (3.2.1)
|
40
40
|
i18n (~> 0.6)
|
41
41
|
multi_json (~> 1.0)
|
42
42
|
arel (3.0.0)
|
@@ -66,7 +66,13 @@ GEM
|
|
66
66
|
ffi (1.0.11)
|
67
67
|
hike (1.2.1)
|
68
68
|
i18n (0.6.0)
|
69
|
-
|
69
|
+
jasmine (1.1.2)
|
70
|
+
jasmine-core (>= 1.1.0)
|
71
|
+
rack (>= 1.1)
|
72
|
+
rspec (>= 1.3.1)
|
73
|
+
selenium-webdriver (>= 0.1.3)
|
74
|
+
jasmine-core (1.1.0)
|
75
|
+
journey (1.0.1)
|
70
76
|
jquery-rails (2.0.0)
|
71
77
|
railties (>= 3.2.0.beta, < 5.0)
|
72
78
|
thor (~> 0.14)
|
@@ -91,17 +97,17 @@ GEM
|
|
91
97
|
rack
|
92
98
|
rack-test (0.6.1)
|
93
99
|
rack (>= 1.0)
|
94
|
-
rails (3.2.
|
95
|
-
actionmailer (= 3.2.
|
96
|
-
actionpack (= 3.2.
|
97
|
-
activerecord (= 3.2.
|
98
|
-
activeresource (= 3.2.
|
99
|
-
activesupport (= 3.2.
|
100
|
+
rails (3.2.1)
|
101
|
+
actionmailer (= 3.2.1)
|
102
|
+
actionpack (= 3.2.1)
|
103
|
+
activerecord (= 3.2.1)
|
104
|
+
activeresource (= 3.2.1)
|
105
|
+
activesupport (= 3.2.1)
|
100
106
|
bundler (~> 1.0)
|
101
|
-
railties (= 3.2.
|
102
|
-
railties (3.2.
|
103
|
-
actionpack (= 3.2.
|
104
|
-
activesupport (= 3.2.
|
107
|
+
railties (= 3.2.1)
|
108
|
+
railties (3.2.1)
|
109
|
+
actionpack (= 3.2.1)
|
110
|
+
activesupport (= 3.2.1)
|
105
111
|
rack-ssl (~> 1.3.2)
|
106
112
|
rake (>= 0.8.7)
|
107
113
|
rdoc (~> 3.4)
|
@@ -139,7 +145,7 @@ GEM
|
|
139
145
|
railties (>= 3.0)
|
140
146
|
rspec (~> 2.8.0)
|
141
147
|
rubyzip (0.9.5)
|
142
|
-
selenium-webdriver (2.
|
148
|
+
selenium-webdriver (2.18.0)
|
143
149
|
childprocess (>= 0.2.5)
|
144
150
|
ffi (~> 1.0.9)
|
145
151
|
multi_json (~> 1.0.4)
|
@@ -170,6 +176,7 @@ DEPENDENCIES
|
|
170
176
|
capybara (~> 1.1)
|
171
177
|
database_cleaner (~> 0.6)
|
172
178
|
factory_girl_rails (~> 1.2)
|
179
|
+
jasmine (~> 1.1)
|
173
180
|
mysql2 (~> 0.3.11)
|
174
181
|
quorum!
|
175
182
|
resque_spec (~> 0.8)
|
data/HISTORY.md
CHANGED
@@ -1,72 +1,9 @@
|
|
1
|
-
//
|
2
|
-
// Mustache style Underscore.js templating.
|
3
|
-
//
|
4
|
-
_.templateSettings = {
|
5
|
-
evaluate: /\{\{(.+?)\}\}/g,
|
6
|
-
interpolate: /\{\{\=(.+?)\}\}/g
|
7
|
-
};
|
8
|
-
|
9
1
|
//
|
10
2
|
// jQuery
|
11
3
|
//---------------------------------------------------------------------------//
|
12
4
|
|
13
5
|
$(function() {
|
14
6
|
|
15
|
-
// jQuery Functions //
|
16
|
-
|
17
|
-
$.fn.extend({
|
18
|
-
// Add hints to the form elements.
|
19
|
-
addHints: function() {
|
20
|
-
$(this).each(function() {
|
21
|
-
if ($(this).attr('title') === '') {
|
22
|
-
return;
|
23
|
-
}
|
24
|
-
|
25
|
-
if ($(this).val() === '') {
|
26
|
-
$(this).val($(this).attr('title'));
|
27
|
-
|
28
|
-
if (!$(this).hasClass('auto-hint')) {
|
29
|
-
$(this).addClass('auto-hint');
|
30
|
-
}
|
31
|
-
} else {
|
32
|
-
$(this).removeClass('auto-hint');
|
33
|
-
}
|
34
|
-
});
|
35
|
-
},
|
36
|
-
|
37
|
-
// Remove hints on submit.
|
38
|
-
removeHintsOnSubmit: function() {
|
39
|
-
$(this).each(function() {
|
40
|
-
if ($(this).val() === $(this).attr('title')) {
|
41
|
-
$(this).val('');
|
42
|
-
}
|
43
|
-
});
|
44
|
-
},
|
45
|
-
|
46
|
-
// Remove hint and class on focus.
|
47
|
-
focusHint: function() {
|
48
|
-
$(this).focus(function() {
|
49
|
-
if ($(this).val() === $(this).attr('title')) {
|
50
|
-
$(this).val('');
|
51
|
-
$(this).removeClass('auto-hint');
|
52
|
-
}
|
53
|
-
});
|
54
|
-
},
|
55
|
-
|
56
|
-
// Retain value or add hint.
|
57
|
-
blurHint: function() {
|
58
|
-
$(this).blur(function() {
|
59
|
-
if ($(this).val() === '' && $(this).attr('title') !== '') {
|
60
|
-
$(this).val($(this).attr('title'));
|
61
|
-
$(this).addClass('auto-hint');
|
62
|
-
}
|
63
|
-
});
|
64
|
-
}
|
65
|
-
});
|
66
|
-
|
67
|
-
// End jQuery Functions //
|
68
|
-
|
69
|
-
|
70
7
|
// Hide Elements //
|
71
8
|
|
72
9
|
if (!$('#job_blastn_job_attributes_queue').is(':checked')) {
|
@@ -91,9 +28,7 @@ $(function() {
|
|
91
28
|
// Form //
|
92
29
|
|
93
30
|
var form = $('form :input.auto-hint');
|
94
|
-
form.
|
95
|
-
form.blurHint();
|
96
|
-
form.addHints();
|
31
|
+
form.autoHint();
|
97
32
|
|
98
33
|
// Toggle Algorithms //
|
99
34
|
|
@@ -121,21 +56,22 @@ $(function() {
|
|
121
56
|
'disabled', 'disabled'
|
122
57
|
);
|
123
58
|
|
124
|
-
form.
|
59
|
+
form.autoHint('removeHints');
|
125
60
|
});
|
126
61
|
|
127
62
|
// Reset form.
|
128
63
|
$('#quorum_job_reset').click(function() {
|
64
|
+
$('textarea').val('');
|
129
65
|
$('input:text').val('');
|
130
66
|
$('input:file').val('');
|
131
|
-
$('input:checkbox').
|
67
|
+
$('input:checkbox').prop('checked', false);
|
132
68
|
$('select').val('');
|
133
69
|
|
134
70
|
$('.toggle').each(function() {
|
135
71
|
$(this).hide();
|
136
72
|
});
|
137
73
|
|
138
|
-
form.addHints
|
74
|
+
form.autoHint('addHints');
|
139
75
|
});
|
140
76
|
|
141
77
|
// End Form //
|
@@ -150,279 +86,3 @@ $(function() {
|
|
150
86
|
|
151
87
|
});
|
152
88
|
|
153
|
-
|
154
|
-
//
|
155
|
-
// JavaScript Functions
|
156
|
-
//---------------------------------------------------------------------------//
|
157
|
-
|
158
|
-
//
|
159
|
-
// Poll quorum search results asynchronously and insert them into
|
160
|
-
// the DOM via #blast_template.
|
161
|
-
//
|
162
|
-
var pollResults = function(id, interval, algos) {
|
163
|
-
|
164
|
-
// Set the default poll interval to 5 seconds.
|
165
|
-
interval = interval || 5000;
|
166
|
-
|
167
|
-
// Algorithms
|
168
|
-
algos = algos || ["blastn", "blastx", "tblastn", "blastp"];
|
169
|
-
|
170
|
-
_.each(algos, function(a) {
|
171
|
-
$.getJSON(
|
172
|
-
'/quorum/jobs/' + id + '/get_quorum_search_results.json?algo=' + a,
|
173
|
-
function(data) {
|
174
|
-
if (data.length === 0) {
|
175
|
-
setTimeout(function() { pollResults(id, interval, [a]); }, interval);
|
176
|
-
} else {
|
177
|
-
$('#' + a + '-results').empty();
|
178
|
-
var temp = _.template(
|
179
|
-
$('#blast_template').html(), {
|
180
|
-
data: data,
|
181
|
-
algo: a
|
182
|
-
}
|
183
|
-
);
|
184
|
-
$('#' + a + '-results').html(temp);
|
185
|
-
return;
|
186
|
-
}
|
187
|
-
}
|
188
|
-
);
|
189
|
-
});
|
190
|
-
}
|
191
|
-
|
192
|
-
//
|
193
|
-
// Display jQuery UI modal box containing detailed report of all hits
|
194
|
-
// to the same query. After the modal box is inserted into the DOM,
|
195
|
-
// automatically scroll to the highlighted hit.
|
196
|
-
//
|
197
|
-
var viewDetailedReport = function(id, focus_id, query, algo) {
|
198
|
-
// Create the modal box.
|
199
|
-
$('#detailed_report_dialog').html(
|
200
|
-
"<p class='center'>" +
|
201
|
-
"Loading... <img src='/assets/quorum/loading.gif' alt='Loading'>" +
|
202
|
-
"</p>"
|
203
|
-
).dialog({
|
204
|
-
modal: true,
|
205
|
-
width: 850,
|
206
|
-
position: 'top'
|
207
|
-
});
|
208
|
-
|
209
|
-
$.getJSON(
|
210
|
-
'/quorum/jobs/' + id + '/get_quorum_search_results.json?algo=' + algo +
|
211
|
-
'&query=' + query,
|
212
|
-
function(data) {
|
213
|
-
var temp = _.template(
|
214
|
-
$('#detailed_report_template').html(), {
|
215
|
-
data: data,
|
216
|
-
query: query,
|
217
|
-
algo: algo
|
218
|
-
}
|
219
|
-
);
|
220
|
-
|
221
|
-
// Insert the detailed report data.
|
222
|
-
$('#detailed_report_dialog').empty().html(temp);
|
223
|
-
|
224
|
-
// Add tipsy to the sequence data.
|
225
|
-
$('a[rel=quorum-tipsy]').tipsy({ gravity: 's' });
|
226
|
-
|
227
|
-
// Highlight the selected id.
|
228
|
-
$('#' + focus_id).addClass("ui-state-highlight");
|
229
|
-
|
230
|
-
// Automatically scroll to the selected id.
|
231
|
-
autoScroll(focus_id, false);
|
232
|
-
}
|
233
|
-
);
|
234
|
-
}
|
235
|
-
|
236
|
-
//
|
237
|
-
// Helper to add title sequence position attribute for tipsy.
|
238
|
-
//
|
239
|
-
// If from > to decrement index; otherwise increment.
|
240
|
-
// If the algo is tblastn and type is hit OR algo is blastx and type is query,
|
241
|
-
// increment / decrement by 3; otherwise increment / decrement by 1.
|
242
|
-
//
|
243
|
-
var addBaseTitleIndex = function(bases, from, to, algo, type) {
|
244
|
-
var forward = true;
|
245
|
-
var value = 1;
|
246
|
-
var index = from;
|
247
|
-
|
248
|
-
if (from > to) {
|
249
|
-
forward = false;
|
250
|
-
}
|
251
|
-
|
252
|
-
// Set value to 3 for the below.
|
253
|
-
if ((type === "hit" && algo === "tblastn") ||
|
254
|
-
(type === "query" && algo === "blastx")) {
|
255
|
-
value = 3;
|
256
|
-
}
|
257
|
-
|
258
|
-
// Add tipsy to each base.
|
259
|
-
return _.map(bases.split(''), function(c) {
|
260
|
-
var str = "<a rel='quorum-tipsy' title=" + index + ">" + c + "</a>";
|
261
|
-
forward ? index += value : index -= value;
|
262
|
-
return str;
|
263
|
-
}).join('');
|
264
|
-
}
|
265
|
-
|
266
|
-
//
|
267
|
-
// Format sequence data for detailed report.
|
268
|
-
//
|
269
|
-
// If q_from > q_to or h_from > h_to, subtract by increment; otherwise add
|
270
|
-
// by increment.
|
271
|
-
//
|
272
|
-
// If algo is tblastn or blastx, multiple increment by 3.
|
273
|
-
//
|
274
|
-
var formatSequenceReport = function(qseq, midline, hseq, q_from, q_to, h_from, h_to, algo) {
|
275
|
-
var max = qseq.length; // max length
|
276
|
-
var increment = 60; // increment value
|
277
|
-
var s = 0; // start position
|
278
|
-
var e = increment; // end position
|
279
|
-
var seq = "\n"; // seq string to return
|
280
|
-
|
281
|
-
while(true) {
|
282
|
-
seq += "qseq " + addBaseTitleIndex(qseq.slice(s, e), q_from, q_to, algo, 'query') + "\n";
|
283
|
-
seq += " " + midline.slice(s, e) + "\n";
|
284
|
-
seq += "hseq " + addBaseTitleIndex(hseq.slice(s, e), h_from, h_to, algo, 'hit') + "\n\n";
|
285
|
-
|
286
|
-
if (e >= max) {
|
287
|
-
break;
|
288
|
-
}
|
289
|
-
|
290
|
-
s += increment;
|
291
|
-
e += increment;
|
292
|
-
|
293
|
-
// If the algorithm is blastx, increment * 3 only for qseq.
|
294
|
-
if (algo === "blastx") {
|
295
|
-
q_from < q_to ? q_from += (increment * 3) : q_from -= (increment * 3);
|
296
|
-
} else {
|
297
|
-
q_from < q_to ? q_from += increment : q_from -= increment;
|
298
|
-
}
|
299
|
-
|
300
|
-
// If the algorithm is tblastn, increment * 3 only for hseq.
|
301
|
-
if (algo === "tblastn") {
|
302
|
-
h_from < h_to ? h_from += (increment * 3) : h_from -= (increment * 3);
|
303
|
-
} else {
|
304
|
-
h_from < h_to ? h_from += increment : h_from -= increment;
|
305
|
-
}
|
306
|
-
}
|
307
|
-
return "<p class='small'>Alignment (Mouse over for positions):</p>" +
|
308
|
-
"<span class='small'><pre>" + seq + "</pre></span>";
|
309
|
-
}
|
310
|
-
|
311
|
-
//
|
312
|
-
// Format Query and Hit Strand.
|
313
|
-
//
|
314
|
-
// If query_frame or hit_frame < 0, print 'reverse'; print 'forward' otherwise.
|
315
|
-
//
|
316
|
-
var formatStrand = function(qstrand, hstrand) {
|
317
|
-
var q = "";
|
318
|
-
var h = "";
|
319
|
-
|
320
|
-
qstrand < 0 ? q = "reverse" : q = "forward";
|
321
|
-
hstrand < 0 ? h = "reverse" : h = "forward";
|
322
|
-
|
323
|
-
return q + " / " + h;
|
324
|
-
}
|
325
|
-
|
326
|
-
//
|
327
|
-
// Display links to Hsps in the same group.
|
328
|
-
//
|
329
|
-
var displayHspLinks = function(focus, group, data) {
|
330
|
-
if (group !== null) {
|
331
|
-
var str = "Related <a onclick=\"(openWindow(" +
|
332
|
-
"'http://www.ncbi.nlm.nih.gov/books/NBK62051/def-item/blast_glossary.HSP'," +
|
333
|
-
"'HSP', 800, 300))\">HSPs</a>: ";
|
334
|
-
|
335
|
-
var ids = _.map(group.split(","), function(i) { return parseInt(i); });
|
336
|
-
|
337
|
-
var selected = _(data).chain()
|
338
|
-
.reject(function(d) { return !_.include(ids, d.id); })
|
339
|
-
.sortBy(function(d) { return d.id; })
|
340
|
-
.value();
|
341
|
-
|
342
|
-
_.each(selected, function(e) {
|
343
|
-
if (e.id !== focus) {
|
344
|
-
str += "<a onclick='(autoScroll(" + e.id + ", true))'>" + e.hsp_num + "</a> ";
|
345
|
-
} else {
|
346
|
-
str += e.hsp_num + " ";
|
347
|
-
}
|
348
|
-
});
|
349
|
-
return str;
|
350
|
-
}
|
351
|
-
}
|
352
|
-
|
353
|
-
//
|
354
|
-
// Download Blast hit sequence.
|
355
|
-
//
|
356
|
-
var downloadSequence = function(id, algo_id, algo, el) {
|
357
|
-
$(el).html('Fetching sequence...');
|
358
|
-
|
359
|
-
$.getJSON(
|
360
|
-
"/quorum/jobs/" + id + "/get_quorum_blast_hit_sequence.json?algo_id=" +
|
361
|
-
algo_id + "&algo=" + algo,
|
362
|
-
function(data) {
|
363
|
-
getSequenceFile(id, data[0].meta_id, el);
|
364
|
-
}
|
365
|
-
);
|
366
|
-
}
|
367
|
-
|
368
|
-
//
|
369
|
-
// Poll application for Blast hit sequence.
|
370
|
-
//
|
371
|
-
var getSequenceFile = function(id, meta_id, el) {
|
372
|
-
var url = "/quorum/jobs/" + id +
|
373
|
-
"/send_quorum_blast_hit_sequence?meta_id=" + meta_id;
|
374
|
-
$.get(
|
375
|
-
url,
|
376
|
-
function(data) {
|
377
|
-
if (data.length === 0) {
|
378
|
-
setTimeout(function() { getSequenceFile(id, meta_id, el) }, 2500);
|
379
|
-
} else {
|
380
|
-
if (data.indexOf("error") !== -1) {
|
381
|
-
// Print error message.
|
382
|
-
$(el).addClass('ui-state-error').html(data);
|
383
|
-
} else {
|
384
|
-
// Force browser to download file via iframe.
|
385
|
-
$(el).addClass('ui-state-highlight').html('Sequence Downloaded Successfully');
|
386
|
-
$('.quorum_sequence_download').remove();
|
387
|
-
$('body').append('<iframe class="quorum_sequence_download"></iframe>');
|
388
|
-
$('.quorum_sequence_download').attr('src', url).hide();
|
389
|
-
}
|
390
|
-
}
|
391
|
-
}
|
392
|
-
);
|
393
|
-
}
|
394
|
-
|
395
|
-
//
|
396
|
-
// Autoscroll to given div id.
|
397
|
-
//
|
398
|
-
var autoScroll = function(id, highlight) {
|
399
|
-
$('html, body').animate({
|
400
|
-
scrollTop: $('#' + id).offset().top
|
401
|
-
}, 1000);
|
402
|
-
|
403
|
-
if (highlight) {
|
404
|
-
$('#' + id).effect("highlight", {}, 4000);
|
405
|
-
}
|
406
|
-
}
|
407
|
-
|
408
|
-
//
|
409
|
-
// Truncate string to length n using word boundary.
|
410
|
-
//
|
411
|
-
String.prototype.trunc = function(n) {
|
412
|
-
var longStr = this.length > n;
|
413
|
-
var str = longStr ? this.substr(0, n-1) : this;
|
414
|
-
|
415
|
-
longStr ? str.substr(0, str.lastIndexOf(' ')) : str;
|
416
|
-
return longStr ? str + '...' : str;
|
417
|
-
}
|
418
|
-
|
419
|
-
//
|
420
|
-
// Open URL in new window.
|
421
|
-
//
|
422
|
-
var openWindow = function(url, name, width, height) {
|
423
|
-
|
424
|
-
var windowSize = "width=" + width + ",height=" + height + ",scrollbars=yes";
|
425
|
-
|
426
|
-
window.open(url, name, windowSize);
|
427
|
-
}
|
428
|
-
|