Soundex Calculator

by Kyle Mitofsky

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="steps">
  	<div class="empty"></div>
    <div class="step">Step 1:</div>
    <div class="step">Step 2:</div>
    <div class="step">Step 3:</div>
    <div class="step">Step 4:</div>
</div>

<div class="soundex" data-bind="template: { name: 'soundex', data: word1 }"> </div>
<div class="soundex" data-bind="template: { name: 'soundex', data: word2 }"> </div>

<div class="steps-desc">
  	<div class="empty"></div>
    <div class="step">Drop vowels and H &amp; Y</div>
    <div class="step">Replace letters with numbers</div>
    <div class="step">Remove redundant numbers</div>
    <div class="step">Trim/Pad to 4 digits</div>
</div>


<script type="text/html" id="soundex">
	<input class="input" data-bind='value: text, valueUpdate: "input"' />
	<div class="step" data-bind="template: { name: 'characters', data: step1Result }"> </div>
	<div class="step" data-bind="template: { name: 'characters', data: step2Result }"> </div>
	<div class="step" data-bind="template: { name: 'characters', data: step3Result }"> </div>
	<div class="step" data-bind="template: { name: 'characters', data: step4Result }"> </div>
</script>

<script type="text/html" id="characters">
  <span class="chars" data-bind="foreach: $data.chars, css: { 'final' : $data.step === 4  }" >
  	<span class="char" data-bind="text: $data !== '' ? $data : '&nbsp;', css: { 'match' : $root.diff()[$index()] } "></span>
  </span>
</script>


<hr class="clear">


<code class="results">
  <span class="keyword" >SELECT</span>
  <span class="function" >SOUNDEX</span><span class="operator">(</span><span class="string" >'<span data-bind="text: word1.text"></span>'</span><span class="operator">)</span><br>

  <span class="keyword" >SELECT</span>
  <span class="function" >SOUNDEX</span><span class="operator">(</span><span class="string" >'<span data-bind="text: word2.text"></span>'</span><span class="operator">)</span><br>

 ...

CSS

@font-face {
	font-family: 'Smiler';
	src: url('//2ttf.com/webfont/EoVxytkhYOw/webfont.ttf') format('truetype');
}

/* sample using the font */
body{
	font-family: 'Smiler';
}
@media (max-width: 660px) { 
    .steps-desc {
        display: none;
    }
}
.steps, .steps-desc, .soundex, .results {
    margin: 10px;
    float: left;
}
.clear {
  clear:both;
}
.empty {
  height: 30px;
}
.input {
    font-size: 16px;
    width: 120px;
}
.step {
    height: 30px;
}
.steps-desc {
  color: #1f1f1f;
  font-size: 14px;
}

.chars .char {
  font-family: consolas, monospace;
  display:inline-block;
  width:15px;
  margin: 0 1.5px;
  text-align: center;
  vertical-align: bottom;
  font-size:20px;
  border-bottom: solid 1.5px black;
  border-radius:3px;
}

.chars.final .char {
  border-bottom-width: 3px;
  border-radius: 4px;
  border-bottom-color: #ec2222;
}


.chars.final .char.match {
  border-bottom-color: #2bd22b;
}

.chars .char:first-child {
   font-weight: bold;
   color: #002cc6;
}

.chars:not(.final) .char:first-child:after,
.chars.final .char:nth-of-type(4):after {
    content: ' ';
    position: relative;
    left: 6px;
    top: 2px;
    border: 1px solid #5b5b5b;
}

code {
  font-family: consolas, monospace;
}
code .keyword { color: #0000FF  }
code .function { color: #FF00FF  }
code .string { color: #FF0000  }
code .operator { color: #988080  }
code .comment { color: #008000  }

JavaScript

if (!Array.prototype.filter){
  Array.prototype.filter = function(func, thisArg) {
    'use strict';
    if ( ! ((typeof func === 'Function' || typeof func === 'function') && this) )
        throw new TypeError();
   
    var len = this.length >>> 0,
        res = new Array(len), // preallocate array
        t = this, c = 0, i = -1;
    if (thisArg === undefined){
      while (++i !== len){
        // checks to see if the key was set
        if (i in this){
          if (func(t[i], i, t)){
            res[c++] = t[i];
          }
        }
      }
    }
    else{
      while (++i !== len){
        // checks to see if the key was set
        if (i in this){
          if (func.call(thisArg, t[i], i, t)){
            res[c++] = t[i];
          }
        }
      }
    }
   
    res.length = c; // shrink down array to proper size
    return res;
  };
} 
 


var ViewModel = function(seedWord1, seedWord2) {
		var self = this;
    
    var dict = {
        A: '', E: '', I: '', O: '', U: '', H: '', W: '',
        B: 1, F: 1, P: 1, V: 1,
        C: 2, G: 2, J: 2, K: 2, Q: 2, S: 2, X: 2, Z: 2,
        D: 3, T: 3,
        L: 4,
        M: 5, N: 5,
        R: 6
      };

		
    
    
    
    var createWord = function(text) {
    
        var word = {}
        // track input
        word.text = ko.observable(text);
        
        // basics
        word.upper = ko.computed(function() { return word.text().toUpperCase().split(''); }, self);
        word.first = ko.computed(function() { return word.upper().slice(0,1); }, self);
        word.chars = ko.computed(function() { return word.upper().slice(1); }, self);

				// step pieces
        word.step1 = ko.computed(function() {
            return word.chars().map(function (el, i) {
                return el.replace(/[AEIOUYHW]/gi,'');
             })
        }, self);

        word.step2 = ko.computed(function() {
            return word.chars().map(function (el, i) {
                return ( dict[el] );
             });
       ...