Android Pattern locker plugin - Javascript

This is my new JavaScript plugin for generating Android like Pattern locker for web applications.

This is fully customizable plugin. Don't know where it can be used.... Just developed in my own interest.... please suggest some improvements for the same..... I think the captcha can be replaced with this plugin.

I have created the plugin using raphaeljs.

How to use? 

Step 1: Include the  following scripts in your head section (click here to download raphaeljs , click here to download pattern.js)
<script src="raphael-min.js" type="text/javascript"></script>
<script src="pattern.js" type="text/javascript"></script>

Step 2: Create an instance for the pattern with your custom properties and draw the pattern within the container with instance.draw(container).

var s = new Pattern(properties);
s.draw(container); 

Properties

Property Description Default Value
dimension Dimension of the patern
No. of rows x No. of columns
Eg:
dimension:‘5x5’
3x3
patternRadius Radius of the pattern circle
Eg:
patternRadius:25
20
patternGap Gap between each Pattern Circle
Eg:
patternGap:70
50
outerColor Color of the Pattern Circle. The color value can be either hex color or hex gradient color
Eg:
outerColor:’#FC0000’
outerColor :‘90-#239EE0:5-#1951A0:95’
#333333
innerColor Color of the inner circle within the Pattern Circle
Eg:
innerColor:’#FC0000’
innerColor :‘90-#239EE0:5-#1951A0:95’
90-#239EE0:5-#1951A0:95
background Pattern box background color
Eg:
background:’#FF00F0’
#000000
backgroundOpacity Pattern box background opacity
Eg:
background:0.5
1
hoverColor Stroke color of Pattern Circle when mouse over
Eg:
hoverColor:’#FF00F0’
#c8ee17
errorColor Stroke color of Pattern Circle for Invalid Pattern
Eg:
errorColor:’#FF00F0’
#FF0000
padding Pattern Box padding
Eg:
padding:25
Value of pattern radius
onFinish Handler to be executed when the user completes the pattern
Eg:
onFinish:function(value){
}
value – value of the pattern drawn passed to the handler
null

also you can set the property values externally with the created pattern Object

Functions

Function Description Return value
draw (container) Draw the pattern within the container. Should be called with pattern instance.
Eg:
s.draw(‘mydiv’);
Here ‘mydiv’ represents containerId
s.draw(document.getElementById(‘mydiv’));
Return Pattern Object
clear() Resets the pattern.
Eg:
s.clear();
Return Pattern Object
error() Used to mark the drawn pattern as error
Eg:
s.error();
Return Pattern Object
val() Used to get the value of drawn pattern
Eg:
s.val();
for the image attached in this post, the value returned will be ‘3-4-5-10-15
Return pattern value

eg:
var samplePattern = "3-4-5-10-15";
var s = new Pattern({
    dimension: '5x5',
    outerColor: '#000',
    patternRadius: 20,
    patternGap: 30,
    padding: 20,
    background: '#333',
    backgroundOpacity: 0.5
});
s.onFinish = function (v) {
    if (v == samplePattern) alert('success');
    else this.error();
};
s.draw('mypattern');


Plugin


