Knockout.JS - Bindings - Forms (EJERCICIO)

Greek Letters

by Marventus

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<h2>Bindings - Forms - Ejercicio</h2>
<div class="consignas">
    <h4>Consignas:</h4>
    <ol class="consignas">
        <li>Mostrar un mensaje cuando se agregan o remueven letras.</li>
        <li>Si se remueve una letra, el mensaje debe ser rojo.</li>
        <li>Si se agrega, azul o verde.</li>
        <li>El mensaje debe contener el nombre de la letra modificada.</li>
    </ol>
</div>
<hr>
<form>
    <h4>New letter:</h4>
    <input data-bind='value: letterToAdd, valueUpdate: "afterkeydown"' />
    <p id="status-container"><span data-bind="text: statusMessage, css: {'removing-span': removing}"></span>&nbsp;</p>
    <div class="buttons">
        <button id="add" data-bind="enable: activeList() === 'available' && letterToAdd() !== '', click: addLetter">Add</button>
        <button id="remove" data-bind="disable: activeList() === 'available' || letterToAdd() === '', click: removeLetter">Remove</button>
        <button id="clear" data-bind="enable: letterToAdd() !== '', click: clearLetter">Clear</button>
    </div>
</form>
<main class="summary">
    <div id="available-letters" class="fifty">
        <h4>Available Letters:</h4>
        <select multiple="multiple" data-bind="foreach: availLetters, hasFocus: activeList() === 'available'">
            <option data-bind="text: $data, event: { click: $root.updateNew, dblclick: $root.updateLists }">
            </option>
        </select>
    </div>
    <div id="selected-letters" class="fifty last">
        <h4>Selected Letters:</h4>
        <select multiple="multiple" data-bind="foreach: selLetters, hasFocus: activeList() === 'selected'">
            <option data-bind="text: $data, event: { click: $root.updateNew, dblclick: $root.updateLists }">
            </option>
        </select>
    </div>
</main>

CSS

body {
    background: rgba(247, 243, 222, 0.9);
    color: #777;
    font: 16px Arial, Helvetica, sans-serif;
    max-width: 100%;
    margin: 0 auto;
    padding: 1em 3em;
    text-align: center;
}

a,
a:hover,
a:visited,
a:active,
a:focus,
h2 {
    color: rgb(139, 116, 61);
}

button,
input,
select {
    box-sizing: border-box;
    border: 1px solid rgba(139, 116, 61, 0.6);
    padding: 0.5em 1em;
    width: 100%;
}

button:focus,
input:focus,
select:focus {
    border: 1px solid rgba(139, 116, 61, 1);
    box-shadow: 0;
    outline: 0;
}

select {
    height: 10em;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    font-weight: bold;
    margin: 0;
}

h2 {
    font-size: 2em;
    margin: 0;
}

h4 {
    font-size: 1.1em;
    margin-bottom: 1em;
}

form,
main,
.consignas {
    margin: 0.7em auto;
    max-width: 80%;
}

main {
    padding: 0.5em 1em;
    position: relative;
}

main p {
    margin-top: 0;
}

p {
    font-size: 0.9rem;
    margin: 0.5em 0;
}

.buttons {
    box-sizing: border-box;
    height: 2em;
}

.buttons button {
    border-color: rgba(0, 0, 0, 0.15);
    border-radius: 3px;
    box-sizing: border-box;
    color: #fff;
    cursor: pointer;
    display: inline-block;
    font-size: 0.7em;
    height: 2em;
    margin: 1em 0 0;
    max-width: 25%;
    outline: none;
    text-transform: uppercase;
}

.buttons button:disabled {
    cursor: default;
    opacity: 0.3;
}

.buttons button:last-of-type {
    margin-right: 0;
}

#add {
    background: #8d8;
}

#clear {
    background: rgba(139, 116, 61, 0.9);
}

#remove {
    background: #f88;
}

.fifty {
    float: left;
    margin-top: 1em;
    width: 48%;
}

.fifty.last {
    float: right;
}

.consignas {
    margin-bottom: 2em;
    text-align: justify;
    max-width: 100%;
}

.consignas ol {
    margin-left: 0;
    padding-left: 1.5em;
}

.consignas ol li {
    font-style: italic;
}

#status-container span {
    color: #6b6;
}

#status-container span.removing-span {
    color: #b66;
}

JavaScript

ko.bindingHandlers.fadeVisible = {
	init: function(){},
    update: function(element, valueAccessor) {
        // On update, fade in/out
        var shouldDisplay = valueAccessor();
        shouldDisplay ? $(element).fadeIn() : $(element).fadeOut();
    }
};

var greekLettersStr = "alpha, beta, gamma, delta, epsilon, zeta, eta, theta, iota, kappa, lambda, mu, nu, xi, omicron, pi, rho, sigma, tau, upsilon, phi, chi, psi, omega",
    greekLettersArr = greekLettersStr.replace(/\s/g, "").split(","),
    greekLettersModel = function() {
        var self = this;
        self.availLetters = ko.observableArray(greekLettersArr);
        self.selLetters = ko.observableArray([]);
        self.letterToAdd = ko.observable("");
        self.removing = ko.observable();
        self.activeList = ko.observable("available");
        self.statusMessage = ko.observable("");
        self.addLetter = function(data) {
            var letter = self.letterToAdd() !== "" ? self.letterToAdd() : data;
            if( !self.letterExists(letter, self.selLetters()) ) {
                self.selLetters.push(letter);
            }
            self.letterToAdd("");
            self.removing(false);
            self.statusMessage("The letter " + letter + " has been ADDED.");

            setTimeout(function(){
            	self.statusMessage("");
            }, 3000);
        };
        self.clearLetter = function() {
            self.letterToAdd("");
        };
        self.letterExists = function(letter, list) {
            if (letter !== "")
                return $.inArray(letter, list) > -1;
        };
        self.removeLetter = function(data) {
            var letter = self.letterToAdd() || data;
            if (self.letterExists(letter, self.selLetters())) {
                self.selLetters.remove(letter);
                self.letterToAdd("");
            }
            self.removing(true);
            self.statusMessage("The letter " + letter + " has been REMOVED.");
            
           ...