suever/MATL-Online

View on GitHub
matl_online/static/js/main.js

Summary

Maintainability
C
1 day
Test Coverage
File `main.js` has 375 lines of code (exceeds 250 allowed). Consider refactoring.
Function has too many statements (45). Maximum allowed is 30.
$(document).ready(function() {
var table = null;
 
var socket = io.connect(window.location.protocol +
'//' + document.domain +
(location.port ? ":" + location.port : ""));
var uuid;
var running = false;
 
var runtext = 'Run (ctrl + enter)';
var killtext = 'Kill (esc)';
 
$('#codeform').on('submit', function(d) {
d.preventDefault();
});
 
// Set up the text areas to automatically resize based on content
autosize($('#code'));
autosize($('#inputs'));
 
// Setup the code and input blocks to resize automatically as needed
$('#code').bind('input propertychange', function(){
countChar(this);
});
 
// Listen to the paste input field so that we can check the type
$('#paste_input_field').bind('input propertychange', function(){
checkInputType(this);
});
 
jQuery.hotkeys.options.filterContentEditable = false;
jQuery.hotkeys.options.filterInputAcceptingElements = false;
 
// Bind ctrl+return to submit the code for the user
$(document).bind('keydown', 'ctrl+return', function(){
submitCode();
});
 
$(document).bind('keydown', 'esc', function(){
killjob();
});
 
function sendAnalyticsEvent(category, type, message) {
if ( typeof ga !== "undefined" ) {
ga('send', 'event', category, type, message);
}
}
 
function timeoutFcn() {
 
Rollbar.warning("SocketIO Connection Failed", { uuid: uuid });
 
// Send a Google Analytics event if it's defined
sendAnalyticsEvent('errors', 'error', 'Submit failed');
 
// Force the socket to reconnect
socket.disconnect();
socket.connect();
 
// Attempt to resubmit the job
submitCode();
}
 
function submitCode() {
 
// If code is already running or there is no code, then skip
if ( running === true || $('#code').val() === '') {
return;
}
 
var form = $('#codeform');
 
$('#errorconsoletab').css('font-weight', 'normal');
$('#errors').html('');
$('#output').html('');
 
var timeoutId = setTimeout(timeoutFcn, 2000);
 
socket.emit('submit', {
code: $('#code').val(),
inputs: $('#inputs').val(),
debug: $('#debug').val(),
version: $('#version').data('version'),
uid: uuid
}, function(resp){
// Make sure that we don't fire the timeout callback
clearTimeout(timeoutId);
 
// Change the status and update the button functionality
running = true;
$('#run').text(killtext);
 
sendAnalyticsEvent('general', 'workflow', 'Job Submitted');
});
}
 
socket.on('connect', function(data){
console.log('Connected to server.');
$('#run').removeClass('disabled');
$('#run').text(runtext);
});
 
// When the server acknowledges that we are connected it will issue
// us an ID which we can use to get updates
socket.on('connection', function(data) {
uuid = data.session_id;
console.log('Session ID: ' + uuid);
});
 
$('#explain').on('click', function(d) {
var code = $('#code').val();
var version = $('#version').data('version');
 
// Do not submit the form unless there is code
if ( code === '' ) {
return;
}
 
$('#modal-explain').text('Parsing...');
 
// Going to get the explanation and post it in a balloon
$('#explainmodal').modal();
 
$.ajax({
url: '/explain',
method: 'GET',
data:{
code: code,
version: version
},
success: function(resp) {
alltext = '';
resp['data'].forEach(function (item) {
alltext = alltext + item.value
});
$('#modal-explain').text(alltext);
}
});
});
 
function getlink() {
 
var data = {
'code': $('#code').val(),
'inputs': $('#inputs').val(),
'version': $('#version').data('version')};
 
var link = $.param(data);
 
// Now sanitize for SE
return link.replace(/[!'()*]/g, function(ch) {
return '%' + ch.charCodeAt(0).toString(16);
});
}
 
Similar blocks of code found in 2 locations. Consider refactoring.
socket.on('killed', function(data) {
$('#run').text(runtext);
running = false;
console.log('Task killed.');
});
 
Similar blocks of code found in 2 locations. Consider refactoring.
socket.on('complete', function(msg) {
$('#run').text(runtext);
running = false;
console.log('Task complete.');
});
 
function killjob(){
socket.emit('kill', {uid: uuid});
}
 
$('#run').on('click', function() {
if ( running ) {
killjob();
} else {
submitCode();
}
});
 
socket.on('status', function(data) {
var output = $('#output');
var errors = $('#errors');
 
console.log('Output received.');
 
// Clear the output
output.text('');
 
if ( data['session'] === uuid ) {
Function has a complexity of 7.
data['data'].forEach(function(item) {
switch ( item.type ) {
case 'image':
case 'image_nn':
 
// Remove any previous images (simulates drawnow)
var thumb = $('.thumb');
 
if ( thumb.length ){
$(thumb).find('.imshow').attr('src', item.value);
} else {
var container = $('<div/>', { 'class': 'thumb' });
var img = $('<img/>', { 'class': 'imshow', src: item.value });
container.append(img);
output.append(container);
 
$('.thumb').on('click', function(e) {
var url = $(this).find('.imshow').attr('src');
$('#imagepreview').attr('src', url);
var img = $('#imagepreview').get(0);
 
$('#dimensions').text(img.naturalHeight + ' x ' + img.naturalWidth);
$('#imagemodal').modal('show');
});
 
 
if ( item.type === 'image_nn' ){
$('.imshow').addClass('nn-interp');
$('#imagepreview').addClass('nn-interp');
} else {
$('.imshow').removeClass('nn-interp');
$('#imagepreview').removeClass('nn-interp');
}
}
 
break;
case 'audio':
var span = $('<div/>', { 'class': 'audio' });
var fallback = 'Your browser does not support the <code>audio</code> element.';
var audio_tag = $('<audio/>', {
src: item.value,
controls: "true",
text: fallback
});
 
span.append(audio_tag);
output.append(span)
break
case 'stderr':
errors.append(document.createTextNode(item.value + '\n'));
$('#errorconsoletab').css('font-weight', 'bold');
break;
default:
output.append(document.createTextNode(item.value));
}
});
}
});
 
function refreshHelp() {
// If the table is initialized, then refresh it
if ( table ) {
table.ajax.url('help/' + $('#version').data('version')).load();
}
}
 
function checkInputType(obj){
// If contains any alphanumeric characters
var str = $(obj).val();
 
if ( str.toLowerCase() === str.toUpperCase() ){
// Then we assume that this is numeric
$('#array_input').prop('checked', true);
$('#string_input').prop('checked', false);
} else {
$('#array_input').prop('checked', false);
$('#string_input').prop('checked', true);
}
}
 
function parseArray(str){
// Replace ][ with ; and [[ with [ and ]] with ]
return str.replace(/\]\s*,?\s*\[/g, '; ').
replace(/\[\s*\[\s*/g, '[').
replace(/\]\s*\]\s*/g, ']').
replace(/\s+/g, ' ').
replace(/\s*,\s*/g, ', ');
}
 
function parseString(str){
// Parse as raw string
str = str.split('\n');
 
// Now pad any strings as needed
var maxLength = 0;
$.each(str, function(index, val){
maxLength = Math.max(maxLength, val.length);
});
 
 
if ( str.length > 1 ){
output = "[";
} else {
output = '';
}
 
for ( var k = 0; k < str.length; ++k ){
num2pad = maxLength - str[k].length;
str[k] += new Array(num2pad+1).join(' ');
output = output + "'" + str[k] + "';";
}
 
output = output.replace(/;$/g, '');
 
if ( str.length > 1 ){
output = output + ']';
}
 
return output;
}
 
$('#paste_apply').on('click', function(evnt){
 
var input = $('#paste_input_field').val();
 
switch ( $("input[name=format]:checked").val() ) {
case 'array':
input = parseArray(input);
break;
case 'string':
input = parseString(input);
break;
}
 
if ( $('#inputs').val().length ){
input = '\n' + input;
}
 
$('#inputs').val($('#inputs').val() + input);
 
// Make sure that we resize the inputs field
autosize.update($('#inputs'));
 
$('#pastemodal').modal('toggle');
$('#paste_input_field').val('');
});
 
$('#paste_input').on('click', function(evnt){
// Open up modal dialog to add data
$('#pastemodal').modal();
});
 
$('#pastemodal').on('shown.bs.modal', function(){
$('#paste_input_field').focus();
});
 
$('.version').on('click', function(e) {
$('#version').text(e.target.text).data('version', $(e.target).data('version'));
refreshHelp();
e.preventDefault();
});
 
$('#save').on('click', function(e) {
link = '?' + getlink();
history.pushState({'Title': '', 'Url': link}, '', link);
e.preventDefault();
});
 
// When the image preview dialog is closed, reset the share link
$('#imagemodal').on('hidden.bs.modal', function () {
$('#share').removeClass('flip-side-2').addClass('flip-side-1');
$('#sharelink').removeClass('flip-side-1');
});
 
// Be sure that we place the CSRF token in all AJAX request headers
$.ajaxSetup({
// Add a header containing the CSRF token if needed
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken",
$('meta[name=csrf-token]').attr('content'));
}
}
});
 
