2
\$\begingroup\$

I am trying to write a jQuery Plugin from a piece of JS code I had written. What this plugin does is it converts a select box into a map of Australia where I can select individual states. It's working perfectly well. However, I'd like to know how I can write it with OOP principles like SOLID etc. can someone review my code and suggest changes.

;(function ( $, window, document, undefined ) {

// undefined is used here as the undefined global variable in ECMAScript 3 is
// mutable (ie. it can be changed by someone else). undefined isn't really being
// passed in so we can ensure the value of it is truly undefined. In ES5, undefined
// can no longer be modified.

// window and document are passed through as local variable rather than global
// as this (slightly) quickens the resolution process and can be more efficiently
// minified (especially when both are regularly referenced in your plugin).

// Create the defaults once
var pluginName = "genAusMap",
    defaults = {};

// The actual plugin constructor
function Plugin ( element, options ) {
    this.element = element;
    var imagesLoaded = 0;
    var canvas, ctx,ausMap,states;
    var elemLeft,elemTop;

    this.setCanvas = function(c,w,h){
        canvas = c;
        canvas.setAttribute('width',w);
        canvas.setAttribute('height',h);
        // Add event listener for `click` events.
        $(canvas).on('click', function(event) {
            var x = event.pageX - elemLeft, y = event.pageY - elemTop;
            canvas.width = canvas.width; // clear the canvas
            ctx.drawImage(ausMap, 0, 0);
            if (y > 59 && y < 164 && x > 146 && x < 205) {
                drawImageOnLoad('nt');
            }
            if (y > 59 && y < 192 && x > 205 && x < 301) {
                drawImageOnLoad('qld');
            }
            if (y > 164 && y < 249 && x > 147 && x < 214) {
                drawImageOnLoad('sa');
            }
            if (y > 260 && y < 295 && x > 229 && x < 261) {
                drawImageOnLoad('tas');
            }
            if (y > 223 && y < 260 && x > 213 && x < 265) {
                drawImageOnLoad('vic');
            }
            if ((y > 191 && y < 222 && x > 220 && x < 297) || (y > 221 & y < 233 & x > 230  & x < 277) || (y > 233 & y < 245 & x > 254  & x < 269) ) {
                // ctx.rect(220,191,77,31); ctx.rect(230,221,47,12); ctx.rect(254,233,15,12); ctx.stroke();       // draw a rectangle box with the various co-ordinates to know where to start from and where to end for nsw /
                drawImageOnLoad('nsw');
            }
            if ((y > 207 && y < 232 && x > 240 && x < 270) || (y > 233 & y < 245 & x > 254  & x < 269)) {
                // ctx.rect(240,207,30,25); ctx.rect(254,233,15,12); ctx.stroke();
                drawImageOnLoad('act');
            }
            if (y > 75 && y < 235 && x > 40 && x < 146) {
                drawImageOnLoad('wa');
            }
        });
    };

    this.setCanvasOffsets = function(l,t){
        elemLeft = l;
        elemTop = t;
    };

    this.getCanvas = function(){
        return canvas;
    };
    this.setCtx = function(c){
        ctx = c;
    };
    this.getCtx = function(){
        return ctx;
    };
    this.setAusMap = function(map){
        ausMap = map;
    }
    this.setStates = function(listOfStates){
        states = listOfStates;
    }
    // jQuery has an extend method which merges the contents of two or
    // more objects, storing the result in the first object. The first object
    // is generally empty as we don't want to alter the default options for
    // future instances of the plugin

    var checkAllImagesLoaded = function () {
        imagesLoaded += 1;
        if(imagesLoaded == 9) {
            drawImageOnLoad();
        }
    };
    var drawImageOnLoad = function(drawState){
        drawState = typeof drawState !== 'undefined' ? drawState : ''; // Assigning Default value
        if(imagesLoaded == 9) {
            canvas.width = canvas.width;
            ctx.drawImage(ausMap, 0, 0);
            ctx.globalAlpha = 0.5;
            $.each(states, function(state){
                state = this;
                console.log(drawState);
                if(drawState == state.getAttribute('data-name')) {
                    ctx.drawImage(state, parseInt(state.getAttribute('data-positionX')), parseInt(state.getAttribute('data-positionY')));
                    //selectDeselectOption(state.getAttribute('data-name'));
                }
            });
        }
    };
    this.imageObj = function(src, x, y,name){
        this.src = src;
        this.x = x;
        this.y = y;
        this.name = name;

    };
    this.loadImage = function(image, onload) {
        var img = new Image();
        img.onload = checkAllImagesLoaded;
        img.src = image.src;
        img.setAttribute('data-positionX',image.x);
        img.setAttribute('data-positionY', image.y);
        img.setAttribute('data-name', image.name);
        return img;
    };
    var selectDeselectOption = function(state){
        $(this.element).val(state);
    };


    if(this.element.tagName == 'SELECT') {
        this.settings = $.extend( {}, defaults, options );
        this._defaults = defaults;
        this._name = pluginName;
        this.states = [];
        this.init();
    } else {
        alert('Cannot instantiate plugin '+pluginName+' on a '+this.element.tagName+'. Try using a SELECT element instead');
    }
}

// Avoid Plugin.prototype conflicts
$.extend(Plugin.prototype, {
    init: function () {
        $el = $(this.element);
        this.setCanvas(document.createElement('canvas'),319,313);
        this.setCtx(this.getCanvas().getContext("2d"));
        this.setAusMap(this.loadImage(new this.imageObj(this.settings.mapOfAustralia ,0,0)));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/act.png',0,0,'act')));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/nsw.png',0,0,'nsw')));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/nt.png',0,0,'nt')));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/sa.png',0,0,'sa')));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/qld.png',0,0,'qld')));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/tas.png',0,0,'tas')));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/vic.png',0,0,'vic')));
        this.states.push(this.loadImage(new this.imageObj('http://localhost/test/wa.png',0,0,'wa')));
        this.setStates(this.states);
        $el.after(this.getCanvas());
        console.log(this.getCanvas());
        this.setCanvasOffsets(this.getCanvas().offsetLeft,this.getCanvas().offsetTop)
    }
});

// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[ pluginName ] = function ( options ) {
    this.each(function() {
        if ( !$.data( this, "plugin_" + pluginName ) ) {
            $.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
        }
    });

    // chain jQuery functions
    return this;
};

})( jQuery, window, document );
\$\endgroup\$
1
\$\begingroup\$

This plugin looks like an attempt to reinvent image maps using a canvas. Most of this code could be eliminated in favour of techniques that were available in 1997.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your comments. It would have definitely become easier if I had used image maps. However, let's just assume I want to use canvas in lieu of image maps. In that case, how can I improve the JS bit of the code? Having said that I would definitely consider a rewrite using image maps. \$\endgroup\$ – J-D Jun 17 '14 at 0:16

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.