NLP

#Natural Language Processing API samples

Extract and highlight entities from a text

This example shows the use of the /nlp/ner/extract/annotations api to extract entities from a text.

Demo

Source code

jQuery(function($) {
  'use strict';

  // Function to call the SYSTRAN NLP Api extractAnnotations
  function extractAnnotations(content, callback) {
    $.ajax({
      method:'GET',
      url: 'https://api-platform.systran.net/nlp/ner/extract/annotations?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&lang=en',
      data: {
        input: content
      },
      success: function(data) {
        callback(null, data);
      },
      error: function(xhr, status, err) {
        callback(err);
      }
    });
  }

  // Get css class from annotation type
  function classFromAnnotationType(type) {
    if (type) {
      if (type.search(/^ENAMEX_(firstname|lastname|person)|title/) !== -1)
        return 'people';
      else if (type.search(/^ENAMEX_organization/) !== -1)
        return 'organization';
      else if (type.search(/^ENAMEX_location/) !== -1)
        return 'location';
      else if (type.search(/TIMEX|day|month|year/) !== -1)
        return 'time';
    }
    return '';
  }

  // Function to build html from the result returned by the extractAnnotations api
  function annotationsToHml(annotations) {
    var html = '';
    var i, j, li, lj;
    if (annotations && annotations.annotations) {
      for (i = 0, li = annotations.annotations.length; i < li; ++i) {
        var annotation = annotations.annotations[i];
        if (annotation.entities) {
          var spans = [];
          var lastIdx = 0;
          for (j = 0, lj = annotation.entities.length; j < lj; ++j) {
            var entity = annotation.entities[j];
            spans.push({idx: entity.start, value: '<span class="entity highlight ' + classFromAnnotationType(entity.type) + '">', start: true});
            spans.push({idx: entity.end, value: '</span>'});
          }
          spans.sort(function(a,b) {
            if (a.idx === b.idx) {
              return a.start ? (b.start ? 0 : 1) : (b.start ? -1: 0);
            } else {
              return (a.idx < b.idx) ? -1: 1;
            }
          });
          for (j = 0, lj = spans.length; j < lj; ++j) {
            var span = spans[j];
            html += annotation.source.substring(lastIdx, span.idx) + span.value;
            lastIdx = span.idx;
          }
          html += annotation.source.substring(lastIdx);
        } else {
          html += annotation.source;
        }
        html += '<br>';
      }
    }
    return html;
  }

  function getTextFromHtml(content) {
    content = content.replace(/<div>(?:<br>)?/gi, '\n').replace(/<\/div>/gi, '');
    content = content.replace(/<p>&nbsp;<\/p>/gi, '\n').replace(/<p>/gi, '').replace(/\n*<\/p>/gi, '\n');
    content = content.replace(/<br[ \/]*>/gi, '\n');
    content = content.replace(/&nbsp;/gi, ' ');
    content = content.replace(/<([^> ]*)[^>]*>/gi, '');  //clean html markup
    content = content.replace(/&lt;/gi, "<").replace(/&gt;/gi, ">"); //unescape some entities
    return content;
  }

  var $textEditor = $('#textEditor');
  function launchAnalysis() {
    //Extract text to analyse
    var toAnalyse = getTextFromHtml($textEditor.html());
    extractAnnotations(toAnalyse, function(err, result) {
      if (!err) {
        //Insert formatted result into the editor
        $textEditor.html(annotationsToHml(result));
      } else {
        if (console.log)
          console.log('Error while doing annotations extraction: ' + err);
      }
    });
  }

  $('#analyseButton').click(launchAnalysis);

});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Extract Annotations demo</title>
    <link href="ner.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="ner.js"></script>
