Twit box

by Sasá Rafalsky

HTML

<!-- Contenteditable-->
<div class="tweet-box">
  <div class="placeholder" id="tweet_highlights"></div>
  <div id="tweet" contenteditable="true"></div>
  
  <div class="tweet_action">
    <div id="tweet_chars"></div>
    <button id="tweet_btn" onclick="createPost()" disabled="disabled">Post</button>
  </div>
  
</div>

CSS

*{
  box-sizing: border-box;
  font-family: sans-serif;
}

.tweet-box{
  padding: 0;
  max-width: 590px;
  margin: 0 auto;
  border-radius: 8px;
  position: relative;
}

#tweet_highlights{
  position: absolute;
  width: calc(100% - 20px);
  padding: 13px 9px;
  
  font-size: 16px;
  color: transparent;
  
  overflow: hidden;
  white-space: pre-wrap;
  pointer-events: none;
}
#tweet_highlights.placeholder
{
  color: #afafaf;
}

#tweet
{
  padding: 12px 8px;
  width: 100%;
  min-height: 100px;
  
  border-radius: 8px;
  background-color: #f5f5f5;
  border: 1px solid #afafaf;
  font-size: 16px;
  margin-bottom: 10px;
  
  overflow: hidden;
  white-space: pre-wrap;
}

@media (min-width: 560px)
{
  .tweet-box
  {
    padding: 40px;
  }
  #tweet_highlights
  {
    width: calc(100% - 80px);
  }
}

.link
{
  background-color: rgba(245, 245, 245, 0.35);
  color: #1c94e0;
}
.danger
{
  background-color: rgba(255, 0, 0, 0.25);
}
.danger .link
{
  background: transparent;
}

.tweet_action
{
  text-align: right;
}

#tweet_chars
{
  display: inline-block;
  text-align: right;
  line-height: 20px;
  margin-right: 8px;
  color: #657786;
}
#tweet_chars.warning
{
  color: red;
}

#tweet_btn
{
  display: inline-block;
  width: 75px;
  border-radius: 0;
  font-size: 14px;
  font-weight: bold;
  line-height: 20px;
  padding: 6px 16px;
  text-align: center;
  box-shadow: none;
  cursor: pointer;
}
#tweet_btn:hover
{
  background-color: #006dbf;
  border: 1px solid #006dbf;
}
#tweet_btn:disabled
{
  opacity: 0.45;
}

JavaScript

const maxTweetLength = 280;
const urlLength = 23;
const placeholder = "What's happening?";
const urlRegex = /(http|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&amp;:\/~+#-]*[\w@?^=%&amp;\/~+#-])?/g;

var overLimit = false;
var diff = 0;
var virtualMaxTweetLength = maxTweetLength;

var tweet = document.getElementById('tweet');
var highlighter = document.getElementById('tweet_highlights');
var tweetChars = document.getElementById('tweet_chars');
var tweetBtn = document.getElementById('tweet_btn');

tweet.addEventListener('input', refresh);
tweet.addEventListener('keydown', refresh);
tweetChars.innerHTML = maxTweetLength;
highlighter.innerHTML = placeholder;

function refresh(){
    diff = calcChars();
    tweetChars.innerHTML = diff;
    handleStates(diff);
    parseTweet();
    handlePlaceholder(diff);
}

function calcChars(){
    var charCount = tweet.innerText.length;
    var urls = tweet.innerText.match(urlRegex);
    var alter = 0;
    virtualMaxTweetLength = maxTweetLength;
  
    if(urls){
        var totalUrls = 0;
        alter = urlLength * urls.length;
        
        for(var i in urls)
            totalUrls += urls[i].length;
        
        charCount = (tweet.innerText.length - totalUrls) + alter;
        virtualMaxTweetLength = (maxTweetLength - alter) + totalUrls;
    }
  
    return maxTweetLength - charCount;
}

function handleStates(diff){
    if(diff<0){
        overLimit = true;
        tweetChars.className = 'warning';
    } else if(diff>=0) {
        overLimit = false;
        tweetChars.className = '';
    }
  
    if(diff < 0 || diff == maxTweetLength)
        tweetBtn.disabled = true;
    else
        tweetBtn.disabled = false;
}

function handlePlaceholder(diff){
    if(diff == maxTweetLength) {
        highlighter.innerHTML = placeholder;
        highlighter.className = 'placeholder';
    } else {
        highlighter.className = '';
    }
}

function parseTweet(){   
    var str = tweet.innerText;
    //str = str.replace(/</g,...