1
\$\begingroup\$

This is the first time I am working on OOJS code and I am a beginner in JS. I think I have pretty much messed it up. I am planning to modularize my code, but with the inclusion of rest calls, I got confused and have messed up my structure. The trouble I am facing is that my success function of Ajax call keeps growing with the addition of new functionality's, whereas I want it to be executed specifically to that module to which the data gets affected. Simply put, I want different chunks of code to be executed based on which module I am working in, instead of stuffing everything inside a monster success function. A clean structure to achieve this is what I need.

//for stopping select button highlighting in IE8
var select = $('select');

select.change(function () {
    select.blur();
});

$('select option').click(function () {
    select.blur();
});
// code for sidebar toggle in jQuery -thomas
$.fn.toggleClick = function () {
    var methods = arguments, // Store the passed arguments for future reference
     count = methods.length; // Cache the number of methods 

    // Use return this to maintain jQuery chainability
    // For each element you bind to
    return this.each(function (i, item) {
        // Create a local counter for that element
        var index = 0;

        // Bind a click handler to that element
        $(item).on('click', function () {
            // That when called will apply the 'index'th method to that element
            // the index % count means that we constrain our iterator between 0
            // and (count-1)
            return methods[index++ % count].apply(this, arguments);
        });
    });
};

//JQ for adding box shadow to ie8  
$(function () {
    var temp;
    $('#sg-evm-interLocutor-popUp').addClass('shadow');
    //JQ for toggle box button
    $(".toggle-box").click(function () {
        $('.toggle-box span').toggleClass('toggle-box-visited');
        $('.toggle-box font').toggleClass('toggle-box-visited-font');
    });
    $(".intb-close").on("click", function () {

        temp = $('#intbpop').bPopup();
        temp.close();
    });
});
//JQ for adding box shadow to ie8 END


// oops code
var sg = {
    w: $(window).innerWidth(),
    h: $(window).innerHeight(),
    paper: new Raphael("paper", "100%", "100%")
};