</head>
<body>
    <div class="controls">
        <button type="button" id="analyseButton" data-t>Analyse</button>
        <span class="entity people" data-t>People</span>
        <span class="entity organization" data-t>Organizations</span>
        <span class="entity location" data-t>Locations</span>
        <span class="entity time" data-t>Dates/Times</span>
        <span class="entity" data-t>Others</span>
    </div>
    <div id="textEditor" contenteditable="true" class="textEditor">
        Bodies from the MH17 crash are being kept on this train, as Natalia Antelava reports<br>
        Pro-Russian rebels have allowed Dutch investigators to examine bodies from the crashed Malaysia Airlines plane at a railway station in eastern Ukraine.<br>
        The three Dutch experts said the train might leave the town of Torez later.<br>
        All 298 people on board flight MH17 died when it crashed over the rebel-held area on 17 July. The US and other nations say there is growing evidence of Russian complicity in the crash.<br>
        Meanwhile, heavy fighting is reported in the main rebel-held city of Donetsk.<br>
        The clashes - involving heavy weapons - are continuing near the city's airport and the railway station, eyewitnesses say.<br>
        At least three civilians were reported killed, and one multi-storey building was seen on fire. Residents are fleeing the city, report BBC correspondents on the ground.<br>
        "People are panicking" due to heavy fighting near Donetsk, reports Fergal Keane<br>
        The fighting in eastern Ukraine erupted in April and is believed to have claimed more than 1,000 lives.<br>
        In other developments on Monday:<br>
        A separate group of 31 international investigators is now in the eastern city of Kharkiv. They are expected to proceed closer to the crash site shortly<br>
        Content from Twitter. Learn more about content from Twitter.<br>
        The Dutch experts from the Disaster Victims Identification team are the first international investigators to arrive in the region where the Boeing 777 went down after being reportedly hit by a missile.<br>
        Monitors from the Organisation for Security and Co-operation in Europe (OSCE) have been at the accident site, but their access to the wreckage was limited by the rebels.<br>
        On Monday, the Dutch experts examined some of the 196 bodies kept in refrigerator wagons in Torez, some 15km away from the crash site.<br>
        "I think the storage of the bodies is of good quality," team leader Peter van Leit said after the inspection.<br>
        The investigators added that they had urged the rebels to allow the train to leave.<br>
        Correspondents in Torez said the smell of decay emanating from the carriages was overwhelming.<br>
        The Dutch experts also later visited the crash site, where some passengers' remains were still lying in bags exposed to summer heat.<br>
        US Secretary of State John Kerry said the US had seen major military supplies moving into Ukraine from Russia in the last month, including a convoy of armoured personnel carriers, tanks and rocket launchers<br>
        A Malaysian team of 133 officials and experts, comprising of search and recovery personnel, forensics experts, technical and medical experts has arrived in Ukraine. A separate UK group of air accident investigators is also there.<br>
        But the government in Kiev says it has been unable to establish a safe corridor to the crash site.<br>
        There has been international outcry over the way rebels have handled the situation, delaying access to the site and allowing untrained volunteers to comb through the area.<br>
        But US President Barack Obama has accused rebels of tampering with other potential evidence and called for the international experts to be granted "immediate and full" access to the site.<br>
        "What exactly are [the rebels] trying to hide?" he said at a press briefing on Monday.<br>
        Mr Obama warned that Russia would "only further isolate itself" if it failed to compel the separatists to co-operate.<br>
        British Prime Minister David Cameron said there was strong evidence that pro-Russian separatists shot down the plane with an anti-aircraft system known as Buk.<br>
        Russia on Monday again denied allegations that it had supplied such missiles or "any other weapons" to the rebels.<br>
        Separately, the Netherlands announced on Monday it had opened an investigation into the disaster, which killed 193 Dutch nationals.<br>
        A spokesman for Dutch prosecutors said the charges could include murder, war crimes and intentionally downing an airliner.<br>
        The Netherlands claims the right under international law to prosecute anyone suspected of committing a war crime against a Dutch citizen.<br>
        The Boeing 777 was flying from Amsterdam to Kuala Lumpur when it crashed between Krasni Luch in Luhansk region and Shakhtarsk in the region of Donetsk.<br>
        Is it possible to strengthen your brain's synapses while you slumber?<br>
    </div>
</body>
</html>
.textEditor {
    display: block;
    border: 1px solid #ddd;
    overflow: auto;
    font-family: monospace;
    font-size: 14px;
    padding: 10px;
    margin: 10px 20px 10px 20px;
    line-height: normal;
    min-height: 50px;
    max-height: 400px;
}

