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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.