if (sg.w >= 1024) {
    //view box for responsive
    sg.paper.setViewBox(0, 0, 1280, 720, false);
} else {
    sg.paper.setViewBox(0, 0, 1280, 720, false);
}
//      module for calling the json for employee popup
sg.employeeJsonCall = function () {

    var employeeJsonCall = this;

    this.init = function () {
        //this.employeeHide();
        return this;
    };



    var firstUrl = "http://srvblrevm98:8099/evmservice/EVMEmployeeService.svc/getUserInfo";
    var Type;
    var Url;
    var Data;
    var ContentType;
    var DataType;
    var ProcessData;


    // jQuery for calling the json file

    //temp function to test dyn JSON
    sg.newJson = function (passedId) {
        var staticUrl = "http://srvblrevm98:8099/EVMSERVICE/EVMEmployeeService.svc/getemployee/";
        WCFJSON(staticUrl + passedId);
        var passedId = 0;
        var showAjaxPopup = setTimeout(function () {
            $("#sg-evm-empContainer").css({
                display: "block",
                visibility: "visible"
            }).animate({
                opcaity: 1
            }, 1500);
        }, 500);
    };

    function WCFJSON(myUrl) {
        Type = "GET";
        Url = myUrl;
        //Data = userid;
        ContentType = "application/json; charset=utf-8";
        DataType = "json";
        var ProcessData = true;
        CallService();
    }

    // Function to call WCF  Service       
    function CallService() {
        $.ajax({
            type: Type, //GET or POST or PUT or DELETE verb
            url: Url, // Location of the service
            data: Data, //Data sent to server
            contentType: ContentType, // content type sent to server
            dataType: DataType, //Expected data format from server
            processdata: ProcessData, //True or False
            success: function (msg) { //On Successfull service call
                ServiceSucceeded(msg);
            },
            error: ServiceFailed // When Service call fails
        });
    }

    function ServiceFailed(result) {
        alert('Service call failed: ' + result.status + '' + result.statusText);
        Type = null;
        varUrl = null;
        Data = null;
        ContentType = null;
        DataType = null;
        ProcessData = null;
    }

    function ServiceSucceeded(result) {
        if (DataType == "json") {
            myData = result;
            //console.log(myData);

            //get the HTML template from script tag
            var theTemplateScript = $("#dynamicData").html();
            //compile the template
            var theTemplate = Handlebars.compile(theTemplateScript);
            $("#sg-hb-employee").html(theTemplate(myData[0]));
            $("#sg-evm-empContainer").animate({
                opacity: 1
            }, 1200);


            // handlebars for the sidebar section
            var sideabarHbs = $("#sg-hb-sideBar").html();
            // compile the template
            var thesideBarTemplate = Handlebars.compile(sideabarHbs);
            var loadSidebar = function () {
                $("#sg-evm-hbsideBar").html(thesideBarTemplate(myData[0]));
            };
            loadSidebar();
            //filter functionality to the sidebar

            var sideBarOptions = {
                valueNames: ['sg-evm-locuter']
            };

            var sgEvmSidebar = new List('sg-evm-hbsideBar', sideBarOptions);

            // CALLING THE POPUP HIDE
            $(".sg-evm-empCloseBtn").click(function () {
                splashScreen.hideEmployeePopup();
                $("#sg-evm-cubeCarousel").owlCarousel({
                    items: 10
                }).trigger('destroy.owl.carousel');
            });

            // this secttion for ajax call of carousel
            var content = "";

            for (var i in myData[0].directReportees) {
                var img = myData[0].directReportees[i].img;
                var alt = myData[0].directReportees[i].alt;

                content += "<img id='sg-evm" + i + "' class='sg-evm-reportee' src=\"images/" + img + "\" alt=\"" + alt + "\">"
            }
            $("#sg-evm-cubeCarousel").html(content);
            // carousel for direct reportee pop-up
            $("#sg-evm-cubeCarousel").owlCarousel({
                items: 10,
                itemsDesktop: false,
                itemsDesktopSmall: false,
                itemsTablet: false,
                itemsMobile: false
            });
            // owl carousal with json
            $("#owl-popupCarousel").owlCarousel({
                items: 1,
                itemsDesktop: false,
                itemsDesktopSmall: false,
                itemsTablet: false,
                itemsMobile: false,
                navigation: true,
                pagination: false,
                navigationText: [
                    "<img src='images/leftarrow.png'>",
                    "<img src='images/rightarrow.png'>"]
            });
            // code for popup plugin
            var callPopUp;

            for (var i = 0; i < myData[0].directReportees.length; i++) {
                var temp = (function (index) {
                    //                        console.log(index);
                    $("#sg-evm" + index).on("click", function () {
                        var popup = $(".bpop");
                        callPopUp = $(".bpop").bPopup({
                            position: [936, 510],
                            opacity: 0,
                            speed: 100,
                            modal: true,
                            modalClose: true,
                            escClose: true,
                            onOpen: function () {
                                popup.html("<div class='sg-pop-child'><img class='sg-evm-drImg' src='images/team.png' alt=''><span class='sg-evm-link' id='sg-dyn-reportee" + index + "'>" + myData[0].directReportees[index].reporteeName + "</span></div>")

                            },
                            onClose: function() {
                                $(".sg-pop-child").remove();
                            }
                        });

                        $("#sg-dyn-reportee" + index).on("click", function() {
                            callPopUp.close();
                            $(".bpop").remove();
                        });

                        $("#sg-dyn-reportee" + index).on("click", function () {
                            var staticUrl = "http://srvblrevm98:8099/EVMSERVICE/EVMEmployeeService.svc/getemployee/";
                            WCFJSON(staticUrl + myData[0].directReportees[index].reporteeId);
                            var showAjaxPopup = setTimeout(function () {
                                $("#sg-evm-empContainer").css({
                                    display: "block",
                                    visibility: "visible"
                                });
                            }, 500);
                        });
                    });

                })(i);
            }

            //hidel any popup screen onclick toggle box
            $(".toggle-box").click(function () {
                $("#sg-evm-empContainer").toggle();
            });
            // writing functionality for toggle click
            //for opening sidebar
            function openSidebar() {
                $('#sg-evm-sidebar').animate({
                    left: 0
                }, 300)
                $('.container').css({
                    position: "fixed"
                }).animate({
                    left: 300
                }, 300)
                $('.content-overlay').delay(300).show();
            }
            //closing sidebar
            function closeSidebar() {
                $('#sg-evm-sidebar').animate({
                    left: -300
                }, 300)
                $('.container').css({
                    position: "fixed"
                }).animate({
                    left: 0
                }, 300)
                $('.content-overlay').delay(300).hide();
            }


            // calling toggleclick here

            $('.toggle-box').toggleClick(openSidebar, closeSidebar);
            //to show alert
            $("#showInterlocutors").on("click", function() {
                var interlocutor = (new sg.interlocutor).init();
            });
        }

    }

    function ServiceFailed(xhr) {
        alert(xhr.responseText);

        if (xhr.responseText) {
            var err = xhr.responseText;
            if (err) error(err);
            else error({
                Message: "Unknown server error."
            })
        }

        return;
    }

    //console.log(firstUrl);
    WCFJSON(firstUrl); //calling the JSON here

};