.entity {
    box-shadow: 4px 4px 4px #888888;
    padding: 1px;
    color: #0C9900;
    background-color: #FCFC70;
}

.entity.people {
    color: #517AFF;
    background-color: #E0F3FF;
}

.entity.organization {
    color: #029A00;
    background-color: #A6FF91;
}

.entity.location {
    color: #FF7600;
    background-color: #FEE39F;
}

.entity.time {
    color: #FC0000;
    background-color: #FFC4C4;
}

.controls {
    margin: 5px 20px 5px 20px;
}
.controls span {
    margin-left: 10px;
}

Anonymize a text

This example shows an example of text anonymisation using the /nlp/ner/extract/annotations api.

Demo

Source code

jQuery(function($) {
  'use strict';

  // Function to call the SYSTRAN NLP Api extractAnnotations
  function extractAnnotations(content, callback) {
    $.ajax({
      method:'GET',
      url: 'https://api-platform.systran.net/nlp/ner/extract/annotations?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&lang=en',
      data: {
        input: content
      },
      success: function(data) {
        callback(null, data);
      },
      error: function(xhr, status, err) {
        callback(err);
      }
    });
  }

  function getTextFromHtml(content) {
    content = content.replace(/<div>(?:<br>)?/gi, '\n').replace(/<\/div>/gi, '');
    content = content.replace(/<p>&nbsp;<\/p>/gi, '\n').replace(/<p>/gi, '').replace(/\n*<\/p>/gi, '\n');
    content = content.replace(/<br[ \/]*>/gi, '\n');
    content = content.replace(/&nbsp;/gi, ' ')
    content = content.replace(/<([^> ]*)[^>]*>/gi, '');  //clean html markup
    content = content.replace(/&lt;/gi, "<").replace(/&gt;/gi, ">"); //unescape some entities
    return content;
  }

  // Function to build html from the result returned by the extractAnnotations api
  function annotationsToHml(annotations) {
    var html = '';
    var i, j, li, lj;
    var personId = 0;
    var personMapping = {};
    if (annotations && annotations.annotations) {
      for (i = 0, li = annotations.annotations.length; i < li; ++i) {
        var annotation = annotations.annotations[i];
        if (annotation.entities) {
          var lastIdx = 0;

          annotation.entities.sort(function(a,b) {
            if (a.start < b.start)
              return -1;
            if (a.end > b.start)
              return -1;
            return (a.start === b.start && a.end === b.end) ? 0: 1;
          });

          for (j = 0, lj = annotation.entities.length; j < lj; ++j) {
            var entity = annotation.entities[j];
            if (entity.type === 'ENAMEX_person') {
              if (entity.start > lastIdx) {
                //Replace person by anonymized form
                var personName = annotation.source.substring(entity.start, entity.end);
                var anonymizedName = personMapping[personName];
                if (!anonymizedName) {
                  anonymizedName = 'XXX-' + (++personId);
                  personMapping[personName] = anonymizedName;
                }
                html += annotation.source.substring(lastIdx, entity.start) + '<span class="anonymize-people">' + anonymizedName + '</span>';
                lastIdx = entity.end;
              }
            } else if (entity.type === 'title') {
              //remove title
              html += annotation.source.substring(lastIdx, entity.start);
              lastIdx = entity.end;
            }
          }
          html += annotation.source.substring(lastIdx);
        } else {
          html += annotation.source;
        }
        html += '<br>\n';
      }
    }
    return html;
  }

  var $editor = $('#anonymizeEditor');
  $('#anonymizeButton').click(function() {
    //Extract text to analyse
    var toAnalyse = getTextFromHtml($editor.html());
    extractAnnotations(toAnalyse, function(err, result) {
      if (!err) {
        //Insert formatted result into the editor
        $editor.html(annotationsToHml(result));
      } else {
        if (console.log)
          console.log('Error while doing annotations extraction: ' + err);
      }
    });
  })

});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Extract Annotations demo</title>
    <link href="anonymization.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="anonymization.js"></script>
