Vigenere Cipher - [Vanilla JS]

Introduction

Vigenere Cipher is used to encode original string to random string, with a given keyword.

Components

  1. Keyword Input Component
  2. Keywords Display Component
  3. Source Text Input Component
  4. Source Text Display Component
  5. Cipher Text Display Component

Model & Events

  1. Model

    1
    2
    3
    4
    5
    6
    7
    let valid = true
    let kw_to_be = []
    let kw = []
    let kw_idx = 0
    let kw_char_row = []
    let kw_num_row = []
    let ciph_row = []
  2. Events

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 2.1 keyword input event handler
    function inputHandler(e){}

    // 2.2 source click button event handler
    function clickHandler(id){}

    // 2.3 update button event handler
    function updateBtnHandler(){}

    // 2.4 clear button event handler
    function clearBtnHandler(){}

Layout & Logic

Code Implementation

  1. HTML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    <!DOCTYPE html>
    <html>

    <head>
    <link rel="stylesheet" href="style.css">

    </head>

    <body>

    <!-- 1. Keyword Input Component -->
    <h1><span>Configuration</span></h1> <p>Keyword</p>
    <div className="comp">
    <input id="inputBox" type="text" />
    <button id="updateBtn" >Update</button>
    <ul id="validation">
    <li>Length of keyword should be in range: 3~8 </li>
    <li>Characters should be uppercase</li>
    </ul>
    </div>

    <!-- 2. Keyword Display Component -->
    <table class="comp"><tbody>
    <tr id="kwCharRow"></tr>
    <tr id="kwNumRow"></tr>
    </tbody></table>

    <!-- 3. Source Text Input Component -->
    <h1><span>Encoding</span></h1> <p>Source Text</p>
    <table class="comp"><tbody>
    <tr id="srcTextRow"></tr>
    <tr id="ciphTextRow"></tr>
    </tbody></table>



    <!-- 4. Source Text Display Component -->
    <div class="comp">
    <input id="srcText" type="text" disabled />
    <button id="clearBtn">Clear</button>
    </div>

    <!-- 5. Cipher Text Display Component -->
    <div class="comp">
    <p>Cipher Text</p>
    <input id="ciphText" type="text" disabled />
    </div>

    <script src="script.js"></script>
    </body>

    </html>
  2. JS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    (function (){

    // 0.1 const
    const CHAR_NUM = 26
    const CHAR_CODE_A = 65
    const CHAR_CODE_Z = 90
    const CHAR_CODE_ALL = new Array(CHAR_NUM).fill(1).map( ( _, i ) => (CHAR_CODE_A+i) )
    const CHAR_ALL = CHAR_CODE_ALL.map(item=>String.fromCharCode(item))

    // 0.2 helper functions
    const getEle = (id) => document.getElementById(id)
    const setEleVal = (ele, val)=> {ele.value = val}
    const creatEle = (tag) => document.createElement(tag)
    const creatText = (str) => document.createTextNode(str)
    const generateEle = ( tag, data, parent )=>{
    let tempNode = document.createDocumentFragment()
    data.forEach((item, index)=>{
    const td = creatEle('TD')
    const text = creatText(item)
    if( tag === 'srcTextRow' ){
    const btn = creatEle('BUTTON')
    btn.appendChild(text)
    btn.onclick = function(){clickHandler(index)}
    td.appendChild(btn)
    }else{
    td.appendChild(text)
    }
    tempNode.appendChild(td)
    })

    while (parent.firstChild) { parent.removeChild(parent.firstChild) }
    parent.appendChild(tempNode)
    }

    const updateCiphTextRow = (kw_idx)=>{
    const offset = kw[kw_idx].charCodeAt(0) - CHAR_CODE_A
    return CHAR_CODE_ALL.map(item=>{
    const code = (item+offset <= CHAR_CODE_Z) ? (item+offset) : (item+offset-CHAR_NUM)
    return String.fromCharCode(code)
    })
    }

    const updateHighlightIndex = (id)=>{
    kwCharRow.childNodes.forEach( function(item, index){
    item.className = id===index ? "highlight" : ""
    })
    kwNumRow.childNodes.forEach( function(item, index){
    item.className = id===index ? "highlight" : ""
    })
    }

    // 0.3 DOM Elements
    const inputBox = getEle("inputBox")
    const errorHint = getEle("validation")
    const updatBtn = getEle("updatBtn")
    const kwCharRow = getEle("kwCharRow")
    const kwNumRow = getEle("kwNumRow")
    const srcTextRow = getEle("srcTextRow")
    const ciphTextRow = getEle("ciphTextRow")
    const srcText = getEle("srcText")
    const clearBtn = getEle("clearBtn")
    const ciphText = getEle("ciphText")


    /*
    1. models
    */
    let valid = true
    let kw_to_be = []
    let kw = []
    let kw_idx = 0
    let kw_char_row = []
    let kw_num_row = []
    let ciph_row = []


    /*
    2. event handlers & DOM manipulation
    */

    // 2.1 keyword input event handler
    function inputHandler(e){
    kw_to_be = e.target.value.split("")
    const len = kw_to_be.length
    const lenValid = (len>=3 && len<=8)
    const caseValid = kw_to_be.every(
    item => item==item.toUpperCase()
    )
    const validNew = lenValid && caseValid
    if(valid!==validNew){
    valid = validNew
    errorHint.classList.toggle("invalid")
    }
    }
    inputBox.oninput=inputHandler

    // 2.2 source click button event handler
    function clickHandler(id){
    if(kw.length === 0 ) return
    srcText.value = srcText.value + CHAR_ALL[id]
    ciphText.value = ciphText.value + ciph_row[id]
    kw_idx = kw_idx+1 < kw.length ? kw_idx+1 : kw_idx+1-kw.length
    ciph_row = updateCiphTextRow(kw_idx)
    generateEle("ciphTextRow", ciph_row, ciphTextRow)
    updateHighlightIndex(kw_idx)
    }

    // 2.3 update button event handler
    function updateBtnHandler(){
    if(!valid) return
    kw = kw_to_be
    kw_idx = 0
    const kw_num = kw.map(item=>item.charCodeAt(0)-CHAR_CODE_A)
    ciph_row = kw.length===0 ? [] : updateCiphTextRow(kw_idx)
    generateEle("kwCharRow", kw, kwCharRow)
    generateEle("kwNumRow", kw_num, kwNumRow)
    generateEle("ciphTextRow", ciph_row, ciphTextRow)
    updateHighlightIndex(kw_idx)
    srcText.value = ""
    ciphText.value = ""
    }
    updateBtn.onclick=updateBtnHandler

    // 2.4 clear button event handler
    function clearBtnHandler(){
    kw_idx = 0
    ciph_row = updateCiphTextRow(kw_idx)
    updateHighlightIndex(kw_idx)
    generateEle("ciphTextRow", ciph_row, ciphTextRow)
    srcText.value = ""
    ciphText.value = ""
    }
    clearBtn.onclick=clearBtnHandler



    // populate 'A'~'Z' in srcTextRow
    let tempNode = document.createDocumentFragment()
    generateEle("srcTextRow", CHAR_ALL, srcTextRow)


    })();
  3. CSS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    table{
    border: 1px solid black;
    border-collapse:collapse;
    text-align: center;
    }

    table td {
    border: 1px solid black;
    padding: 5px;
    width: 20px;
    height: 20px;
    }

    h1{
    margin-top: 30px;
    }

    h1 span{
    color: grey;
    border-bottom: 1px solid ;
    }

    input[disabled]{
    background-color:#F5F5DC;
    width: 200px;
    }


    ul{
    display: none;
    }

    .invalid{
    display: block;
    color: red;
    }

    .highlight{
    background-color: yellow;
    }

    .comp{
    margin: 5px 0;
    }