/**
 * prcSlider
 *
 * multirole slider plug-in
 *
 * @category	jQuery plugin
 * @copyright	2010 RaNa design associates, inc.
 * @author		keisuke YAMAMOTO <keisukey@ranadesign.com>
 * @link		http://www.ranadesign.com/
 * @version		2.0
 * @since		Aug 30, 2010
 */

(function($){

	/* for iPhone & iPad ============================= */
	var iphone = /(iPhone)|(iPad)/i.test(navigator.userAgent);


	/* prcSlider ==================================== */
	$.fn.prcSlider = function(param){
		return this.each(function(){

			// 初期設定 ------------------------- //

			// 初期値
			var def = {
				loop: true,			// ループさせるかどうか
				time: 10,			// 1px動かすのにかける時間
				speed: 1,			// 早送りや遅送りの比率指定
				direction: "left",	// 送り方向
				reverse: true,		// コントロール方向を逆向きにするかどうか
				auto: true,			// trueで「自動実行」falseで「手動(コントロール)実行｣
				easing: "linear",	// イージングの初期値
				guideSelector: ".slideGuide",	// ガイド用クラス名
				cellSelector: ".slideCell",	// セル用クラス名
				ctrlSelector: ".slideCtrl",	// コントローラー用クラス名
				ctrlClick: false,	// コントローラ操作用イベントハンドラ(click)
				ctrlHover: true,	// コントローラ操作用イベントハンドラ(hover)
				draggable: false,	// スライドをドラッグで操作するかどうか
				dragCursorOpen: "open.cur",	// ドラッグ可能時のカーソルアイコン
				dragCursorClose: "close.cur", // ドラッグ中のカーソルアイコン
				shuttle: false,		// 往復スライドにするかどうか
				once: false,		// 動きを1コマずつ止めるかどうか
				pause: true,		// マウスオーバー時に一時停止するかどうか
				build: true,		// 暗黙のオプション（幅の初期設定）falseで幅設定をしない
				sp: 1,				// 基本速度
				guide: $(this)
			};
			
			// プライベート用のプロパティの上書き禁止
			if(param){
				delete param.guide;
				delete param.sp;
			}
			
			// パラメータ受け渡し
			$.extend(def, param);

			// draggableかshuttleがtrueの時は関係するオプションをオフにする
			if(def.draggable || def.shuttle) def.loop = def.auto = def.pause = false;

			// 内部処理用プロパティ設定
			def.d = def.direction; // 方向の初期設定を記憶
			def.cell = def.cellSelector;
			def.ctrl = def.ctrlSelector;
			def.curOpen = "url(" + def.dragCursorOpen + "), default";
			def.curClose = "url(" + def.dragCursorClose + "), default";
			def.mousedownX = 0;
			
			// パラメータやセレクターが不正または無効な場合は次へ
			if(!def.guide || def.loop && !def.guide.children(def.cell).length || !def.loop && def.guide.hasClass(def.guideSelector)) return true;
			
			// 幅の初期設定
			if(def.build){
				$(window).resize(init(def)).triggerHandler("resize");
				// init(def);
				// $(window).resize(init(def));
			}
			
			// frame枠の取得
			def.frameBorder = def.guide.offset().left - def.guide.parent().offset().left;
			// cellマージンの取得
			def.margin = def.guide.find(def.cell).eq(0).outerWidth({ margin: true }) - def.guide.find(def.cell).eq(0).outerWidth();

			// iPhone, iPad用
			def.handlerMousedown = iphone ? "touchstart" : "mousedown";
			def.handlerMousemove = iphone ? "touchmove" : "mousemove";
			def.handlerMouseup = iphone ? "touchend" : "mouseup";



			// 実行設定 ------------------------------- //

			// 自動起動
			if(def.auto) slider(def);


			// オプション設定 ------------------------- //

			// マウスオーバーで一時停止する
			if(def.pause){
				def.guide.hover(
					function(){	$(this).find(def.cell).stop(true); },
					function(){ slider(def); }
				);
			}
			
			
			// コントローラ設定 ------------------------- //

			// ホバーイベント
			if(def.ctrlHover){
				// for iPhone & iPad
				if (iphone){
					def.guide.siblings(def.ctrl)
						.bind(def.handlerMousedown, function(event){
							event.preventDefault();
							def.guide.find(def.cell).stop(true);
							def.sp = def.speed;
							def.direction = $(this).hasClass("right") ? "left" : "right";
							slider(def);
						}).bind(def.handlerMouseup, function(){
							def.guide.find(def.cell).stop(true);
							def.sp = 1;
							def.direction = def.d;
							if(def.auto) slider(def);
						});
				} else {
					// 早送り・巻き戻し
					def.guide.siblings(def.ctrl).hover(
						function(){
							def.guide.stop(true).find(def.cell).stop(true);
							def.sp = def.speed;
							def.direction = $(this).hasClass("right") ? def.reverse ? "left" : "right" : def.reverse ? "right" : "left";
							slider(def);
						},
						function(){
							def.guide.stop(true).find(def.cell).stop(true);
							def.sp = 1;
							def.direction = def.d;
							if(def.auto) slider(def);
						}
					);
				}
			}
			
			// クリックイベント
			if(def.ctrlClick){
				def.guide.siblings(def.ctrl).bind(def.handlerMousedown, function(event){
					// アニメーションを省略
					def.guide.find(def.cell).stop(true, true);
					// 初期動作の中止
					event.preventDefault();
					// 方向設定
						def.direction = $(this).hasClass("right") ? def.reverse ? "left" : "right" : def.reverse ? "right" : "left";
					// 1クリックで1コマだけ動くようにする
					def.once = true;
					
					slider(def);
				});
			}

			// ドラッグイベント
			if(def.draggable){
				def.guide.bind(def.handlerMousedown, function(event){
					event.preventDefault();
					def.mousedownX = (iphone ? event.originalEvent.touches[0].pageX : event.pageX) - def.guide.position().left;
					$(this).stop(true).css("cursor", def.curClose);
					$(document).bind(def.handlerMousemove, dragmove);
				});
				$(document).bind(def.handlerMouseup, function(){
					def.guide.css("cursor", def.curOpen);
					$(document).unbind(def.handlerMousemove, dragmove);
				});
			}
			// dragmove設定
			function dragmove(event){
				var frameEnd = def.guide.parent().width() + def.guide.parent().position().left;
				var guideEnd = def.guide.width() + def.guide.position().left;
				var stuff = 100;

				def.guide.css("position", "absolute").css("left", (iphone ? event.originalEvent.touches[0].pageX : event.pageX) - def.mousedownX);

				if(frameEnd - guideEnd > stuff || def.guide.offset().left - def.guide.parent().offset().left > stuff){
					$(document).unbind(def.handlerMousemove, dragmove).one(def.handlerMouseup, function(){
						def.guide.animate({
							left: frameEnd - guideEnd > stuff ? "+=" + (frameEnd - guideEnd) : 0
						}, {
							duration: 500,
							easing: "quart"
						});
					});
				}
			}

			/* slider 定義 ========================== */
			// ループする場合は「先頭セルを左に移動させたあと、最終セルの後ろに移動させる」を繰り返す。
			function slider(par){
				var cell = par.guide.find(def.cell);
				
				if(par.loop){
					cell.first = cell.eq(0);
					cell.last = cell.eq(cell.length - 1);

					if(par.direction.toLowerCase() === "left"){
						cell.first.animate({
							marginLeft: -1 * cell.first.width() - par.margin
						}, {
							// 途中で止まっていた場合は残りを調整してanimateし、次以降は再ループさせる。
							duration: ~~Math.abs(par.time / par.sp * cell.first.width() * 
								(cell.first.offset().left - par.guide.offset().left < 0 ? 
									(cell.first.width() + cell.first.offset().left - par.guide.offset().left) / cell.first.width() : 1
								)
							),
							easing: par.easing,
							complete: function(){
								if(def.loop){
									cell.first.appendTo(par.guide).css("marginLeft", 0);
									if(!par.once) slider(par);
								}
							}
						});
					}else if(par.direction.toLowerCase() === "right"){
						if(cell.first.offset().left - par.guide.offset().left < 0){
							cell.first.animate({
								marginLeft: 0
							},{
								duration: ~~Math.abs(par.time / par.sp * cell.first.width() * 
									(cell.first.offset().left - par.guide.offset().left < 0 ? 
										(par.guide.offset().left - cell.first.offset().left) / cell.first.width() : 1
									)
								),
								easing: par.easing,
								complete: function(){ if(!par.once) slider(par); }
							});
						}else{
							cell.last.prependTo(par.guide).css("marginLeft", -1 * cell.last.width() - par.margin);
							slider(par);
						}
					}else{ return false; }
				
				}else{ // ループしない時
					// 方向の取得
					var d = par.direction.toLowerCase() === "left" ? -1 : 1;
					// コントローラの表示
					var ctrl = par.guide.siblings(par.ctrl);
					ctrl.show();
					// スライド設定
					par.guide.animate({
						marginLeft: par.guide.width() * d
					}, {
						duration: par.time * par.guide.width() / par.sp,
						easing: par.easing
					});
					// 停止設定
					var gl = par.guide.offset().left;
					var fl = par.guide.parent().offset().left;
					var timerId = setInterval(function(){
						if(d > 0 && par.guide.offset().left > par.guide.parent().offset().left + par.frameBorder){
							clearInterval(timerId);
							par.guide.stop(true);
							ctrl.filter(".left").hide();
						}
						if(d < 0 && par.guide.parent().width() + par.guide.parent().offset().left + par.frameBorder + par.margin > par.guide.width() + par.guide.offset().left){
							clearInterval(timerId);
							par.guide.stop(true);
							ctrl.filter(".right").hide();
						}
					}, 1);
				}
			}
			
			/* init 定義 ============================= */
			// スライド枠の幅設定
			function init(def){
				var cell = def.guide.find(def.cell);

				// slideCellの数調整。足りない時にコピーして増やす。
				var max = 0;
				var size = 0;
				// セルの合計幅と最大幅を取得する。
				cell.each(function(){
					max = max > $(this).outerWidth() ? max : $(this).outerWidth();
					size += $(this).outerWidth({ margin: true });
				});
				// ループしない時はセルを増やさない。ガイド幅をセルの合計幅に合わせて終了。
				if(!def.loop){
					def.guide.width(size);
					return false;
				}
				// フレーム枠を超えるまでセルを増やす。
				while(size < def.guide.parent().width() + max){
					cell.clone(true).appendTo(def.guide);
					size += cell.outerWidth();
				}
				// slideGuideのサイズを合計幅＋最大幅にする。
				def.guide.width(size + max);
			}

		});
	}; // prcSlider end


	/* プラグイン外の追加設定 ==================================== */
	$(function(){

		// slideFrameのoverflowをhiddenに
		$(".slideFrame").css("overflow", "hidden");
		
		// slideCtrl設定
		$(".slideCtrl")
			.show()
			.hover(
				function(){ $(this).css("opacity", 0.5); },
				function(){ $(this).css("opacity", 0.8); }
			);
		;

		// ホバーでセルをグレースケール化
		$(".slideCell").hover(
			function(){ $(this).css("opacity", 0.8); },
			function(){ $(this).css("opacity", 1); }
		);
			
	});
	

/* easing ****************************/

/*
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Uses the built in easing capabilities added In jQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - jQuery Easing
 * 
 * Open source under the BSD License. 
 * 
 * Copyright © 2008 George McGinley Smith
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
*/

	$.extend($.easing, {
		easeOutBounce: function (x, t, b, c, d) {
			if ((t/=d) < (1/2.75)) return c*(7.5625*t*t) + b;
			else if (t < (2/2.75)) return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
			else if (t < (2.5/2.75)) return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
			else return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		},
		quart: function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }
	});


// おまけ ////////////////////////////////
//	setInterval(function(){
//		$(".slideCell img").attr("src", "kaeru.gif");
//		if(!Math.floor(Math.random() * 3)) $(".slideCell img")[Math.floor(Math.random() * $(".slideCell img").length)].src = "apology.png";
//	}, 2000);

}(jQuery));