// sliding of splashscreen
sg.splashScreen = function () {
    var splashScreen = this;

    this.init = function () {
        //        this.employeeJsonCall();
        this.splashSlide();
        return this;
    };


    this.splashSlide = function () {
        $("#splashscreen").click(function () {
            // call intro animation logic on click
            splashScreen.introAnimation();

            $(".sg-evm-splashscreen-container").animate({
                right: '100%',
                opacity: 0
            }, 500);
            //$(".sg-evm-splashscreen-container").animate({right: '100%'}, 500);
            $("#splashscreen").delay(400).hide(500);
        });
        // CALLING THE POPUP HIDE
        $(".sg-evm-empCloseBtn").click(function () {
            splashScreen.hideEmployeePopup();
        });
    };
    // logic for the initial loading and animation of the cube
    this.introAnimation = function () {
        var introCube = sg.paper.set();
        var option = ["- ", ""];
        // for animation of the cube
        var loadCube = Raphael.animation({
            opacity: 1,
                "stroke-dasharray": option[0]
        }, 500);
        var fillTop = Raphael.animation({
            fill: "#e60028",
                "stroke-width": 0
        }, 500);
        var fillLeft = Raphael.animation({
            fill: "#aa1e1e",
                "stroke-width": 0
        }, 500);
        var fillRight = Raphael.animation({
            fill: "#c71313",
                "stroke-width": 0
        }, 500);
        var fillOuter = Raphael.animation({
            stroke: "#ffffff",
                "stroke-width": 8
        }, 500);
        introCube.push(
        sg.paper.path("M510 375 L600 427 690 375 600 323 510 375"),
        sg.paper.path("M510 375 L510 480 600 532 600 427 510 375"),
        sg.paper.path("M690 375 L690 480 600 532 600 427 690 375"),
        sg.paper.path("M690 375 L600 322 510 375 510 480 600 532 690 480 689 372")).hover(function () {
            this.attr({
                cursor: "pointer"
            });
        }).attr({
            opacity: 0,
            stroke: "#e60028",
                "stroke-dasharray": option[0]
        }).animate(loadCube.delay(1000));

        setTimeout(function () {
            introCube.attr({
                "stroke-dasharray": option[1]
            });
        }, 3000)

        // different colors to different faces
        introCube[0].animate(fillTop.delay(3000));
        introCube[1].animate(fillLeft.delay(3000));
        introCube[2].animate(fillRight.delay(3000));
        introCube[3].animate(fillOuter.delay(5000));

        // ON CLICKING THE CUBE
        function invokePopUp() {
            introCube.click(function () {
                splashScreen.showEmployeePopup();
            });
        }
        setTimeout(invokePopUp, 5500);
        //to hide the newly loaded pop-up
        $("#showInterlocutors").on("click", function() {
            splashScreen.hideEmployeePopup();
            introCube.animate({opacity: 0}, 300).hide();
        });
    };
    this.showEmployeePopup = function () {
        $("#sg-evm-empContainer").css({
            visibility: "visible",
            display: "block"
        });
    }
    this.hideEmployeePopup = function () {
        $("#sg-evm-empContainer").css({
            visibility: "hidden",
            display: "none"
        });
    }
};
//codes for the interlocutor page
sg.interlocutor = function () {
    var interlocutor = this;
    this.middleCube = sg.paper.set();
    this.middleCubePosition = [405, 203];
    this.animateInterlocutor = Raphael.animation({
        opacity: 1
    }, 1000);
    this.mappedPoints = [
        [405, 410],
        [42, -6],
        [224, -112],
        [-140, -112],
        [-140, 98],
        [-140, 305],
        [42, 410],
        [587, 98],
        [585, 308],
        [222, 308],
        [405, -6],
        [767, -6],
        [587, -112],
        [767, 203],
        [950, -112],
        [222, 98],
        [950, 98],
        [950, 305],
        [42, 203],
        [769, 410]
    ];
    this.mappedLines = [];
    this.lines = sg.paper.set(); //set for lines
    this.noOfCubes = this.mappedPoints.length; //no of cubes
    this.init = function () {
        this.drawLines();
        this.strokedCube();
        this.createCubes();
        this.interlocutorPopUp();
        this.clicks();
        return this;
    };
    //click events on this section
    this.clicks = function() {
        this.middleCube.click(function() {
            $("#sg-evm-interLocutor-popUp").show();
        });

        //hide locutors pop-up
        $("#locutor-close").click(function () {
            $("#sg-evm-interLocutor-popUp").hide()
        })
    };

//    for drawing the lines between cubes
    this.drawLines = function() {
        this.lines.push(sg.paper.path("M1019 0 L458 323").attr("stroke", "red"),
                        sg.paper.path("M640 428 L1600 980").attr("stroke", "black"),
                        sg.paper.path("M822 323 L1600 770").attr("stroke", "red"),
                        sg.paper.path("M656 0 L0 379").attr("stroke", "black"),
                        sg.paper.path("M622 0 L1500 502").attr("stroke", "black"),
                        sg.paper.path("M0 268 L1104 902").attr("stroke", "black"),
                        sg.paper.path("M0 58 L640 428").attr("stroke", "red"), //640 428 (..points to the center..)
                        sg.paper.path("M1300 256 L184 900").attr("stroke", "black"),
                        sg.paper.path("M456 532 L822 322").attr("stroke", "red"),
                        sg.paper.path("M262 0 L822 322").attr("stroke", "black"),
                        sg.paper.path("M822 322 L1380 0").attr("stroke", "black"),
                        sg.paper.path("M456 532 L0 794").attr("stroke", "black"),
                        sg.paper.path("M0 478 L740 900").attr("stroke", "black"),
                        sg.paper.path("M1300 466 L540 900").attr("stroke", "black")
                       ).attr({opacity: 0}).animate(this.animateInterlocutor);
    };
    // to draw the middle cube
    this.strokedCube = function() {
        interlocutor.middleCube.push(sg.paper.path("M190 200 L235 175 280 200 235 225 190 200"),
            sg.paper.path("M190 200 L235 225 235 275 190 250 190 200"),
            sg.paper.path("M235 225 L280 200 280 250 235 275 235 225"),
            sg.paper.path("M190 200 L235 175 280 200 280 250 235 275 190 250 190 200")).transform("t" + this.middleCubePosition + "").attr({opacity: 0}).animate(this.animateInterlocutor);
        //add codes for red cubes here
            interlocutor.middleCube[0].attr({
                fill: "#e60028",
                "stroke-width": 0
            })
            interlocutor.middleCube[1].attr({
                fill: "#aa1e1e",
                "stroke-width": 0
            })
            interlocutor.middleCube[2].attr({
                fill: "#c71313",
                "stroke-width": 0
            })
            interlocutor.middleCube[3].attr({
                "stroke-width": 4,
                "stroke": "#ffffff",
                cursor: "pointer"
            })
    };
    //creating cubes randomly --  excluded middle cube
    this.createCubes = function () {
        for (var i = 0; i < this.noOfCubes; i++) {
//            value of this is sg.interlocutor
            var captured  = this; // captured this value to use inside loop

            this['cube' + (i)] = sg.paper.set();
            this['cube' + (i)].push(sg.paper.path("M190 200 L235 175 280 200 235 225 190 200"),
            sg.paper.path("M190 200 L235 225 235 275 190 250 190 200"),
            sg.paper.path("M235 225 L280 200 280 250 235 275 235 225"),
            sg.paper.path("M190 200 L235 175 280 200 280 250 235 275 190 250 190 200")).transform("t" + this.mappedPoints[i] + "").attr({opacity: 0}).animate(this.animateInterlocutor);
            //console.log(this['cube' + (i)]);
            this['cube' + (i)].click(function () {
                var cubes = this;
                $("#interPop").bPopup({
                    position: [(this.getBBox().cx) - 60, (this.getBBox().cy) + 20],
                    opacity: 0,
                    speed: 100,
                    modal: true,
                    escClose: true
                });
            });

                this['cube' + (i)].click(function() {
                    console.log(this.getBBox().x + "," + this.getBBox().y);
                })
        };
        // to color the cube randomly - for RED
        for (var i = 0; i < this.noOfCubes; i += 2) {
            //add codes for red cubes here
                this['cube' + (i)][0].attr({
                    fill: "#e60028",
                    "stroke-width": 0
                })
                this['cube' + (i)][1].attr({
                    fill: "#aa1e1e",
                    "stroke-width": 0
                })
                this['cube' + (i)][2].attr({
                    fill: "#c71313",
                    "stroke-width": 0
                })
                this['cube' + (i)][3].attr({
                    "stroke-width": 0,
                    cursor: "pointer"
                })
        }
        // to color the cube randomly - for Black
        for (var i = 1; i < this.noOfCubes; i += 2) {
            //add codes for red cubes here
                this['cube' + (i)][0].attr({
                    fill: "#000000",
                    "stroke-width": 0
                })
                this['cube' + (i)][1].attr({
                    fill: "#1e1e1e",
                    "stroke-width": 0
                })
                this['cube' + (i)][2].attr({
                    fill: "#282828",
                    "stroke-width": 0
                })
                this['cube' + (i)][3].attr({
                    "stroke-width": 0,
                    cursor: "pointer"
                })
        }
    };
    this.interlocutorPopUp = function () {
        //functionality for slider popup
        $("#sg-evm-seeMore").on("click", function () {
            $("#sg-evm-empLeftPop").show();
            $("#sg-evm-sliderCloseBtn").show();
        })
        $("#sg-evm-sliderCloseBtn").on("click", function () {
            $("#sg-evm-empLeftPop").hide();
            $("#sg-evm-sliderCloseBtn").hide();
        });
    }
};