$('#share').on('click', function(e){
// Display loading while we upload to imgur and get the link
$('#imgurlink').val('Loading...').css('color', '');
 
// Flip over to the link to prevent the user from clicking share again
$('#share').addClass('flip-side-2');
$('#sharelink').addClass('flip-side-1');
 
$.post({
url: '/share',
data: {'data': $('.thumb').find('.imshow').attr('src')},
success: function(resp){
// Paste the link into the input box and highlight it
$('#imgurlink').val(resp.link)
.prop('disabled', false)
.css('color', 'black')
.select();
},
error: function(resp){
// Some unspecified error happened (could be CSRF or others)
$('#imgurlink').val('SERVER ERROR')
.css('color', 'red');
}
});
});
 
function countChar(val) {
var count = val.value.length;
 
if ( count === 1 ) {
$('#charcount').text('(' + count + ' character)');
} else {
$('#charcount').text('(' + count + ' characters)');
}
}
 
function searchTable(table, string) {
 
var origLen = string.length;
var hasQuote = false;
 
// If only one " then replace with ""
if ( (string.match(/"/g) || []).length === 1 ){
string = string.replace(/"/g, '""');
hasQuote = true;
}
 
// Determine if it starts with X, Y, or Z
var hasXYZ = string.match('^[XYZ]');
 
// If we have a single character OR the search string
// starts with X, Y, or Z and is two characters (or 3 if
// one is a quote)
if ( origLen === 1 || ( hasXYZ && origLen === 2 ) ) {
// Search just the first column (no regex, non-smart,
// and case-sensitive)
table.columns(0).search(string, false, false, false).draw();
} else {
// Apply the search to the table as a whole
table.columns(0).search('');
table.search(string).draw();
}
}
 
Function `toggleDocumentation` has 53 lines of code (exceeds 25 allowed). Consider refactoring.
function toggleDocumentation(){
 
var navitem = $('#doctoggle').parent();
var activeClasses = 'col-md-6 col-lg-6 col-sm-6';
 
if ( navitem.hasClass('active') ){
navitem.removeClass('active');
$('#left_div').removeClass(activeClasses);
$('.drawer').drawer('hide');
} else {
navitem.addClass('active');
$('#left_div').addClass(activeClasses);
$('.drawer').drawer('show');
 
// Create the datatable if it doesn't exist already
if ( table === null ){
table = $('#documentation').DataTable({
paging: false,
ordering: false,
info: false,
ajax: 'help/' + $('#version').data('version'),
stripe: true,
scrollY: 'calc(100% - 80px)',
scrollX: false,
scrollCollapse: true,
order: [[ 0, "asc"]],
columns: [
{
data: 'source',
orderable: true,
render: function(src) {
return '<strong>' + src + '</strong>';
}
},
{
data: null,
orderable: false,
render: function(data) {
output = '<strong>' + data.brief + '</strong>';
if ( data.arguments ){
output = output + '\n' + data.arguments;
}
return output + '\n' + data.description;
}
}]
});
 
// The events that we'd like to override from datatables
var events = 'keyup.DT search.DT input.DT paste.DT cut.DT';
 
var searchDelay = null;
 
// Custom search function to handle single " characters
$('#documentation_filter input')
.off(events)
.on(events, function(evnt){
// Only allow the search to be performed every 250ms
clearTimeout(searchDelay)
searchDelay = setTimeout(function(){
searchTable(table, evnt.target.value);
}, 250);
});
}
}
}
 
$('.drawer').on('shown.bs.drawer', function(){
$('input[type=search]').focus();
});
 
$('#doctoggle').on('click', function(evnt){
toggleDocumentation();
evnt.preventDefault();
});
 
$('#docbutton').on('click', function(evnt){
toggleDocumentation();
});
});