4
\$\begingroup\$

I am a backbone.js newbie. I have created a color picker application using backbone.js. I am trying to figure out if I have nailed the MVC concept. Please review my code and let me know if I can do better.

<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>MVC Design  - Color Picker</title>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.6/underscore-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.3.3/backbone-min.js"></script>

<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>

<script>
    $(function(){
                    //Model for RGB
                    var RGB = Backbone.Model.extend({

                defaults: {R: 255, G: 255, B: 255}

                });
                    //End of Model for RGB

                    //Model for HEX
        var HEX = Backbone.Model.extend({

                defaults: {HEX: "000000"}

                });
                     //End of Model for HEX


            //Model for HSV
        var HSV = Backbone.Model.extend({

                defaults: {H: 0, S: 0, V: 1}

                });
                     //End of Model for HSV


                    //Model for CMYK 
        var CMYK = Backbone.Model.extend({defaults: {C: 0, M: 0, Y: 0, K:0}});
                    //End of Model for CMYK



        //Begin RGB View    
        RView = Backbone.View.extend({
            id: "RGB",
            view: {},               
            initialize: function(){

                            this.render();

            },
            render: function(){

                            $("#R").val(rgb.get("R"));
            $("#G").val(rgb.get("G"));
            $("#B").val(rgb.get("B"));

                            },
                            events: {
                                     "change" : "globalChange"
                           },

                            globalChange: function(){

                            //set all the textbox values
                            set();


                            var res_hsv = rgb2hsv(rgb.get("R"),rgb.get("G"),rgb.get("B"));
                            var res_cmyk = rgb2cmyk (rgb.get("R"),rgb.get("G"),rgb.get("B"));                   


                            $("#R").val(rgb.get("R"));
                            $("#G").val(rgb.get("G"));
                            $("#B").val(rgb.get("B"));


                            $("#H").val(res_hsv[0]);
                            $("#S").val(res_hsv[1]);
                            $("#V").val(res_hsv[2]);


                            $("#C").val(res_cmyk[0]);
                            $("#M").val(res_cmyk[1]);
                            $("#Y").val(res_cmyk[2]);
                            $("#K").val(res_cmyk[3]);

                          $('#HEX').css({'background-color' : '#'+rgbToHex(rgb.get("R"),rgb.get("G"),rgb.get("B"))});

                            //set all the textbox values
                            set();

                            //alert(rgb2cmyk (rgb.get("R"),rgb.get("G"),rgb.get("B")));

                            //alert(rgb2hsv(rgb.get("R"),rgb.get("G"),rgb.get("B")));

                            //alert("#"+rgbToHex(rgb.get("R"),rgb.get("G"),rgb.get("B")));

                             //alert(rgb.get("R")+", "+rgb.get("G")+", "+rgb.get("B"));

                        }


        });

        //End RGB View               

                    //Begin HSV View
                    HSV_View = Backbone.View.extend({
            id: "HSV",
            view: {},
            initialize: function(){

                                            this.render();

                },
            render: function(){
                $("#H").val(hsv.get("H"));
                $("#S").val(hsv.get("S"));
                $("#V").val(hsv.get("V"));

                },
                           events: {

                       "change" : "globalChange"

                           },

                        globalChange: function(){

                        //set all the textbox values
                        set();




                        var res_rgb = hsv2rgb(hsv.get("H"), hsv.get("S"), hsv.get("V"));
                        var res_cmyk = rgb2cmyk(res_rgb[0],res_rgb[1],res_rgb[2]);  

                        $("#R").val(res_rgb[0]);
                        $("#G").val(res_rgb[1]);
                        $("#B").val(res_rgb[2]);


                        $("#H").val(hsv.get("H"));
                        $("#S").val(hsv.get("S"));
                        $("#V").val(hsv.get("V"));


                        $("#C").val(res_cmyk[0]);
                        $("#M").val(res_cmyk[1]);
                        $("#Y").val(res_cmyk[2]);
                        $("#K").val(res_cmyk[3]);


                         $('#HEX').css({'background-color' : '#'+rgbToHex(res_rgb[0],res_rgb[1],res_rgb[2])});

                        //set all the textbox values
                        set();


                       //  alert(hsv2rgb(hsv.get("H"), hsv.get("S"), hsv.get("V")));

                      }
                  });                
                  //End HSV View

            // Begin CMYK View
        CMYK_View = Backbone.View.extend({
            id: "CMYK",
            view: {},
            initialize: function(){
                        this.render();

                },
            render: function(){

                    $("#C").val(cmyk.get("C"));
                    $("#M").val(cmyk.get("M"));
                    $("#Y").val(cmyk.get("Y"));
                    $("#K").val(cmyk.get("K"));

                },
                           events: {

                       "change" : "globalChange"

                           },

                        globalChange: function(){


                   //set all the textbox values
                        set();


                       var res_rgb =  cmyk2rgb(cmyk.get("C"), cmyk.get("M"), cmyk.get("Y"), cmyk.get("K"));
                       var res_hsv = rgb2hsv(res_rgb[0], res_rgb[1], res_rgb[2]);

                        $("#R").val(res_rgb[0]);
                        $("#G").val(res_rgb[1]);
                        $("#B").val(res_rgb[2]);


                        $("#H").val(res_hsv[0]);
                        $("#S").val(res_hsv[1]);
                        $("#V").val(res_hsv[2]);


                        $("#C").val(cmyk.get("C"));
                        $("#M").val(cmyk.get("M"));
                        $("#Y").val(cmyk.get("Y"));
                        $("#K").val(cmyk.get("K"));

                    $('#HEX').css({'background-color' : '#'+rgbToHex(res_rgb[0],res_rgb[1],res_rgb[2])});
                    //set all the textbox values
                        set();

                //alert("sending"+" "+cmyk.get("C")+" "+cmyk.get("M")+" "+cmyk.get("Y")+" "+cmyk.get("K"));

                 //alert(cmyk2rgb(cmyk.get("C"), cmyk.get("M"), cmyk.get("Y"), cmyk.get("K"))); 


                        }

                });
            // End CMYK View                                        



        // Models
                    var rgb = new RGB();
        var hex = new HEX();
        var hsv = new HSV();
        var cmyk = new CMYK();
                    // End of Models

                    // Views
                    var rgb_view = new RView({ el: $("#RGB") });        
                    var cmyk_view = new CMYK_View({ el: $("#CMYK") });      
                    var hsv_view = new HSV_View({ el: $("#HSV") }); 
                    //End of Views


                function set(){

                rgb.set({ R: $("#R").val() });
                rgb.set({ G: $("#G").val() });
                rgb.set({ B: $("#B").val() });

                hsv.set({ H: $("#H").val() });
                hsv.set({ S: $("#S").val() });
                hsv.set({ V: $("#V").val() });

                cmyk.set({ C: $("#C").val() });
                cmyk.set({ M: $("#M").val() });
                cmyk.set({ Y: $("#Y").val() });
                cmyk.set({ K: $("#K").val() });

                }


                //CMYK to RGB

                function cmyk2rgb(c, m, y, k){


                                var computedR = 0;
                                var computedG = 0;
                                var computedB = 0;

                                c = c / 100;
                                m = m / 100;
                                y = y / 100;
                                k = k / 100;

                                computedR = 1 - Math.min( 1, c * ( 1 - k ) + k );
                                computedG = 1 - Math.min( 1, m * ( 1 - k ) + k );
                                computedB = 1 - Math.min( 1, y * ( 1 - k ) + k );

                                computedR = Math.round( computedR * 255 );
                                computedG = Math.round( computedG * 255 );
                                computedB = Math.round( computedB * 255 );

                                return [computedR,computedG,computedB];

                }



                // End of CMYK to RGB



                //HSV to RGB

                function hsv2rgb(h,s,v){

                    var computedR = 0;
                    var computedG = 0;
                    var computedB = 0;

                    h = h / 360;
                    s = s / 100;
                    v = v / 100;

                    if (s == 0) {
                                        computedR = v * 255;
                                        computedG = v * 255;
                                        computedB = v * 255;
                                }

                else {
                                        var_h = h * 6;
                                        var_i = Math.floor(var_h);
                                        var_1 = v * (1 - s);
                                        var_2 = v * (1 - s * (var_h - var_i));
                                        var_3 = v * (1 - s * (1 - (var_h - var_i)));

                                        if (var_i == 0) {var_r = v; var_g = var_3; var_b = var_1}
                                        else if (var_i == 1) {var_r = var_2; var_g = v; var_b = var_1}
                                        else if (var_i == 2) {var_r = var_1; var_g = v; var_b = var_3}
                                        else if (var_i == 3) {var_r = var_1; var_g = var_2; var_b = v}
                                        else if (var_i == 4) {var_r = var_3; var_g = var_1; var_b = v}
                                        else {var_r = v; var_g = var_1; var_b = var_2};

                                        computedR = var_r * 255;
                                        computedG = var_g * 255;
                                        computedB = var_b * 255;

                                        computedR = Math.round(computedR);
                                        computedG = Math.round(computedG);
                                        computedB = Math.round(computedB);
                                }                



                     return [computedR,computedG,computedB];


                }


                // End of HSV to RGB



                //RGB to CMYK
                function rgb2cmyk (r,g,b) {
                 var computedC = 0;
                 var computedM = 0;
                 var computedY = 0;
                 var computedK = 0;

                 //remove spaces from input RGB values, convert to int
                 var r = parseInt( (''+r).replace(/\s/g,''),10 ); 
                 var g = parseInt( (''+g).replace(/\s/g,''),10 ); 
                 var b = parseInt( (''+b).replace(/\s/g,''),10 ); 

                 if ( r==null || g==null || b==null ||
                     isNaN(r) || isNaN(g)|| isNaN(b) )
                 {
                   alert ('Please enter numeric RGB values!');
                   return;
                 }
                 if (r<0 || g<0 || b<0 || r>255 || g>255 || b>255) {
                   alert ('RGB values must be in the range 0 to 255.');
                   return;
                 }

                 // BLACK
                 if (r==0 && g==0 && b==0) {
                  computedK = 1;
                  return [0,0,0,1];
                 }

                 computedC = 1 - (r/255);
                 computedM = 1 - (g/255);
                 computedY = 1 - (b/255);

                 var minCMY = Math.min(computedC,
                              Math.min(computedM,computedY));
                 computedC = (computedC - minCMY) / (1 - minCMY) ;
                 computedM = (computedM - minCMY) / (1 - minCMY) ;
                 computedY = (computedY - minCMY) / (1 - minCMY) ;
                 computedK = minCMY;

                 return [computedC,computedM,computedY,computedK];
                }

                //End of RGB to CMYK


                //RGB to HSV
                function rgb2hsv (r,g,b) {
                 var computedH = 0;
                 var computedS = 0;
                 var computedV = 0;

                 //remove spaces from input RGB values, convert to int
                 var r = parseInt( (''+r).replace(/\s/g,''),10 ); 
                 var g = parseInt( (''+g).replace(/\s/g,''),10 ); 
                 var b = parseInt( (''+b).replace(/\s/g,''),10 ); 

                 if ( r==null || g==null || b==null ||
                     isNaN(r) || isNaN(g)|| isNaN(b) ) {
                   alert ('Please enter numeric RGB values!');
                   return;
                 }
                 if (r<0 || g<0 || b<0 || r>255 || g>255 || b>255) {
                   alert ('RGB values must be in the range 0 to 255.');
                   return;
                 }
                 r=r/255; g=g/255; b=b/255;
                 var minRGB = Math.min(r,Math.min(g,b));
                 var maxRGB = Math.max(r,Math.max(g,b));

                 // Black-gray-white
                 if (minRGB==maxRGB) {
                  computedV = minRGB;
                  return [0,0,computedV];
                 }

                 // Colors other than black-gray-white:
                 var d = (r==minRGB) ? g-b : ((b==minRGB) ? r-g : b-r);
                 var h = (r==minRGB) ? 3 : ((b==minRGB) ? 1 : 5);
                 computedH = 60*(h - d/(maxRGB - minRGB));
                 computedS = (maxRGB - minRGB)/maxRGB;
                 computedV = maxRGB;
                 return [computedH,computedS,computedV];
                }
                // End of RGB to HSV

                //RGB to HEX

                function rgbToHex(R,G,B) {return toHex(R)+toHex(G)+toHex(B);}
                function toHex(n) {
                 n = parseInt(n,10);
                 if (isNaN(n)) return "00";
                 n = Math.max(0,Math.min(n,255));
                 return "0123456789ABCDEF".charAt((n-n%16)/16)
                      + "0123456789ABCDEF".charAt(n%16);
                }

                // End of RGB to HEX



    });
    </script>


    </head>

    <body>
    <div id="RGB">
    <table>
    <tr><td>R</td><td><input type="text" id="R" /></td><td>G</td><td><input type="text" id="G" /></td><td>B</td><td><input type="text" id="B" /></td></tr>
    </table>
    </div>
    <div id="HSV">
    <table>
    <tr><td>H</td><td><input type="text" id="H" /></td><td>S</td><td><input type="text" id="S" /></td><td>V</td><td><input type="text" id="V" /></td></tr>
    </table>
    </div>
    <div id="CMYK">
    <table>
        <tr><td>C</td><td><input type="text" id="C" /></td><td>M</td><td><input type="text" id="M"  /></td><td>Y</td><td><input type="text" id="Y"  /></td><td>K</td><td><input type="text" id="K"  /></td></tr>
    </table>
    </div>


    <canvas id="HEX" width="50" height="50" style=" border: 1px solid wheat; background-color: #fff;">
    Your browser does not support the HTML5 canvas tag.
    </canvas>

    </body>
    </html>