</head>
<body>
    <div class="anonymize-controls">
        <button type="button" id="anonymizeButton" data-t>Anonymize text</button>
    </div>
    <div id="anonymizeEditor" contenteditable="true" class="anonymize-editor">
        In her first interviews since being accused of misrepresenting her racial background and stepping down as an N.A.A.C.P. official, Ms. Dolezal did not back down on Tuesday, stating “I identify as black,” although she has white parents.<br>
        When Matt Lauer of NBC’s “Today” show asked, “When did you start deceiving people?” Ms. Dolezal would not concede that she had done so.<br>
        “I do take exception to that, because it’s a little more complex than me identifying as black, or answering a question of, ‘Are you black or white?’” she said.<br>
        In a second interview on Tuesday, on MSNBC, Melissa Harris-Perry asked, “Are you a con artist?”<br>
        “I don’t think so,” Ms. Dolezal said.<br>
    </div>
</body>
</html>
.anonymize-editor {
    display: block;
    border: 1px solid #ddd;
    overflow: auto;
    font-family: monospace;
    font-size: 14px;
    padding: 10px;
    margin: 10px 20px 10px 20px;
    line-height: normal;
    min-height: 50px;
    max-height: 400px;
}

.anonymize-people {
    box-shadow: 4px 4px 4px #888888;
    padding: 1px;
    color: #517AFF;
    background-color: #E0F3FF;
}

.anonymize-controls {
    margin: 5px 20px 5px 20px;
}

Detect language for a document

This example shows the use of the /nlp/lid/detectLanguage/document api to detect the language of a document.

Demo

Source code

jQuery(function($) {
  'use strict';

  // Function to call the SYSTRAN NLP Api lid/document
  function getLanguage(content, callback) {
    $.ajax({
      method:'GET',
      url: 'https://api-platform.systran.net/nlp/lid/detectLanguage/document?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
      data: {
        input: content
      },
      success: function(data) {
        callback(null, data);
      },
      error: function(xhr, status, err) {
        callback(err);
      }
    });
  }

  function getTextFromHtml(content) {
    content = content.replace(/<div>(?:<br>)?/gi, '\n').replace(/<\/div>/gi, '');
    content = content.replace(/<p>&nbsp;<\/p>/gi, '\n').replace(/<p>/gi, '').replace(/\n*<\/p>/gi, '\n');
    content = content.replace(/<br[ \/]*>/gi, '\n');
    content = content.replace(/&nbsp;/gi, ' ');
    content = content.replace(/<([^> ]*)[^>]*>/gi, '');  //clean html markup
    content = content.replace(/&lt;/gi, "<").replace(/&gt;/gi, ">"); //unescape some entities
    return content;
  }

  var $textEditor = $('#textEditor');
  var $result = $('#result');
  var $analysing = $('#analysing');

  function launchAnalysis() {
    $result.addClass('hidden');
    $analysing.removeClass('hidden');
    //Extract text to analyse
    var toAnalyse = getTextFromHtml($textEditor.html());
    getLanguage(toAnalyse, function(err, result) {
      $analysing.addClass('hidden');
      if (!err) {
        //Show result
        var detectedLanguages = result && result.detectedLanguages;
        if (detectedLanguages && detectedLanguages.length > 1) {
          $result.html(detectedLanguages[0].lang.toUpperCase() + ' language detected');
        } else {
          $result.html('Unknown');
        }
        $result.removeClass('hidden');
      } else {
        if (console.log)
          console.log('Error while doing language detection: ' + err);
      }
    });
  }

  $('#analyseButton').click(launchAnalysis);

});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Language detection demo</title>
    <link href="lid.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="lid.js"></script>
</head>
<body>
    <div class="controls">
        <button type="button" id="analyseButton" data-t>Detect language</button>
        <div id="result" class="hidden"></div>
        <div id="analysing" class="hidden" data-t>Analysing...</div>
    </div>
    <div id="textEditor" contenteditable="true" class="textEditor">
        Bodies from the MH17 crash are being kept on this train, as Natalia Antelava reports<br>
        Pro-Russian rebels have allowed Dutch investigators to examine bodies from the crashed Malaysia Airlines plane at a railway station in eastern Ukraine.<br>
        The three Dutch experts said the train might leave the town of Torez later.<br>
        All 298 people on board flight MH17 died when it crashed over the rebel-held area on 17 July. The US and other nations say there is growing evidence of Russian complicity in the crash.<br>
        Meanwhile, heavy fighting is reported in the main rebel-held city of Donetsk.<br>
    </div>
