jQuery.fn.imageRollover = function() {
    // Константы режимов "перекатывания"
    var ROLLOVER_MODE_OVER = 1;
    var ROLLOVER_MODE_OUT = 2;
    var ROLLOVER_MODE_CLICK = 3;

    // Константы ключей в словаре data для получений URL рисунков
    var IMAGE_URL_KEY_COMMON = "commonImageUrl";
    var IMAGE_URL_KEY_HOVERED = "hoveredImageUrl";
    var IMAGE_URL_KEY_CLICKED = "clickedImageUrl";

    var _instance = this; // экземпляр объекта
    var _preloadedImages = new Array(); // массив для хранения предварительно загруженных рисунков

    var originalImageSuffixRegexp = null;
    var selectedImageSuffixRegexp = null;
    var hoveredImageSuffixRegexp = null;
    var clickedImageSuffixRegexp = null;
    var backgroundImageUrlRegExp = null;

    _instance.selectedElements = null; // выбранные элементы
    _instance.getRolloverElement = function(targetElement) { }; // функция для получения элемента, к которому применяется эффект "перекатывания"
    _instance.useBackgroundImage = false; // признак использования css-свойства background-image вместо атрибута src
    _instance.useEffectsForClick = false; // разрешает использовать эффекты при клике по элементу
    _instance.useEffectsOnSelectedImage = true; // разрешает использование эффектов для выбранного (текущего) рисунка
    _instance.originalImageSuffix = ""; // суффикс обычного рисунка
    _instance.selectedImageSuffix = ""; // суффикс выбранного рисунка
    _instance.hoveredImageSuffix = ""; // суффикс рисунка, на который наведена мышь
    _instance.clickedImageSuffix = ""; // суффикс рисунка, на котором кликнули мышью


    // Инициализирует объект
    _instance.init = function(options) {
        // Запоминаем настройки
        _instance.saveOptions(options);

        // Создаем регулярные выражения
        originalImageSuffixRegexp = new RegExp("(" + _instance.originalImageSuffix + ")(\.[a-z]+)$", "ig");
        selectedImageSuffixRegexp = new RegExp("(" + _instance.selectedImageSuffix + ")(\.[a-z]+)$", "ig");
        hoveredImageSuffixRegexp = new RegExp("(" + _instance.hoveredImageSuffix + ")(\.[a-z]+)$", "ig");
        clickedImageSuffixRegexp = new RegExp("(" + _instance.clickedImageSuffix + ")(\.[a-z]+)$", "ig");
        if (_instance.useBackgroundImage) {
            backgroundImageUrlRegExp = new RegExp("^url\\(\"?(.[^\"]*)\"?\\)$", "ig");
        }

        // Запускаем предварительную загрузку рисунков
        _instance.preloadImages();

        for (var i = 0; i < _instance.selectedElements.length; i++) {
            jQuery(_instance.selectedElements[i]).bind(
				"mouseover",
				{ rolloverMode: ROLLOVER_MODE_OVER },
				_instance.rolloverImage
			);

            jQuery(_instance.selectedElements[i]).bind(
				"mouseout",
				{ rolloverMode: ROLLOVER_MODE_OUT },
				_instance.rolloverImage
			);

            if (_instance.useEffectsForClick) {
                jQuery(_instance.selectedElements[i]).bind(
					"click",
					{ rolloverMode: ROLLOVER_MODE_CLICK },
					_instance.rolloverImage
				);
            }
        }
    };

    // Запоминает настройки
    _instance.saveOptions = function(options) {
        if (!jQuery.isNullOrEmpty(options)) {
            if (!jQuery.isNullOrEmpty(options.selectedElements)) {
                _instance.selectedElements = options.selectedElements;
            }

            if (!jQuery.isNullOrEmpty(options.getRolloverElement)) {
                _instance.getRolloverElement = options.getRolloverElement;
            }

            if (!jQuery.isNullOrEmpty(options.useBackgroundImage)) {
                _instance.useBackgroundImage = options.useBackgroundImage;
            }

            if (!jQuery.isNullOrEmpty(options.useEffectsForClick)) {
                _instance.useEffectsForClick = options.useEffectsForClick;
            }

            if (!jQuery.isNullOrEmpty(options.useEffectsOnSelectedImage)) {
                _instance.useEffectsOnSelectedImage = options.useEffectsOnSelectedImage;
            }

            if (!jQuery.isNullOrEmpty(options.originalImageSuffix)) {
                _instance.originalImageSuffix = options.originalImageSuffix;
            }

            if (!jQuery.isNullOrEmpty(options.hoveredImageSuffix)) {
                _instance.hoveredImageSuffix = options.hoveredImageSuffix;
            }

            if (!jQuery.isNullOrEmpty(options.selectedImageSuffix)) {
                _instance.selectedImageSuffix = options.selectedImageSuffix;
            }

            if (!jQuery.isNullOrEmpty(options.clickedImageSuffix)) {
                _instance.clickedImageSuffix = options.clickedImageSuffix;
            }
        }
    };

    // Предварительная загрузка рисунков
    _instance.preloadImages = function() {
        var rolloverElement = null; // рисунок
        var previousImageUrl = ""; // URL предыдущего рисунка
        var currentImageUrl = ""; // URL текущего рисунка
        var hoveredImageUrl = ""; // URL рисунка, на который наведена мышь
        var clickedImageUrl = ""; // URL рисунка, на котором кликнули мышью

        for (var i = 0; i < _instance.selectedElements.length; i++) {
            rolloverElement = _instance.getRolloverElement(_instance.selectedElements[i]);
            currentImageUrl = _instance.getImageUrl(rolloverElement);

            if (currentImageUrl != previousImageUrl) {
                hoveredImageUrl = _instance.getNewImageUrl(currentImageUrl, ROLLOVER_MODE_OVER);
                _preloadedImages.add(jQuery.createImage(hoveredImageUrl));

                if (_instance.useEffectsForClick) {
                    clickedImageUrl = _instance.getNewImageUrl(currentImageUrl, ROLLOVER_MODE_CLICK);
                    _preloadedImages.add(jQuery.createImage(clickedImageUrl));
                }
            }

            // Сохраняем URL рисунков в коллекции data текущего элемента
            rolloverElement.data(IMAGE_URL_KEY_COMMON, currentImageUrl);
            rolloverElement.data(IMAGE_URL_KEY_HOVERED, hoveredImageUrl);
            rolloverElement.data(IMAGE_URL_KEY_CLICKED, clickedImageUrl);

            // Запоминаем URL текущего рисунка
            previousImageUrl = currentImageUrl;
        }

        /*for (var i = 0; i < _preloadedImages.length; i++)
        {
        alert(_preloadedImages[i].src);
        }*/
    };

    // Применяет к рисунку эффект "перекатывания"
    _instance.rolloverImage = function(event) {
        var rolloverElement = _instance.getRolloverElement(this); // рисунок
        var oldImageUrl = _instance.getImageUrl(rolloverElement); // URL старого рисунка
        var rolloverMode = event.data.rolloverMode; // идентификатор режима "перекатывания"
        var newImageUrl = ""; // URL нового рисунка
        if (rolloverMode == ROLLOVER_MODE_OVER) {
            newImageUrl = rolloverElement.data(IMAGE_URL_KEY_HOVERED);
        }
        else if (rolloverMode == ROLLOVER_MODE_CLICK) {
            newImageUrl = rolloverElement.data(IMAGE_URL_KEY_CLICKED);
        }
        else {
            newImageUrl = rolloverElement.data(IMAGE_URL_KEY_COMMON);
        }

        if (_instance.useEffectsOnSelectedImage
			|| (oldImageUrl.search(selectedImageSuffixRegexp) == -1
			&& newImageUrl.search(selectedImageSuffixRegexp) == -1)) {
            _instance.setImageUrl(rolloverElement, newImageUrl);
        }

        rolloverElement = null;
    };

    // Возвращает URL рисунка
    _instance.getImageUrl = function(rolloverElement) {
        var imageUrl = ""; // URL рисунка

        if (!jQuery.isNullOrEmpty(rolloverElement)) {
            if (_instance.useBackgroundImage) {
                imageUrl = rolloverElement
					.css("background-image")
					.replace(backgroundImageUrlRegExp, "$1")
					;
            }
            else {
                imageUrl = rolloverElement.attr("src");
            }
        }

        return imageUrl;
    };

    // Задает URL рисунка
    _instance.setImageUrl = function(rolloverElement, imageUrl) {
        if (!jQuery.isNullOrEmpty(rolloverElement)) {
            if (_instance.useBackgroundImage) {
                rolloverElement.css("background-image", "url(" + imageUrl + ")");
            }
            else {
                rolloverElement.attr("src", imageUrl);
            }
        }
    };

    // Возвращает URL рисунка с использованием нового суффикса
    _instance.getNewImageUrl = function(oldImageUrl, rolloverMode) {
        var oldImageSuffix = ""; // старый суффикс
        var oldImageSuffixRegexp = null; // регулярное выражение для поиска/замены старого суффикса
        var newImageSuffix = ""; // новый суффикс
        var newImageUrl = ""; // URL нового рисунка

        if (rolloverMode == ROLLOVER_MODE_OVER) {
            if (oldImageUrl.search(selectedImageSuffixRegexp) != -1) {
                oldImageSuffix = _instance.selectedImageSuffix;
                oldImageSuffixRegexp = selectedImageSuffixRegexp;
            }
            else if (oldImageUrl.search(clickedImageSuffixRegexp) != -1) {
                oldImageSuffix = _instance.clickedImageSuffix;
                oldImageSuffixRegexp = clickedImageSuffixRegexp;
            }
            else {
                oldImageSuffix = _instance.originalImageSuffix;
                oldImageSuffixRegexp = originalImageSuffixRegexp;
            }
            newImageSuffix = _instance.hoveredImageSuffix;
        }
        else if (rolloverMode == ROLLOVER_MODE_OUT) {
            oldImageSuffix = _instance.hoveredImageSuffix;
            oldImageSuffixRegexp = hoveredImageSuffixRegexp;
            if (oldImageUrl.search(selectedImageSuffixRegexp) != -1) {
                newImageSuffix = _instance.selectedImageSuffix;
            }
            else if (oldImageUrl.search(clickedImageSuffixRegexp) != -1) {
                newImageSuffix = _instance.clickedImageSuffix;
            }
            else {
                newImageSuffix = _instance.originalImageSuffix;
            }
        }
        else if (rolloverMode == ROLLOVER_MODE_CLICK) {
            oldImageSuffix = _instance.hoveredImageSuffix;
            oldImageSuffixRegexp = hoveredImageSuffixRegexp;
            newImageSuffix = _instance.clickedImageSuffix;
        }

        newImageUrl = oldImageUrl.replace(oldImageSuffixRegexp, newImageSuffix + "$2");

        return newImageUrl;
    }
}