var splashScreen = (new sg.splashScreen).init();
var employeeJsonCall = (new sg.employeeJsonCall).init();
\$\endgroup\$
1
  • \$\begingroup\$ if you have big or sophisticated architecture, probably you would like to use angular, as @prakharsingh95 sad in his answer. Also, angular is not a silver bullet, because there are a lot of thing that you can use in wrong way and they will make you code even more unfit. I mean that you should read code style, but, probably you have not so much time to inject additional frameworks as dependencies in you current project state. \$\endgroup\$
    – outoftime
    Jun 18 '15 at 5:51
2
\$\begingroup\$

About code style, I highly recommend you to read angular code style even if you have no idea what is it. It contains a lot of sanity approaches about how to make HUGE classes clear and smooth.

About comments: they needed only if you have no idea what is going on, or it is hard to understand. Don't overuse them.

UPD: I always met problems of bad design when I have no idea how should I do something. Fist stage - write you code, code that will do what I want. Second stage - refactoring. Take one piece of responsibility and move it into separate location until all code is rewritten.

As I see, you already have such pieces, that can be placed in separate location, e.g. plugins like // code for sidebar toggle in jQuery -thomas they are really tiny, but there are no need to write ugly comments, that make code unreadable. Move it into single file, and configure grunt-contrib-concat for glueing all pieces together.