var Pattern = function (options) {
    this.dimension = options.dimension && options.dimension.indexOf('x') !== -1 ? options.dimension.split('x') : [3, 3];

    this.patternRadius = options.patternRadius || 20;
    this.patternGap = options.patternGap || 50;
    this.innerColor = options.innerColor || '90-#239EE0:5-#1951A0:95';
    this.outerColor = options.outerColor || '#333333';
    this.background = options.background || '#000000';
    this.backgroundOpacity = options.backgroundOpacity || 1;
    this.hoverColor = options.hoverColor || '#c8ee17';

    this.errorColor = options.errorColor || '#FF0000';

    this.padding = options.padding || this.patternRadius;

    this.onFinish = options.onFinish || function (v) {};

    this.width = this.dimension[0] * (2 * this.patternRadius + this.patternGap) + (this.padding * 4) - this.patternGap;
    this.height = this.dimension[1] * (2 * this.patternRadius + this.patternGap) + (this.padding * 4) - this.patternGap;

    this.paper = null;
    this.patternCircle = [];
    this.value = [];
    this.blocker = null;

    var patternObj = this;
    this.isMouseDown = false;
    this.lineX = 0;
    this.lineY = 0;
    this.currentPath = null;
    this.currentTarget = null;


    //styes
    this.paperStyle = {
        fill: patternObj.background,
            'stroke-width': 0,
        opacity: patternObj.backgroundOpacity
    };
    this.selectedStyle = {
        'fill': '#CCC',
            'fill-opacity': 0.3,
            'stroke-width': 3,
            'stroke': '#C8E02A'
    };
    this.innerStyle = {
        'fill': patternObj.innerColor,
            'stroke-width': 0
    };
    this.outerStyle = {
        'fill': patternObj.outerColor,
            'stroke-width': 0,
            'fill-opacity': 1,
    };
    this.hoverStyle = {
        'stroke-width': 3,
            'stroke': patternObj.hoverColor,
            'stroke-opacity': 1
    };
    this.pathStyle = {
        stroke: '#FFF',
            'stroke-width': '5',
            'stroke-linecap': 'round',
            'opacity': 0.3
    };
    this.errorStyle = {
        'stroke-width': 3,
            'stroke': patternObj.errorColor,
            'stroke-opacity': 0.75
    };

    //common handlers
    var paperMouseUpHandler = function () {
        patternObj.isMouseDown = false;
        if (patternObj.currentPath) {
            patternObj.currentPath.remove();
        }
        if (patternObj.currentTarget.length && patternObj.currentTarget[0].data('connection') == 1) {
            patternObj.blocker = patternObj.paper.rect(0, 0, patternObj.width, patternObj.height, patternObj.patternRadius).attr({
                fill: '#000',
                    'stroke-width': 0,
                    'fill-opacity': 0
            });
        }
        patternObj.currentPath = [];
        patternObj.currentTarget = [];

        if (typeof (patternObj.onFinish)) {
            patternObj.onFinish(patternObj.val());
        }
    };

    var drawPathHandler = function (e) {
        e = e || window.event;

        var pX = e.pageX;
        var pY = e.pageY;
        if (pX === undefined) {
            pX = e.clientX;
            pY = e.clientY;
        }
        pX = pX - ((window.pageXOffset || document.body.scrollLeft) + patternObj.paper.canvas.getBoundingClientRect().left);
        pY = pY - ((window.pageYOffset || document.body.scrollTop) + patternObj.paper.canvas.getBoundingClientRect().top);

        e.stopPropagation();
        if (patternObj.isMouseDown && patternObj.currentPath) {
            patternObj.currentPath.attr('path', "M" + patternObj.lineX + " " + patternObj.lineY + "L" + pX + " " + pY);
        }
    };

    this.disableSelection = function (target) {
        if (typeof target.onselectstart != "undefined") {
            target.onselectstart = function () {
                return false
            };
        } else if (typeof target.style.MozUserSelect != "undefined") {
            target.style.MozUserSelect = "none";
        } else {
            target.onmousedown = function () {
                return false
            };
        }
    };

    this.createPattern = function (paperObj) {
        var x = 0,
            y = 0,
            i, j, elm = null;
        for (i = 0; i < patternObj.dimension[0]; i++) {
            x = i * (patternObj.patternGap + patternObj.patternRadius * 2) + patternObj.padding + patternObj.patternRadius;
            for (j = 0; j < patternObj.dimension[1]; j++) {
                y = j * (patternObj.patternGap + patternObj.patternRadius * 2) + patternObj.patternRadius + patternObj.padding;
                elm = patternObj.drawPatternPoint(paperObj, x, y, i, j);
                patternObj.patternCircle.push(elm);
                elm[0].data({
                    x: i,
                    y: j
                });
            }
        }
        patternObj.sort();
    };

    this.sort = function () {
        var i, j, tmpAry = [],
            elm, cnt = 1;
        for (i = 0; i < patternObj.dimension[1]; i++) {
            for (j = 0; j < patternObj.dimension[0]; j++) {
                elm = patternObj.getByXY(j, i);
                elm[0].data({
                    'value': cnt
                });
                elm[1].data({
                    'value': cnt
                });
                tmpAry.push(elm);
                cnt++;
            }
        }
        patternObj.patternCircle = tmpAry;
    };

    this.drawConnection = function (oCircle, iCircle, currentTarget) {
        patternObj.currentPath.attr('path', "M" + patternObj.lineX + " " + patternObj.lineY + "L" + (oCircle.getBBox().x + oCircle.getBBox().width / 2) + " " + (oCircle.getBBox().y + oCircle.getBBox().height / 2));
        patternObj.lineX = (oCircle.getBBox().x + oCircle.getBBox().width / 2);
        patternObj.lineY = (oCircle.getBBox().y + oCircle.getBBox().height / 2);

        patternObj.currentPath = patternObj.paper.path("M" + patternObj.lineX + " " + patternObj.lineY + "L" + patternObj.lineX - 2 + " " + patternObj.lineY - 2).attr(patternObj.pathStyle);
        patternObj.currentPath.mouseup(paperMouseUpHandler);
        currentTarget.data({
            'connection': 1
        }).attr(patternObj.selectedStyle);
        iCircle.data({
            'connection': 1
        });
        oCircle.data({
            'connection': 1
        }).attr(patternObj.selectedStyle);
        patternObj.value.push(oCircle.data('value'));
    };

    this.getByXY = function (x, y) {
        for (var i = 0; i < this.patternCircle.length; i++) {
            if (this.patternCircle[i][0].data('x') == x && this.patternCircle[i][0].data('y') == y) {
                return this.patternCircle[i];
            }
        }
        return null;
    };

    this.drawPatternPoint = function (paperObj, x, y, xi, yi) {
        var patternElm = paperObj.set();
        var oCircle = paperObj.circle(patternObj.padding + x, patternObj.padding + y, patternObj.patternRadius).attr(patternObj.outerStyle);
        var iCircle = paperObj.circle(patternObj.padding + x, patternObj.padding + y, patternObj.patternRadius / 3).attr(patternObj.innerStyle);
        iCircle.data({
            'connection': 0,
                'x': xi,
                'y': yi
        });
        oCircle.data({
            'connection': 0,
                'x': xi,
                'y': yi
        });

        patternElm.push(oCircle, iCircle);

        var patternMousemoveHandler = function (e) {
            oCircle.attr(patternObj.hoverStyle);

            if (patternObj.currentPath && (patternObj.currentTarget[0] !== oCircle[0] && patternObj.currentTarget[1] !== iCircle[0]) && (iCircle.data('connection') == 0)) {

                var x1 = patternObj.currentTarget[0].data('x');
                var y1 = patternObj.currentTarget[0].data('y');
                var x2 = oCircle.data('x');
                var y2 = oCircle.data('y');

                var xs = x1 > x2 ? x2 : x1;
                var xe = x1 > x2 ? x1 : x2;
                var ys = y1 > y2 ? y2 : y1;
                var ye = y1 > y2 ? y1 : y2;

                var i, j;
                var telm = null;
                var connect = function (a, b) {
                    telm = patternObj.getByXY(a, b);
                    if (telm[0].data('connection') == 0) {
                        patternObj.drawConnection(telm[0], telm[1], telm[0]);
                    }
                };
                if (x1 == x2 && y1 !== y2) {
                    if (y1 < y2) {
                        for (i = ys; i <= ye; i++) {
                            connect(xs, i);
                        }
                    } else {
                        for (i = ye; i >= ys; i--) {
                            connect(xs, i);
                        }
                    }
                } else if (y1 == y2 && x1 !== x2) {
                    if (x1 > x2) {
                        for (i = xe; i >= xs; i--) {
                            connect(i, ys);
                        }
                    } else {
                        for (i = xs; i <= xe; i++) {
                            connect(i, ys);
                        }
                    }
                } else if (Math.abs(x1 - x2) == Math.abs(y1 - y2)) {
                    for (i = 0; i <= Math.abs(ys - ye); i++) {
                        if (y1 < y2 && x1 > x2) {
                            connect(x1 - i, y1 + i);
                        } else if (y1 > y2 && x1 < x2) {
                            connect(x1 + i, y1 - i);
                        } else if (y1 < y2) {
                            connect(x1 + i, y1 + i);
                        } else {
                            connect(x1 - i, y1 - i);
                        }
                    }
                }
                patternObj.currentTarget.pop();
                patternObj.currentTarget = [oCircle, iCircle];
            }

        };

        var patternMouseoutHandler = function () {
            if (oCircle.data('connection') == 0) {
                oCircle.attr({
                    'stroke-width': 0
                });
            }
        };

        var patternMouseDownHandler = function (e) {
            patternObj.isMouseDown = true;
            patternObj.currentTarget = [oCircle, iCircle];
            patternObj.lineX = patternElm.getBBox().x + patternElm.getBBox().width / 2;
            patternObj.lineY = patternElm.getBBox().y + patternElm.getBBox().height / 2;
            patternObj.currentPath = patternObj.paper.path("M" + patternObj.lineX + " " + patternObj.lineY + "L" + patternObj.lineX - 2 + " " + patternObj.lineY - 2).attr(patternObj.pathStyle);
            patternObj.currentPath.touchend(paperMouseUpHandler);
            patternObj.currentPath.mouseup(paperMouseUpHandler);
        };

        patternElm.mousemove(patternMousemoveHandler);
        patternElm.mouseout(patternMouseoutHandler);
        patternElm.mousedown(patternMouseDownHandler);
        patternElm.mouseup(paperMouseUpHandler);
        return [oCircle, iCircle];
    };

    this.drawPaper = function (elm) {
        var paper = Raphael(elm, patternObj.width, patternObj.height);
        var rect = paper.rect(0, 0, patternObj.width, patternObj.height, patternObj.patternRadius).attr(patternObj.paperStyle);
        rect.mousemove(drawPathHandler);
        rect.mouseup(paperMouseUpHandler);
        return paper;
    };

    this.draw = function (elm) {
        patternObj.paper = patternObj.drawPaper(elm);
        patternObj.createPattern(this.paper);
        patternObj.disableSelection(patternObj.paper.canvas);
        return this;
    };

    this.clear = function () {
        var paths = patternObj.paper.canvas.getElementsByTagName('path');
        var cnt = paths.length;
        for (var i = 0; i < cnt; i++) {
            paths[0].parentNode.removeChild(paths[0]);
        }
        patternObj.paper.forEach(function (el) {
            if (el[0].tagName.toLowerCase() == 'circle') {
                el.data({
                    'connection': 0
                });
                if (patternObj.patternRadius == el.attr('r')) {
                    el.attr(patternObj.outerStyle);
                }
            }
        });
        if (patternObj.blocker) {
            patternObj.blocker.remove();
        }
        patternObj.blocker = null;
        patternObj.currentPath = null;
        patternObj.currentTarget = [];
        patternObj.value = [];
        
        return this;
    };

    this.error = function () {
        for (var i = 0; i < patternObj.patternCircle.length; i++) {
            if (patternObj.patternCircle[i][0].data('connection') == 1) {
                patternObj.patternCircle[i][0].attr(patternObj.errorStyle);
            }
        }
        
        return this;
    };

    this.val = function () {
        return patternObj.value.toString().replace(/,/g, '-');
    };
};

Popular posts from this blog

Simple Animation Using C

Personal Diary - A Mini Project Written in Turbo C With Graphical Interface

2D Reflection