</body>
</html>
.textEditor {
    display: block;
    border: 1px solid #ddd;
    overflow: auto;
    font-family: monospace;
    font-size: 14px;
    padding: 10px;
    margin: 10px 20px 10px 20px;
    line-height: normal;
    min-height: 50px;
    max-height: 400px;
}

#result {
    display: inline-block;
    margin-left: 15px;
}

#analysing {
    color: #428bca;
    font-style: italic;
    display: inline-block;
    margin-left: 15px;
}

#result.hidden,
#analysing.hidden {
    display:none;
}

.controls {
    margin: 5px 20px 5px 20px;
}

Detect language for each paragraph of a document

This example shows the use of the /nlp/lid/detectLanguage/paragraph api to detect the language of each paragraph in a document.

Demo

Source code

jQuery(function($) {
  'use strict';

  // Function to call the SYSTRAN NLP Api lid/document
  function getLanguage(content, callback) {
    $.ajax({
      method:'GET',
      url: 'https://api-platform.systran.net/nlp/lid/detectLanguage/paragraph?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
      data: {
        input: content
      },
      success: function(data) {
        callback(null, data);
      },
      error: function(xhr, status, err) {
        callback(err);
      }
    });
  }

  // Function to build html from the result returned by the extractAnnotations api
  function detectedLangsToHml(data) {
    var html = '';
    var i, li, j, lj;

    if (data && data.paragraphs) {
      for (i=0, li=data.paragraphs.length; i<li; ++i) {
        var paragraph = data.paragraphs[i];
        var lang = '??';
        if (paragraph.detectedLanguages && paragraph.detectedLanguages.length)
          lang = paragraph.detectedLanguages[0].lang.toUpperCase();

        html += '<span class="lang">' + lang + '</span>' + paragraph.source + '<br>';
      }
    }
    return html;
  }

  function getTextFromHtml(content) {
    content = content.replace(/<span class="lang">.*?<\/span>/gi, '');
    content = content.replace(/<div>(?:<br>)?/gi, '\n').replace(/<\/div>/gi, '');
    content = content.replace(/<p>&nbsp;<\/p>/gi, '\n').replace(/<p>/gi, '').replace(/\n*<\/p>/gi, '\n');
    content = content.replace(/<br[ \/]*>/gi, '\n');
    content = content.replace(/&nbsp;/gi, ' ');
    content = content.replace(/<([^> ]*)[^>]*>/gi, '');  //clean html markup
    content = content.replace(/&lt;/gi, "<").replace(/&gt;/gi, ">"); //unescape some entities
    return content;
  }

  var $textEditor = $('#textEditor');
  var $analysing = $('#analysing');

  function launchAnalysis() {
    $analysing.removeClass('hidden');
    //Extract text to analyse
    var toAnalyse = getTextFromHtml($textEditor.html());
    getLanguage(toAnalyse, function(err, result) {
      $analysing.addClass('hidden');
      if (!err) {
        //Show result
        $textEditor.html(detectedLangsToHml(result));
      } else {
        if (console.log)
          console.log('Error while doing language detection: ' + err);
      }
    });
  }

  $('#analyseButton').click(launchAnalysis);

});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Language detection demo</title>
    <link href="lid-paragraph.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="lid-paragraph.js"></script>
</head>
<body>
    <div class="controls">
        <button type="button" id="analyseButton" data-t>Detect language</button>
        <div id="analysing" class="hidden" data-t>Analysing...</div>
    </div>
    <div id="textEditor" contenteditable="true" class="textEditor">
        Bodies from the MH17 crash are being kept on this train, as Natalia Antelava reports<br>
        ベリリウムは原子番号4の元素。元素記号は Be。第2族元素に属し、原子量は約9.012である。<br>
        Le palais des beaux-arts de Lille est un musée municipal d'art et d'antiquités situé place de la République à Lille.<br>
        Это белая птица с чёрными концами крыльев, длинной шеей, длинным тонким красным клювом и длинными красноватыми ногами<br>
    </div>