Then I'd start refactor sg. There are a lot of thing that you can do from covered code style above.

Firstly make constructor for your plugin, and move there this code

// oops code
var sg = {
    w: $(window).innerWidth(),
    h: $(window).innerHeight(),
    paper: new Raphael("paper", "100%", "100%")
};

configure();

return sg;

function configure() {
    if (sg.w >= 1024) {
        //view box for responsive
        sg.paper.setViewBox(0, 0, 1280, 720, false);
    } else {
        sg.paper.setViewBox(0, 0, 1280, 720, false);
    }
};

Then continue refactoring method be method, you'll probably spend a lot of time, but you code became much easier for reading and making changes of any difficulty. As you have constructor, all methods should in it

var sg = {
    w: $(window).innerWidth(),
    h: $(window).innerHeight(),
    paper: new Raphael("paper", "100%", "100%"),
    employeeJsonCall: employeeJsonCall,
    splashScreen: splashScreen
};

or by using prototyping

SG.prototype.employeeJsonCall = function () { /*some code*/ }

IMHO: first way is preferable, because you can see objects signature just by looking at constructor with no need for overview whole source file. Just don't forget to sort methods and properties in alphabetic order for quicker search (it really helps, do not ignore).

\$\endgroup\$
3
  • \$\begingroup\$ I find it very hard to debug as I am on development. I usually remove most of them when going for production. Thanks for the useful tip though. I will have a look into the style guide you suggested. \$\endgroup\$ Jun 18 '15 at 4:41
  • \$\begingroup\$ @ThomasSebastian added updates. I hope it will help. \$\endgroup\$
    – outoftime
    Jun 18 '15 at 5:17
  • \$\begingroup\$ Dude that is awesome. You explained things with regard to my code which makes me understand better. I know a little about prototype, so I got the idea of what we are trying to achieve. Could you also provide a link where I can look more into these kind of stuffs? The hard time I have is making AJAX calls from html onclick and using the data inside the individual modules. \$\endgroup\$ Jun 18 '15 at 5:28
