jQuery.fn.extend({ create3DCarousel: function (/*options*/) { //config var options = { vanishingPoint: arguments[0].vanishingPoint, speed: arguments[0].speed } //3DCarousel class $3DCarousel = function (jEl) { var parent = this; //Extra Class /* NODE */ $Node = function (/*node, x, y, z*/) { var self = this; /*fields*/ this.node = arguments[0].node; //DOM node this.nodeIMG = { //CONST width: self.node.find("img:first").width(), height: self.node.find("img:first").height() } this.x = arguments[0].x; this.y = arguments[0].y; //CONST this.z = arguments[0].z; this.angle_TO_FRONT = arguments[0].angle_TO_FRONT; this.depth = 10000 - Math.round(Math.abs(this.z)); this.timer; /*methods*/ //calculate the item dimension relate to its position(z-depth) this.calculate = function () { /*caculation*/ //Positioning var sinX = Math.cos((-90-this.angle_TO_FRONT)*Math.PI/180); var cosX = Math.sin((-90-this.angle_TO_FRONT)*Math.PI/180); this.z = parent.Radius*(1+cosX); this.depth = 10000 - Math.round(Math.abs(this.z)); this.scalingRatio = 1 - this.z/parent.vanishingPoint; this.x = parent.Radius*sinX/this.scalingRatio; //Scaling this.surfaceScalingRatio = Math.pow(this.scalingRatio, 2); //LIs this.node.width( Math.round( this.surfaceScalingRatio*this.nodeIMG.width ) ); this.node.height( Math.round( this.surfaceScalingRatio*this.nodeIMG.height ) ); //IMG this.node.find("img:first").css("width", Math.round( this.surfaceScalingRatio*this.nodeIMG.width ) ); this.node.find("img:first").css("height", Math.round( this.surfaceScalingRatio*this.nodeIMG.height ) ); } //output to browser: coords of viewport is top:0, left:0 this.outputToScreen = function () { var opacity = Math.round(Math.pow(this.depth/10000, 40)*100); this.node.css({ left: parent.Radius + this.x - this.node.find("img:first").width()/2 + "px", top: this.y - this.node.find("img:first").height()/2 + "px", zIndex: this.depth }); this.node.find("img:first").css({ opacity: opacity/100 }); } /* /* choose CLOCWISE as the DEFAULT ROTATION in case /* rotate LEFT OR RIGHT are both possible */ //CORE function to move NODE : move node to any angle value this.moveNodeToAngle = function (angle) { var node = $(this.node); //angle_TO_FRONT + offset*i until = |rangeAngle| //with i = rangeAngle/|rangeAngle| this.timer = setInterval(function () { if ( self.angle_TO_FRONT >= 180 ) { self.angle_TO_FRONT -= 360; } if ( self.angle_TO_FRONT <= -180 ) { self.angle_TO_FRONT += 360; } var rangeAngle = Math.abs(angle - self.angle_TO_FRONT); if ( rangeAngle > 180 ) { rangeAngle = -angle + self.angle_TO_FRONT; } else if ( rangeAngle < 180 ) { rangeAngle = angle - self.angle_TO_FRONT; } else { rangeAngle = Math.abs(angle - self.angle_TO_FRONT) } var i = rangeAngle/Math.abs(rangeAngle); if ( self.angle_TO_FRONT == angle ) { parent.timerControl.collectGarbageTimer(); clearInterval(self.timer); } else { if ( Math.abs(angle-self.angle_TO_FRONT) < parent.speed ) { restAngle = Math.abs(angle-self.angle_TO_FRONT); self.angle_TO_FRONT += restAngle*i; } else { self.angle_TO_FRONT += parent.speed*i; } self.calculate(); self.outputToScreen(); } }, 1); parent.timerControl.groupTimer.push(this.timer); } //node event this.node.find("a:first").bind("click", function (evt) { if ( self.angle_TO_FRONT != 0 && self.angle_TO_FRONT != 360 ) { parent.queue.rotateToFront(self); } return false; }); } /* QUEUE */ $Queue = function (/*$NodeList:Array*/) { var self = this; /*field*/ this.queue = typeof(arguments[0]) != "undefined" ? arguments[0] : []; this.head = this.queue.length > 0 ? this.queue[0] : null; /*methods*/ //return the number of items in queue this.length = function (item/*type:$Node*/) { return this.queue.length; } //return the first item out of the queue this.serve = function () { var returnItem = this.queue[0]; this.queue.splice(0,1); return returnItem; } //append item to the end of a queue, no return this.append = function (item/*type:$Node*/) { this.queue.push(item); } //prepend item to the front of a queue, no return this.prepend = function (item/*type:$Node*/) { var swapArr = new Array(); swapArr.push(item); for ( var i = 0 ; i < this.queue ; i++ ) { swapArr.push(this.queue[i]); } this.queue = swapArr; } //refresh angle of node in queue and clear timer control this.refresh = function (callback) { if ( this.head != null ) { /*call external function*/ this.head.node.find("a:first").unbind("click", doExtraWork); /*end.call external function*/ } parent.timerControl.clear(); callback(); } //output the queue's node to browser this.show = function (item/*type:$Node*/) { for ( var i = 0 ; i < this.queue.length ; i++ ) { this.queue[i].outputToScreen(); } } //rotate queue by angle this.rotateToFront = function (item/*type:$Node*/) { if ( parent.timerControl.isClear() && parent.ready ) { this.refresh(function () { self.head = item; var offsetAngle = item.angle_TO_FRONT; for ( var i = 0 ; i < self.queue.length ; i++ ) { var angle = self.queue[i].angle_TO_FRONT - offsetAngle; if ( angle > 180 ) { angle -= 360; } else if ( angle <= -180 ) { angle += 360; } self.queue[i].moveNodeToAngle(angle); } }); } } //rotate queue CW this.rotateCW = function () { if ( parent.timerControl.isClear() && parent.ready ) { this.refresh(function () { var offsetAngle = parent.angleX; for ( var i = 0 ; i < self.queue.length ; i++ ) { var angle = self.queue[i].angle_TO_FRONT + offsetAngle; if ( angle > 180 ) { angle -= 360; } else if ( angle <= -180 ) { angle += 360; } if ( angle == 0 || angle == 360) { self.head = self.queue[i]; } self.queue[i].moveNodeToAngle(angle); } }); } } //rotate queue CCW this.rotateCCW = function () { if ( parent.timerControl.isClear() && parent.ready ) { this.refresh(function () { var offsetAngle = -parent.angleX; for ( var i = 0 ; i < self.queue.length ; i++ ) { var angle = self.queue[i].angle_TO_FRONT + offsetAngle; if ( angle > 180 ) { angle -= 360; } else if ( angle <= -180 ) { angle += 360; } if ( angle == 0 || angle == 360 ) { self.head = self.queue[i]; } self.queue[i].moveNodeToAngle(angle); } }); } } } /* TIMER CONTROL */ $TimerControl = function () { this.groupTimer = new Array(); this.garbageTimer = 0; /*methods*/ //Clear TimerControl this.clear = function () { for ( var i = 0 ; i < this.groupTimer.length ; i++ ) { clearInterval(this.groupTimer[i]); } this.groupTimer = new Array(); this.garbageTimer = 0; } //Collect finished timer this.collectGarbageTimer = function () { this.garbageTimer++; if ( this.isClear() ) { parent.callback(); } } //isClear: all timer have been collected this.isClear = function () { return this.groupTimer.length == this.garbageTimer; } } //Extends function jQuery.fn.extend({ traverse: function (i) { var startItem = this.next(); var endItem = typeof(i) == "undefined" ? null : i; var regExp; var items = new Array(); if (endItem != null) { do { items.push(startItem); startItem = startItem.next(); regExp = startItem.attr("class") != "" ? new RegExp(startItem.attr("class"), "g") : null } while ( regExp == null || !(regExp).test(endItem.attr("class")) ) } else { do { items.push(startItem); startItem = startItem.next() } while ( startItem.html() != null ) } return items; } }); //MAIN this.vanishingPoint = options.vanishingPoint; this.speed = options.speed; this.listItem = jEl; var classes = this.listItem.attr("class").split(" "); for ( var i = 0 ; i < classes.length ; i++ ) { if ( classes[i].match(/Theme_/) ) { var theme = "Carousel3D_" + classes[i]; } else { var theme = "Carousel3D_Default"; } } this.container = $("
"); this.listItem.before(this.container); this.listItem.appendTo(this.container); //Controler this.controller = $( "" ); this.controller.appendTo(this.container); //Loading Container this.loadingContainer = $("