</body>
</html>
.textEditor {
    display: block;
    border: 1px solid #ddd;
    overflow: auto;
    font-family: monospace;
    font-size: 14px;
    padding: 10px;
    margin: 10px 20px 10px 20px;
    line-height: normal;
    min-height: 50px;
    max-height: 400px;
}

#analysing {
    color: #428bca;
    font-style: italic;
    display: inline-block;
    margin-left: 15px;
}

#analysing.hidden {
    display:none;
}

.lang {
    margin: 0px 5px 0px 0px;
    padding: 0px 4px 0px 4px;
    color: #517AFF;
    background-color: #E0F3FF;
    border-radius: 4px;
    box-shadow: 2px 2px 4px #888888;
}

.controls {
    margin: 5px 20px 5px 20px;
}

Transcribe text

This example shows the use of the /nlp/transcription/transcribe api to transcribe a text from one language to another.

Demo

Source code

jQuery(function($) {
  'use strict';

  // Function to call the SYSTRAN NLP Api lid/document
  function transcribe(source, target, content, callback) {
    $.ajax({
      method:'GET',
      url: 'https://api-platform.systran.net/nlp/transcription/transcribe?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
      dataType: 'text',
      data: {
        source: source,
        target: target,
        input: content
      },
      success: function(data) {
        callback(null, data);
      },
      error: function(xhr, status, err) {
        callback(err);
      }
    });
  }

  function getTextFromHtml(content) {
    content = content.replace(/<div>(?:<br>)?/gi, '\n').replace(/<\/div>/gi, '');
    content = content.replace(/<p>&nbsp;<\/p>/gi, '\n').replace(/<p>/gi, '').replace(/\n*<\/p>/gi, '\n');
    content = content.replace(/<br[ \/]*>/gi, '\n');
    content = content.replace(/&nbsp;/gi, ' ');
    content = content.replace(/<([^> ]*)[^>]*>/gi, '');  //clean html markup
    content = content.replace(/&lt;/gi, "<").replace(/&gt;/gi, ">"); //unescape some entities
    return content;
  }

  var $textEditor = $('#textEditor');
  var $transcribing = $('#transcribing');
  var $source = $('#source');
  var $target = $('#target');

  function launchTranscription() {
    $transcribing.removeClass('hidden');
    //Extract text to analyse
    var toTranscribe = getTextFromHtml($textEditor.html());
    transcribe($source.val(), $target.val(), toTranscribe, function(err, result) {
      $transcribing.addClass('hidden');
      if (!err) {
        //Show result
        $textEditor.text(result);
      } else {
        if (console.log)
          console.log('Error while doing transcription: ' + err);
      }
    });
  }

  $('#transcribeButton').click(launchTranscription);

});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Transcription demo</title>
    <link href="transcription.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="transcription.js"></script>
</head>
<body>
    <div class="controls">
        <select id="source">
            <option value="en" selected data-t>English</option>
        </select>
        <select id="target">
            <option value="ru" selected data-t>Russian</option>
            <option value="ja" data-t>Japanese</option>
            <option value="ko" data-t>Korean</option>
            <option value="zh" data-t>Chinese</option>
        </select>
        <button type="button" id="transcribeButton" data-t>Transcribe</button>
        <div id="transcribing" class="hidden" data-t>Transcribing...</div>
    </div>
    <div id="textEditor" contenteditable="true" class="textEditor">
        John Doe
    </div>
</body>
</html>
.textEditor {
    display: block;
    border: 1px solid #ddd;
    overflow: auto;
    font-family: monospace;
    font-size: 14px;
    padding: 10px;
    margin: 10px 20px 10px 20px;
    line-height: normal;
    min-height: 50px;
    max-height: 400px;
}

#transcribing {
    color: #428bca;
    font-style: italic;
    display: inline-block;
    margin-left: 15px;
}

#transcribing.hidden {
    display:none;
}

.controls {
    margin: 5px 20px 5px 20px;
}