3
\$\begingroup\$

For OOJS, I would personally recommend Angular.js.

For the above code, here's you can impelment OOJS:

function RESTService() {

    this.service = function () {

        $.ajax({
            type: this.type, //GET or POST or PUT or DELETE verb
            url: this.url, // Location of the service
            data: Data, //Data sent to server
            contentType: this.contentType, // content type sent to server
            dataType: this.dataType, //Expected data format from server
            processdata: this.processData, //True or False
            success: this.onSuccess,
            error: this.onFail // When Service call fails
        });
    }

    this.GET = function(parameters) {

        this.type="GET";
        this.url = parameters.URL;
        this.contentType = parameters.contentType;
        this.dataType = parameters.dataType;
        this.processData = parameters.processData;

        return this;
    }

    this.execute = function() {        
        this.service();
    }
}

Use it like this:

RESTGETrequest = new RESTService.GET(parameters);
RESTGETrequest.onSuccess(function() {alert("Success GET";} );

RESTGETrequest.execute();

RESTPOSTrequest = new RESTService.POST(parameters);
RESTPOSTrequest.onSuccess(function() {alert("Success POST";} );

RESTPOSTrequest.execute()
\$\endgroup\$
1
  • \$\begingroup\$ I used handlebars specifically as I need support for IE8. I am looking forward to have a detailed study of angular.js in the near future. I will review your code and get back. Thanks a ton for helping. \$\endgroup\$ Jun 18 '15 at 4:33

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.