\$\endgroup\$
  • \$\begingroup\$ I'd recommend you checkout some Backbone data binding library such as Thorax, Epoxy or stick-it \$\endgroup\$ – megawac Aug 2 '14 at 15:17
2
\$\begingroup\$

Interesting question,

MVC

It seems, you did not nail the MVC concept perfectly.

In essence, you should have 1 single render routine for both RGB, HSV and CMYK since all these are just outputs of the same value from the model. It does not make sense to split that out. ( If you do this exercise you will notice the removal of a ton of copy pasted code).

Readability

Your indenting is terrible, please use a tool like jsbeautifier

Inconsistencies

  • You are using 2 ways to store RGB values:

    {R: 255, G: 255, B: 255} and [computedR,computedG,computedB]

    sticking to one convention would be far more readable ( I would go for the first convention )

  • You should split out the check for valid values of r, g and b these validity checks do not belong in conversion routines (plus, they are copy pasted right now)

JsHint

  • You are missing a ton of semicolons
  • In function rgb2cmyk (r,g,b) { you do not need to use var r since r is already known ( as a parameter ).

Naming

  • This, is terrible as well:

    var_h = h * 6;
    var_i = Math.floor(var_h);
    var_1 = v * (1 - s);
    var_2 = v * (1 - s * (var_h - var_i));
    var_3 = v * (1 - s * (1 - (var_h - var_i)));
    

    Do not prefix your variables with var_, it adds no value at all and hinders readability. Also variables 1 through 3 are terrible variable names.

  • Similarly, this

    var computedR = 0;
    var computedG = 0;
    var computedB = 0;
    

    has the prefix of computed which also adds very little (there is plenty of context to tell the reader that these values are computed.) I would have gone for

    var red, blue, green;
    

    since you do nothing with the 0 value anyway.

\$\endgroup\$

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.