function Tesla(node, options) {
    // (C) Andrea Giammarchi - Mit Style License
    var
        document = node.ownerDocument,
        abs = Math.abs,
        self = this,
        False = false
    ;
    if (!options) {
        options = {};
    }
    if (document) {
        document.addEventListener("touchstart", {
            handleEvent: self.handleEvent,
            self: self
        }, False);
        self.document = document;
    } else {
        self.document = node;
    }
    self.touches = [];
    self.frequency = abs(options.frequency) || 30;
    self.blur = abs(options.blur) || 0;
    self.noise = abs(options.noise) || 0;
    self.noiserad = (abs(options.noiserad) || 50) * 2;
    self.delta = abs(options.delta) || 5;
    self.ratio = abs(options.ratio) || 5;
    self.rays = abs(options.rays) || 1;
    self.width = abs(options.width) || 1;
    self.shadow = options.shadow ? decodeURIComponent(options.shadow) : "rgba(255,255,255,0.9)";
    self.stroke = options.stroke === false ? false : decodeURIComponent(options.stroke) || "rgba(0,0,255,0.6)";
    self.fill = options.fill === false ? false : decodeURIComponent(options.fill) || "rgba(255,255,255,0.9)";
    self.node = (self._e = node);
    self.canvas = document.createElement("canvas");
    self.resize(node.offsetWidth, node.offsetHeight);
    self.context = self.canvas.getContext("2d");
    self.draw(self);
    node.addEventListener("touchstart", self, False);
    node.addEventListener("mousedown", self, False);
    node.appendChild(self.canvas);
}
Tesla.prototype.resize = function (width, height) {
    var
        self = this,
        canvas = self.canvas
    ;
    canvas.width = width;
    canvas.height = height;
    self.center = {x: Math.round(width / 2), y: Math.round(height / 2)};
    return self;
};
Tesla.prototype.handleEvent = function (e) {
    var
        self = this,
        False = false,
        add = "addEventListener",
        remove = "removeEventListener"
    ;
    if (e.type == "touchstart") {
        if (self.self) {
            (self = self.self).node[remove]("touchstart", self, False);
            self._e = self.document;
        } else {
            self.document[remove]("touchstart", self, False);
        }
        self.node[remove]("mousedown", self, False);
        self.handleEvent = self.touchEvent;
        self._e[add]("touchmove", self, False);
        self._e[add]("touchend", self, False);
        self._e[add]("gesturestart", self.stopEvent, False);
        self._e[add]("gesturechange", self.stopEvent, False);
        self._e[add]("gestureend", self.stopEvent, False);
        self._e[add]("transformactionstart", self.stopEvent, False);
        self._e[add]("transformactionupdate", self.stopEvent, False);
        self._e[add]("transformactionend", self.stopEvent, False);
    } else {
        self.node[remove]("touchstart", self, False);
        self.document[remove]("touchstart", self, False);
        self.handleEvent = self.mouseEvent;
        self._e[add]("mousemove", self, False);
        self._e[add]("mouseup", self, False);
    }
    self.handleEvent(e);
};
Tesla.prototype.stopEvent = function (e) {
    e.preventDefault();
    e.stopPropagation();
};
Tesla.prototype.position = function () {
    var
        self = this,
        node = self.canvas,
        x = 0, y = 0
    ;
    do {
        x += node.offsetLeft;
        y += node.offsetTop;
    } while (node = node.offsetParent);
    self.position.x = x;
    self.position.y = y;
    return self;
};
Tesla.prototype.createNoise = function () {
    for (var
        self = this,
        canvas = self.canvas,
        random = Math.random,
        width = canvas.width / 2,
        height = canvas.height / 2,
        size = self.noiserad,
        hsize = size / 2,
        noise = [],
        i = self.noise;
        i--;
    ) {
        noise[i] = {
            noise: true,
            pageX: width - hsize + random() * size ,
            pageY: height - hsize + random() * size
        };
    }
    return noise;
};
Tesla.prototype.draw = function (self) {
    self
        .clean()
        .position()
        .createNoise()
        .forEach(
            self.lightning,
            self
        )
    ;
    self.touches.forEach(
        self.lightning,
        self
    );
    self.timeout = setTimeout(
        self.draw,
        self.frequency,
        self
    );
};

Tesla.prototype.lightning = function (e) {
    var
        self = this,
        context = self.context
    ;
    context.beginPath();
    lightning(
        context,
        self.center.x,
        self.center.y,
        e.pageX - self.position.x,
        e.pageY - self.position.y,
        self.delta,
        self.ratio,
        self.rays
    );
    context.lineWidth = self.width;
    if (self.blur) {
        context.shadowBlur = self.blur;
        context.shadowColor = self.shadow;
    }
    if (self.fill && !e.noise) {
        context.fillStyle = self.fill;
        context.fill();
    }
    if (self.stroke) {
        context.strokeStyle = self.stroke;
        context.stroke();
    }
};
Tesla.prototype.clean = function () {
    var
        self = this,
        canvas = self.canvas
    ;
    self.context.clearRect(0, 0, canvas.width, canvas.height);
    return self;
};
Tesla.prototype.touchEvent = function (e) {
    var
        self = this,
        forEach = self.touches.forEach;
    ;
    self.stopEvent(e);
    (self.touches = e.touches || []).forEach = forEach;
}
Tesla.prototype.mouseEvent = function (e) {
    var self = this;
    self.stopEvent(e);
    switch (e.type) {
        case "mouseup":
            self.touches = [];
            break;
        case "mousedown":
            self.touches = [e];
        case "mousemove":
            if (self.touches.length) {
                self.touches = [e];
            }
            break;
    }
};
