(function ($_) {
    function gid (id) {
        return document.getElementById(id);
    }
    function random (prefix) {
        return (prefix?prefix:"") + Math.random().toString(36).slice(2);
    }
    function JobQueue (_interval) {
        var self = {
            init: function () {
                return this;
            },
            regist: function (callback, bindObj) {
                this.tasks.push({
                    callback: callback,
                    bindObj: bindObj || this,
                    arguments: []
                });
                if ( this.task_id == null ) {
                    return this.start_worker();
                }
            },
            start_worker: function () {
                this.task_id = setInterval(function () {
                    if ( self.tasks.length && !self.lock ) {
                        self.lock = true;
                        var task = self.tasks.shift();
                        task.callback.apply(task.bindObj, task.arguments);
                        self.lock = false;
                    } else if ( self.tasks.length == 0 && self.lock ) {
                        clearInterval( self.task_id );
                        self.task_id = null;
                    } else {}
                }, this.interval);
            },
            setInterval: function (_interval) {
                this.interval = _interval;
            },
            tasks: [],
            task_id: null,
            lock: false,
            interval: _interval || 100
        };
        return self.init();
    };
    var worker = new JobQueue(200),
        loader = function (image, retry_count) {
            var retry = arguments.callee.caller,
                retry_count = retry_count || 0;
            worker.regist(function () {
                this.src     = this.getAttribute('rel');
                if ( retry_count > 0 ) {
                    this.onerror = function () {
                        retry.count = !retry.count ? 1 : ++retry.count;
                        return retry.count <= retry_count ? retry.apply(image, [image, retry_count]) : ( image.onerror=function(){} );
                    };
                }
            }, image);
        },
        format = function (str, params) {
            var params = params || {};
            return ( str || "" ).replace(/(?:{([^}]+)})/g, function (a,vars) {
                return params[vars] ? params[vars] : "";
            });
        };
    $_.fn.extend({
        carouselSuperLite: function (_option) {
            var option = $_.extend({
                dataSource: "",
                params: {},
                image_width: 60,
                image_height: 60,
                count: 6,
                move_count: 6,
                speed: 1000,
                easing: 'swing',
                position: function(){return true},
                format: '<a href="{link}" target="_blank"><img src="/img/no_image.gif" /></a>',
                disableClassPrev: 'disable-prev',
                disableClassNext: 'disable-next',
                selectClass: 'selected',
                liClass: '',
                retry_count: 0,
                load_interval: 100,
                data: [],
                padding: 6,
                callback: function(){},
                touchClass: ''
            }, _option), targets = this, isSupportCSS3Animation = ( "webkitTransform" in document.body.style ) && !( navigator.appVersion.match(/Chrome/) && navigator.platform.match(/Win/) ), isSupportTranslate3D = (function () {
                var s = document.createElement('span');
                s.style.webkitTransform = 'translate3d(0, 0, 0)';
                return !!( s.style.webkitTransform );
            })();
            var images = $_.map(option.data, function (data) {
                    return $_.extend({}, data);
                }),
                image_list_array = $_.map(images, function (image, i) {
                    // fixed position
                    $_.isFunction( option.position ) && option.position.call(null, image, i) && ( option.position = i );
                    // format list tag
                    return format('<li id="{list_id}" class="{liClass}">' + option.format + '</li>', $_.extend(image, option, {
                        list_id:random('_'),
                        index: i,
                        liClass: option.liClass ? ( typeof option.liClass == 'function' ) ? option.liClass(i) : option.liClass : ''
                    }));
                });
            /*
            if ( $_.browser.msie && $_.browser.version >= 8 ) {
                var position    = $_.isFunction( option.position ) ? 0 : option.position,
                    count       = option.count > images.length ? images.length : option.count,
                    center      = Math.max( position - Math.floor(count/2), 0 ),
                    image_list  = image_list_array.slice(center, center+count).join(""),
                    images      = images.slice(center, center+count);
                option.position = Math.max(position-center, 0);
            } else {
                var image_list = image_list_array.join("");
            }
            */
            var image_list = image_list_array.join("");

            worker.setInterval(option.load_interval);
            return targets.each(function (target_count) {
                var ulbox       = $_(option.ulbox,   this).append( image_list ),
                    prevBtn     = $_(option.prevBtn, this),
                    nextBtn     = $_(option.nextBtn, this),
                    max         = Math.max(images.length - option.count,0),
                    count       = option.count > images.length ? images.length : option.count,
                    box_width   = ( option.image_width * count ) + ( count * ( option.padding * 2 ) ),
                    position    = $_.isFunction( option.position ) ? 0 : option.position,
                    easing      = $_.easing.hasOwnProperty(option.easing) ? option.easing : 'swing',
                    retry_count = option.retry_count,
                    loaded      = {},
                loadImage = function (specify_pos, specify_count) {
                        var p = specify_pos   || position,
                            c = specify_count || count;
                        $_.each(images.slice(p, p+c), function (i, image) {
                            if ( !loaded.hasOwnProperty( image.list_id ) ) {
                                try {
                                    var tag = gid( image.list_id ).getElementsByTagName('img')[0];
                                    loader(tag, retry_count);
                                } catch (e) {}
                                loaded[ image.list_id ] = true;
                            }
                        });
                    },
                scrollTo  = function (is_static) {
                        loadImage();
                        option.disableClassPrev && prevBtn[ ( position > 0   ? 'remove' : 'add' ) + 'Class' ]( option.disableClassPrev );
                        option.disableClassNext && nextBtn[ ( position < max ? 'remove' : 'add' ) + 'Class' ]( option.disableClassNext );
                        var left = -( ( option.image_width * position ) + ( position * ( option.padding * 2 ) ) ) + "px";
                        option.callback( position );
                        return isSupportCSS3Animation ? ( ulbox[0].style.webkitTransform = translate3DString(left) ) : ulbox.animate({
                            left: left
                        }, is_static ? 0 : option.speed, easing); 
                    },
                    scrollToCenter = function () {
                        $_('#'+images[ position ].list_id).addClass(option.selectClass);
                        position = Math.max( Math.min( position - Math.floor( count / 2 ), max ), 0 );
                        scrollTo(true);
                    },
                    calculatePosition = function (pos) {
                        return -( ( option.image_width * pos ) + ( pos * ( option.padding * 2 ) ) );
                    },
                    /* utility functions for smartphone */
                    getWebkitTransformX = function (elem, not_found_value) {
                        return parseInt( ( ( elem.style.webkitTransform || '' ).match(/\(([\d-]+)/) || ['', not_found_value || 0] )[1] );
                    },
                    translate3DString = function (x,y,z) {
                        var args = isSupportTranslate3D ? [x,y,z] : [x,y],
                            prop = isSupportTranslate3D ? 'translate3d' : 'translate';
                        return prop + '(' + args.map(function (v) { return ( !v ? 0 : parseInt(v, 10) ) + "px" }).join(',') + ')';
                    };
                if (isSupportCSS3Animation) {
                    ulbox.css({
                        webkitTransition      : '-webkit-transform 0.5s ease-out',
                        webkitUserSelect      : 'none',
                        webkitTransitionDelay : 'initial'
                    });
                }
                /* bind event listener */
                prevBtn.bind('mousedown', function () {
                    position = Math.max(0, position - option.move_count);
                    scrollTo();
                });
                nextBtn.bind('mousedown', function () {
                    // console.log([position + option.move_count, position, option.move_count]);
                    position = ( position + option.move_count ) > max ? max : ( position + option.move_count );
                    // console.log(position);
                    scrollTo();
                });
                /* initialize */
                scrollToCenter();
                /* add methods */
                $_( this ).data({
                    scrollPosition: function (_pos) {
                        position = parseInt( _pos );
                        scrollTo();
                    }
                })
                // touch interface
                var touchTargetElem = option.touchClass && $_(option.touchClass, this).size() && $_(option.touchClass, this)[0];
                touchTargetElem && (touchTargetElem.ontouchstart = function (event) {
                    var touch  = event.targetTouches[0],
                        startX = touch.clientX,
                        startL = getWebkitTransformX(ulbox[0], ulbox.css('left')),
                        maxL   = calculatePosition(max);
                    ulbox.css({
                        webkitTransition: '-webkit-transform 0.1ms linear'
                    });
                    var passedIndex = [], movecount = 0;
                    touchTargetElem
                        .ontouchmove = function (event2) {
                            var clientX  = event2.targetTouches[0].clientX,
                                currentX = startL + ( clientX - startX ),
                                currentP = Math.round( Math.abs( currentX ) / ( option.image_width + ( option.padding * 2 ) ) );
                            if ( !passedIndex.length ) {
                                passedIndex.push( currentP );
                            }
                            else if ( passedIndex.indexOf( currentP ) == -1 ) {
                                loadImage( currentP );
                                passedIndex.push( currentP );
                            }
                            ulbox.css({
                                webkitTransform: translate3DString( currentX )
                            });
                            movecount++;
                            event2.preventDefault();
                        };
                    touchTargetElem
                        .ontouchend = function (event3) {
                            ulbox.css({
                                webkitTransition: '-webkit-transform 0.5s ease-out'
                            });
                            var endX = getWebkitTransformX( ulbox[0] );
                            if ( movecount < 2 ) {
                               var targetElement = touch.target,
                                   targetAnchor  = targetElement.tagName == 'IMG' ? $_( targetElement.parentNode ) : $_('a:eq(0)', targetElement);
                               if ( targetAnchor.size() && targetAnchor.is('a') ) {
                                   var targetHref = targetAnchor.attr('href');
                                   if ( targetHref && !/^javascript/.test( targetHref ) ) {
                                       location.href = targetHref;
                                   } else {
                                       $_.each(['mousedown', 'click', 'mouseup'], function (i, ename) {
                                           targetAnchor.trigger(ename);
                                       });
                                   }
                               }
                            }
                            else if ( endX > 0 ) {
                               ulbox.css({
                                   webkitTransform: translate3DString()
                               });
                               position = 0;
                            }
                            else if ( endX < maxL ) {
                               ulbox.css({
                                   webkitTransform: translate3DString(maxL)
                               });
                               position = max;
                            }
                            else {
                               var nx = Math.round( Math.abs( endX ) / ( option.image_width + ( option.padding * 2 ) ) );
                               ulbox.css({
                                   webkitTransform: translate3DString( calculatePosition( nx ) )
                               });
                               position = nx;
                            }
                            option.disableClassPrev && prevBtn[ ( position > 0   ? 'remove' : 'add' ) + 'Class' ]( option.disableClassPrev );
                            option.disableClassNext && nextBtn[ ( position < max ? 'remove' : 'add' ) + 'Class' ]( option.disableClassNext );
                            touchTargetElem.ontouchmove = function () {};
                            touchTargetElem.ontouchend = function () {};
                            event3.preventDefault();
                        };
                    event.preventDefault();
                });
            });
        }
    });
})(jQuery);

