2
\$\begingroup\$

Overview

I am looking for feedback on this (personal) project that I am working on that is rapidly becoming quite complex. I am fully aware that this is horrific code quality... I just want to get my bearing so that I can start writing cleaner code.

What it is

I am trying to create a classroom type environment that is able to load lessons. Each lesson is made up of a number of slides (I called them cards) of which the teacher has control (using left/right arrows). There is a chatbox and a countdown timer as well as a media library so that the teacher can load a lesson.

The app makes heavy use of AJAX to load the slides...

All communication between the student and teacher (e.g. moving to the next slide, sending a message, loading a lesson) is handled by pusher/websockets.

The main template:

This is the html structure of the app... I am using flexbox to display a footer (with the controls for the slider as well ass access to the content library, number of users connected, the countdown, etc...) and a websockets/pusher powered chat-box sidebar.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <title>Classroom</title>

        <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
        <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.4.0/animate.min.css" />
        <link rel="stylesheet" href="css/style.css">

        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
    </head>
    <body>

        <!-- chat box sidebar -->
        <div class="chatbox">
            <div class="chatbox-body"></div>
            <div class="chatbox-footer">
                <input id="message">
                <button id="sendMessage" data-name="<?php echo $lesson['chatName']; ?>" data-is-teacher="<?php echo $lesson['isTeacher']; ?>">
                    <i class="fa fa-paper-plane"></i>
                </button>
            </div>
        </div>
        <div class="chatbox-tab">
            <i class="fa fa-bars"></i>
        </div>
        <!-- end chat box sidebar -->

        <!-- main content -->
        <div class="content">

            <!-- library -->
            <div id="libraryModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="libraryModal" aria-hidden="true">
                <div class="modal-dialog modal-lg">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">
                                <i class="fa fa-book"></i>
                                Lesson library
                            </h5>
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            <!-- render the data -->
                            <div class="pull-right">
                                <div class="form-group">
                                    <select class="form-control" id="categories"></select>
                                </div>
                            </div>
                            <br><br><hr>
                            <div class="library">
                                <div class="library-shelf">
                                    <h6>Course</h6><hr>
                                    <div id="courses"></div>
                                </div>
                                <div class="library-shelf">
                                    <h6>Level</h6><hr>
                                    <div id="levels"></div>
                                </div>
                                <div class="library-shelf">
                                    <h6>Chapter</h6><hr>
                                    <div id="chapters"></div>
                                </div>
                                <div class="library-shelf">
                                    <h6>Lesson</h6><hr>
                                    <div id="lessons"></div>
                                </div>
                            </div>
                            <!-- end render the data -->

                            <!-- categories -->
                            <script id="categoriesTemplate" type="text/template">
                                <option class="category-list" value="">All</a>
                                {{#.}}
                                    <option class="category-list" value="{{.}}">{{.}}</a>
                                {{/.}}
                            </script>
                            <!-- end categories -->

                            <!-- courses -->
                            <script id="coursesTemplate" type="text/template">
                                {{#.}}
                                    <a class="course-list" href="#" data="{{course_id}}">{{course_name}}</a>
                                {{/.}}
                            </script>
                            <!-- end courses -->

                            <!-- levels -->
                            <script id="levelsTemplate" type="text/template">
                                {{#.}}
                                    <a class="level-list" href="#" data="{{name}}">{{name}}</a>
                                {{/.}}
                            </script>
                            <!-- end levels -->

                            <!-- chapters -->
                            <script id="chaptersTemplate" type="text/template">
                                {{#.}}
                                    <a class="chapter-list" href="#" data="{{chapter_id}}">{{chapter_name}}</a>
                                {{/.}}
                            </script>
                            <!-- end chapters -->

                            <!-- lessons -->
                            <script id="lessonsTemplate" type="text/template">
                                {{#.}}
                                    <a class="lesson-list" href="#" data="{{lesson_id}}">{{lesson_name}}</a>
                                {{/.}}
                            </script>
                            <!-- end lessons -->

                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                            <button id="loadLesson" type="button" class="btn btn-outline-success">
                                <i class="fa fa-download"></i>
                                Load Lesson
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            <!-- end library -->

            <!-- intro -->
            <div id="lessonIntro"></div>
            <script id="lessonIntroTemplate" type="text/template">
                <div id="card-1" class="container card-container" style="display: none;">
                    <div class="row">
                        <div class="col-md-6 offset-3">
                            <h4 class="animated fadeInDown">{{lesson_name}}</h4><hr>
                            <div class="animated fadeInUp">{{{lesson_objectives}}}</div>
                        </div>
                    </div>
                </div>
            </script>
            <!-- end intro -->

            <!-- lesson content -->
            <div id="cardContent"></div>
            <script id="cardContentTemplate" type="text/template">
                {{#cards}}
                <div id="card-{{card_order_id}}" class="container card-container" style="display:none;">
                    <div class="row">
                        <div class="col-md-6 animated fadeInLeft" id="leftContent-{{card_order_id}}">
                            <h4>{{card_name}}</h4>
                            {{{card_html}}}
                        </div>
                        <div class="col-md-6 animated fadeInRight" id="rightContent-{{card_order_id}}">
                            {{{media}}}
                        </div>
                    </div>
                </div>
                {{/cards}}
            </script>
            <!-- end lesson content -->

            <!-- outro -->
            <div id="lessonOutro"></div>
            <script id="lessonOutroTemplate" type="text/template">
                <div class="container card-container end-card" style="display:none;">
                    <div class="row">
                        <div class="col-12">
                            <h2 class="text-center text-muted animated fadeInDown">Thanks!</h2>
                            <hr>
                            <div class="text-center animated fadeInUp">
                                <h3 class="text-center">End of lesson</h3>
                            </div>
                        </div>
                    </div>
                </div>
            </script>
            <!-- end outro -->

        </div>
        <!-- end main content -->

        <!-- site footer -->
        <div class="footer">
            <div class="footer-group footer-group-left">
                <span id="slideCounter"></span>
                <span>
                    <i class="fa fa-user"></i> &nbsp;
                    <div id="userCount">1</div>
                </span>
                <?php if ($lesson['isTeacher']) : ?>
                <button id="showLibrary">
                    <i class="fa fa-book"></i> &nbsp;
                    Library
                </button>
                <?php endif; ?>
            </div>
            <?php if ($lesson['isTeacher']) : ?>
            <div class="footer-group footer-group-center">
                <button id="prevCard">
                    <i class="fa fa-arrow-left"></i>
                </button>
                <button id="nextCard">
                    <i class="fa fa-arrow-right"></i>
                </button>
            </div>
            <div class="footer-group footer-group-right">
                <span id="countdown"></span>
            </div>
            <?php endif; ?>
        </div>
        <!-- end site footer -->

        <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
        <script src="js/slider.js"></script>
        <script src="js/loadLesson.js"></script>
        <script src="js/library.js"></script>
        <script src="js/chatbox.js"></script>
        <script src="js/countdown.js"></script>

        <script>

            (function(){

                slider.init(
                    '<?php echo $lesson['token']; ?>'
                );

                loadLesson.init(
                    "<?php echo $lesson['token']; ?>",
                    "<?php echo $lesson['studentId']; ?>",
                    "<?php echo $lesson['lessonId']; ?>",
                    'data/cards.php',
                    '', // teach/classroom/updateEvent
                    ''  // teach/classroom/saveProgress
                );

                library.init(
                    "<?php echo $lesson['token']; ?>",
                    "<?php echo $lesson['studentId']; ?>",
                    'data/progress.php',
                    'data/courses.php',
                    'data/levels.php',
                    'data/categories.php',
                    'data/chapters.php',
                    'data/lessons.php',
                    '', // teach/classroom/filterLessons
                    'data/cards.php',
                    ''  // teach/classroom/saveProgress
                );

                chatbox.init(
                    "<?php echo $lesson['token']; ?>"
                );

                countdown.init(
                    '<?php echo $lesson['duration']; ?>'
                );

            })();

        </script>

    </body>
</html>

I am including and initializing all of my custom scripts at the bottom of my template.

My first object handles the slider itself:

var slider = {

    card_id: 1,

    init: function(token){
        this.token = token;
        this.cacheDOM();
        this.bindEvents();
    },

    cacheDOM: function(){
        this.$prevCard     = $('#prevCard');     // button
        this.$nextCard     = $('#nextCard');     // button
        this.$slideCounter = $('#slideCounter'); // span
    },

    bindEvents: function(){
        this.$prevCard.on('click', this.prevCard.bind(this));
        this.$nextCard.on('click', this.nextCard.bind(this));
    },

    showCard: function(card_id){
        cards = document.getElementsByClassName('card-container');
        for(i = 0; i < cards.length; i++){
            $(cards[i]).hide(); // hide all cards
        }
        $('#card-' + (card_id)).show(); // show card
        location.hash = card_id;
        this.slideCounter(card_id, cards.length);
    },

    prevCard: function(){
        if (this.card_id != 1) {
            this.card_id--;
            this.showCard(this.card_id);
            pusher.channel.trigger('client-slide-' + this.token  + '-triggered', {card_id: this.card_id});
        }
    },

    nextCard: function(){
        if ($('#card-' + (this.card_id + 1)).length != 0) {
            this.card_id++;
            this.showCard(this.card_id);
            pusher.channel.trigger('client-slide-' + this.token + '-triggered', {card_id: this.card_id});
        }
    },

    slideCounter: function(card_id, cards){
        this.$slideCounter.html('Slide ' + card_id + '/' + cards);
    },
}

Next we have the object that is responsible for loading lessons:

var loadLesson = {
    init: function(token, studentId, lessonId, loadLessonPath, updateEventPath, saveProgressPath){
        this.token            = token;
        this.studentId        = studentId;
        this.loadLessonPath   = loadLessonPath;
        this.updateEventPath  = updateEventPath;
        this.saveProgressPath = saveProgressPath;

        this.cacheDOM();
        this.renderLesson(lessonId); // load the initial lesson
    },

    cacheDOM: function(){
        this.$lessonIntro         = $('#lessonIntro');         // div
        this.$lessonIntroTemplate = $('#lessonIntroTemplate'); // mustache template
        this.$cardContent         = $('#cardContent');         // div
        this.$cardContentTemplate = $('#cardContentTemplate'); // mustache template
        this.$lessonOutro         = $('#lessonOutro');         // div
        this.$lessonOutroTemplate = $('#lessonOutroTemplate'); // mustache template
    },

    renderLesson: function(lessonId){
        this.lessonId = lessonId;
        var self = this;
        $.ajax({
            url: this.loadLessonPath,
            async: false,
            method: 'post',
            data: {lessonId: lessonId},
            success: function(data){
                self.$lessonIntro.html(Mustache.render(self.$lessonIntroTemplate.html(), data.lesson));
                self.$cardContent.html(Mustache.render(self.$cardContentTemplate.html(), {cards: data.cards}));
                self.$lessonOutro.html(Mustache.render(self.$lessonOutroTemplate.html()));
                $('.end-card').attr('id', 'card-' + (data.cards.length + 2))

                window.location.hash = 1;
                slider.card_id       = 1;
                slider.showCard(1);
            },
        });
        this.updateEvent(lessonId);
        $('#saveProgress').on('click', this.saveProgress.bind(this));
    },

    updateEvent: function(lessonId){
        $.ajax({
            url: this.updateEventPath,
            async: false,
            method: 'post',
            data: {
                token:    this.token,
                lessonId: lessonId,
            },
            success: function(data){
                // alert();
            },
        });
    },

    saveProgress: function(e){
        $.ajax({
            url: this.saveProgressPath,
            async: false,
            method: 'post',
            data: {
                token:      this.token,
                lesson_id:  this.lessonId,
                student_id: this.studentId,
            },
            success: function(data){
                $('#saveProgress').removeClass('btn-outline-secondary').addClass('btn-outline-success').html('<i class="fa fa-check"></i> Saved!');
            },
        });
    }
}

Then there is the code for the lesson library (the largest object)

var library = {
    init: function(
        token,
        studentId,
        progressPath,
        coursePath,
        levelPath,
        categoryPath,
        chapterPath,
        lessonPath,
        filterLessonPath,
        loadLessonPath,
        saveProgressPath
    ){
        this.token            = token;            // to define our pusher channel
        this.studentId        = studentId;        // student
        this.progressPath     = progressPath,     // where to get students current progress
        this.coursePath       = coursePath;       // get all courses
        this.levelPath        = levelPath;        // get all levels
        this.categoryPath     = categoryPath;     // get all categories
        this.chapterPath      = chapterPath;      // get all chapters
        this.lessonPath       = lessonPath;       // get all lessons
        this.filterLessonPath = filterLessonPath; // get a filtered list of lessons
        this.loadLessonPath   = loadLessonPath;   // (public) get lessons cards
        this.saveProgressPath = saveProgressPath; // marks lesson as done

        this.cacheDOM();
        this.bindEvents();
        this.getProgress();
        this.renderCategories();
        this.renderCourses();
    },

    cacheDOM: function(){
        this.$libraryModal       = $('#libraryModal');       // modal
        this.$showLibrary        = $('#showLibrary');        // button
        this.$loadLesson         = $('#loadLesson');         // button

        this.$categories         = $('#categories');         // select
        this.$categoriesTemplate = $('#categoriesTemplate'); // mustache template
        this.$courses            = $('#courses');            // div
        this.$coursesTemplate    = $('#coursesTemplate');    // mustache template
        this.$levels             = $('#levels');             // div
        this.$levelsTemplate     = $('#levelsTemplate');     // mustache template
        this.$chapters           = $('#chapters');           // div
        this.$chaptersTemplate   = $('#chaptersTemplate');   // mustache template
        this.$lessons            = $('#lessons');            // div
        this.$lessonsTemplate    = $('#lessonsTemplate');    // mustache template
    },

    bindEvents: function(){
        this.$showLibrary.on('click', this.showLibrary.bind(this));
        this.$loadLesson .on('click', this.handleLesson.bind(this));
    },

    getProgress: function(){
        var self = this;
        $.ajax({
            url: this.progressPath,
            async: false,
            method: 'post',
            data: {student_id: this.studentId},
            success: function(data){
                if (data) {
                    self.nextCourse  = data.course_id;     // sets the student's current course
                    self.nextLevel   = data.english_level; // sets the student's english level
                    self.nextChapter = data.chapter_id;    // sets the student's current chapter
                    self.nextLesson  = data.lesson_id;     // sets the student's current lesson
                }
            },
        });
    },

    renderCategories: function(){
        var self = this;
        $.ajax({
            url: this.categoryPath,
            async: false,
            success: function(data){
                self.$categories.html(Mustache.render(self.$categoriesTemplate.html(), data));
            },
        });
    },

    renderCourses: function(){
        var self = this;
        $.ajax({
            url: this.coursePath,
            async: false,
            success: function(data){
                self.$courses.html(Mustache.render(self.$coursesTemplate.html(), data));
                $('.course-list').on('click', self.selectCourse.bind(self));
                self.highlightCourse();
            },
        });
    },

    highlightCourse: function(){
        var courses = document.getElementsByClassName('course-list');
        for (var i = 0; i < courses.length; i++) {
            var course_id = $(courses[i]).attr('data');
            if (course_id === this.nextCourse) {
                $(courses[i]).addClass('next-item');
            }
        }
    },

    selectCourse: function(e){
        $('.course-list').removeClass('active');
        $(e.target).addClass('active');
        this.courseId = $(e.target).attr('data');
        this.$chapters.html(Mustache.render('')); // clears chapters
        this.$lessons.html(Mustache.render(''));  // clears lessons
        this.renderLevels();
    },

    showLibrary: function(){
        this.$libraryModal.modal('show');
    },

    renderLevels: function(){
        var self = this;
        $.ajax({
            url: this.levelPath,
            async: false,
            success: function(data){
                self.$levels.html(Mustache.render(self.$levelsTemplate.html(), data));
                $('.level-list').on('click', self.selectLevel.bind(self));
                self.highlightLevel();
            },
        });
    },

    highlightLevel: function(){
        var levels = document.getElementsByClassName('level-list');
        for (var i = 0; i < levels.length; i++) {
            var level = $(levels[i]).attr('data');
            if (level === this.nextLevel) {
                $(levels[i]).addClass('next-item');
            }
        }
    },

    selectLevel: function(e){
        $('.level-list').removeClass('active');
        $(e.target).addClass('active');
        this.level = $(e.target).attr('data');
        this.$lessons.html(Mustache.render(''));
        this.renderChapters();
    },

    renderChapters: function(){
        var self = this;
        $.ajax({
            url: this.chapterPath,
            async: false,
            method: 'post',
            data: {
                course: this.courseId,
                level: this.level,
            },
            success: function(data){
                self.$chapters.html(Mustache.render(self.$chaptersTemplate.html(), data));
                $('.chapter-list').on('click', self.selectChapter.bind(self));
                self.highlightChapter();
            },
        });
    },

    highlightChapter: function(){
        var chapters = document.getElementsByClassName('chapter-list');
        for (var i = 0; i < chapters.length; i++) {
            var chapter = $(chapters[i]).attr('data');
            if (chapter === this.nextChapter) {
                $(chapters[i]).addClass('next-item');
            }
        }
    },

    selectChapter: function(e){
        $('.chapter-list').removeClass('active');
        $(e.target).addClass('active');
        this.chapterId = $(e.target).attr('data');
        this.renderLessons();
    },

    renderLessons: function(){
        var self = this;
        $.ajax({
            url: this.lessonPath,
            async: false,
            method: 'post',
            data: {
                chapter: this.chapterId,
                studentId: this.studentId,
            },
            success: function(data){
                self.$lessons.html(Mustache.render(self.$lessonsTemplate.html(), data));
                self.highlightLesson();
                $('.category-list').on('click', self.selectCategory.bind(self));
                $('.lesson-list')  .on('click', self.selectLesson  .bind(self));
            },
        });
    },

    selectCategory: function(e){
        this.category = $(e.target).val();
        this.filterLessons();
    },

    filterLessons: function(){
        var self = this;
        $.ajax({
            url: this.filterLessonPath,
            async: false,
            method: 'post',
            data: {
                category: this.category,
                chapter:  this.chapterId,
            },
            success: function(data){
                self.$lessons.html(Mustache.render(self.$lessonsTemplate.html(), data));
                self.highlightLesson();
                $('.lesson-list').on('click', self.selectLesson.bind(self));
            },
        });
    },

    highlightLesson: function(){
        var lessons = document.getElementsByClassName('lesson-list');
        for (var i = 0; i < lessons.length; i++) {
            var lesson = $(lessons[i]).attr('data');
            if (lesson === this.nextLesson) {
                $(lessons[i]).addClass('next-item');
            }
        }
    },

    selectLesson: function(e){
        this.lessonId = $(e.target).attr('data');
        $('.lesson-list').removeClass('active');
        $(e.target).addClass('active');
    },

    handleLesson: function(){
        if (this.$libraryModal.is(':visible')) {
            this.$libraryModal.modal('hide');
        }
        this.emittLessonId(this.lessonId);
        loadLesson.renderLesson(this.lessonId);
    },

    emittLessonId: function(lessonId){
        pusher.channel.trigger('client-lesson-' + this.token + '-loaded', {lessonId: lessonId});
    },
}

Next the websockets chatbox

var chatbox = {

    init: function(token){
        this.token = token;
        this.cacheDOM();
        this.bindEvents();
    },
    cacheDOM: function(){
        this.$content     = $('.content');
        this.$chatbox     = $('.chatbox');
        this.$chatboxTab  = $('.chatbox-tab');
        this.$chatboxBody = $('.chatbox-body');
        this.$message     = $('#message');
        this.$sendMessage = $('#sendMessage');
    },
    bindEvents: function(){
        this.$chatboxTab.on('click', this.toggleChatbox.bind(this));
        self = this;
        this.$message.keydown(function(e){
            if (e.which == 13) {
                self.$sendMessage.click();
            }
        });
        this.$sendMessage.on('click', this.sendMessage.bind(this));
    },
    toggleChatbox: function(){
        this.$chatbox   .toggleClass('toggled');
        this.$content   .toggleClass('toggled');
        this.$chatboxTab.toggleClass('toggled');
    },
    sendMessage: function(e){
        var name       = $(e.target).attr('data-name');
        var is_teacher = $(e.target).attr('data-is-teacher');
        var message    = this.$message.val();
        message = this.linkify(message);
        if (message !== '') {
            if (is_teacher) {
                textColor      = '#27ae60;';
                containerColor = '#eef9eb;';
                nameAlign      = 'text-align: left;';
                msgAlign       = 'text-align: left;';
            } else {
                textColor      = '#2c3e50;';
                containerColor = '#fafafa;';
                nameAlign      = 'text-align: right;';
                msgAlign       = 'text-align: right;';
            }
            html = '<div class="chat-container" style="background:' + containerColor + '">' +
                '<p class="chat-name"    style="color: ' + textColor + nameAlign + '">' + name    + '</p>' +
                '<p class="chat-message" style="color: ' + textColor + msgAlign  + '">' + message + '</p>' +
                '</div>';
            data = {
                name: name,
                html: html,
            }
            this.showMessage(data);
            pusher.channel.trigger('client-message-' + this.token + '-triggered', data);
            this.$message.val(''); // clear input field
        }
    },
    showMessage: function(data){
        if (this.$chatbox.hasClass('toggled') === false) {
            this.toggleChatbox();
        }
        $('.chatbox-body').append(data.html);
        c = $('.chatbox-body');
        c.scrollTop(c.prop("scrollHeight"));
    },
    linkify: function(message){
        var urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; // precious
        return message.replace(urlRegex, function(url) {
            return '<a href="' + url + '" target="_blank">' + url + '</a>';
        });
    }
}

and finally the countdown timer

var countdown = {

    duration: 0,
    classEnded: false,

    init: function(duration){
        this.duration = duration; // countdown start time
        this.cacheDOM();
        var self = this;
        this.intervalId = setInterval(function(){
            self.countdown();
        }, 1000);
    },
    cacheDOM: function(){
        this.$countdown = $('#countdown');
    },

    countdown: function(){
        if (this.classEnded === false) {
            this.duration--;
            if (this.duration < 1) { // check if time is up
                this.classEnded = true;
                clearInterval(this.intervalId);
                this.$countdown.html('Ended').addClass('text-danger');
                return;
            }
            this.renderTime();
        }
    },

    renderTime: function () {
        var minutes = Math.floor(this.duration / 60);
        var seconds = this.duration - (minutes * 60);
        if (seconds === 60) {
            seconds = '00';
        } else if (seconds <= 9) {
            seconds = '0' + seconds;
        }
        if (minutes <= 9) {
            minutes = '0' + minutes;
        }
        this.$countdown.html(minutes + ':' + seconds)
    }
}

Issues

This thing is becoming harder and harder to add features to... I know that if I keep on this trajectory it will become unmaintainable.

My gut tells me the problem may be due to having one huge template with a bunch ID's scattered about that needs to be targeted by functions in random js files...

I am interested very much in modularizing everything... but I am not sure how to go about that. I already feel I have made huge improvements by making use of objects and mustache templating, but it's certainly not enough.

Here is a link to the github repo... it should run standalon with the dummy data I provided (php).

https://github.com/JethroHazelhurst/classroom/

Feedback request

I have been coding for a year now and started with javascript a few months ago so... this is pretty much the result of me just going for it... writing code with very little thought to structure (besides Mustache templating) and so I am realizing just how hard it is to write maintainable applications.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.