// JavaScript Document
/*global window:false, console:false, document:false, event:false, jQuery:false */

/***********************************************************************************

author:		Daniel Kazmer - http://iframework.net
created:	24.11.2012
version:	2.1.2

	version history:
		2.1.2	bug fix: text inputs were not selectable by mouse-drag in Chrome for jQuery - a proper if statement in the document's mousemove event listener solved it, thereby possibly increasing performance (applied to both jQuery and standalone) (01.02.2015)
		2.1.1	bug fix: clicking anywhere on bar didn't update value; nor did it update color in follow bar, for which a couple of constraint issues were also fixed (24.01.2015)
		2.1.0	removed snap properties hard & onlyOnDrop in favour of snap.type; also snap.markers became snap.marks; added totalRange property & runtime values thereof returned; destroy method now chainable for jQuery; fixed minor scoping issue; modified colorShift handling; significant changes with regards to data entered and data received; replaced setInterval with event listener (+ IE polyfill); removed drag and drop callbacks from initiator function; added slider data to onload callback; jQuery: removed unnecessary removeEventListeners for IE that caused an error on destroy (16.11.2014)
		2.0.0	major improvements in code structure, stability, accuracy; changed color shift property (see usage); only corresponding arrow keys for horizontal or vertical; added windows phone support; added retina image handling; fixed issues in destroy method; added shift + arrow keys (30.10.2014)
		1.10.0	added keyboard functionality (03.01.2014)
		1.9.1	bug fix: when button is pressed but released off button, button action now gets cleared (19.12.2013)
		1.9.0	added -/+ buttons, along with the onButton and onload callbacks (18.12.2013)
		1.8.8	stability (some distortion resistance); better rebuild on mobile; mobile orientation change support (09.12.2013)
		1.8.7	snap marks now align to snap points; bug fix: vertical now rebuilds properly (03.12.2013)
		1.8.5	mobile ready; added onSnap callback (02.12.2013)
		1.7.1	added real snapping and reworked its options; added "destroy" method - now allows clean rebuild; bug fix: when shell is thinner than knob, knob didn't retain its position in vertical mode (28.11.2013)
		1.5.0	added loadbar capability and "animated" option (27.11.2013)
		1.0.0	added Vertical mode; added option to hide knob (26.11.2013)
		0.3.1	more accurate snap markers; added color shifting (25.07.2013)
		0.2.6	bug fix: constraints when dragging (20.12.2012)
		0.2.5	bug fix: when knob is image, startAt now gets the correct knob width (13.12.2012)
		0.2.0	added disabled state (08.12.2012)
		0.1.0	created

	usage:
		apply the following to an empty DIV with a unique id

		$('#slider').sGlide({
			startAt: 60,			// start slider knob at - default: 0
			image: ''				// string - image path
			retina: true,			// boolean - larger knob image with suffix @2x for retina displays
			width: 200,				// integer - default: 100
			height: 20,				// integer - default: 40
			unit: 'px',				// 'px' or '%' (default)
			pill:					// boolean - default: true
			snap: {
				marks		: false,
				type		: false,
				points		: 0
			},
			disabled:				// boolean - default: false
			colorShift:				// array of 2 css color values
			vertical:				// boolean - default: false
			showKnob:				// boolean - default: true
			buttons:				// boolean - default: false
			drop/drag/onSnap/onButton/onload: function(o){
				console.log('returned object',o);
			}
		});

		all properties are optional, however, to retrieve data, use one of the callbacks

	goals:
		- if unit is %, then markers should be also
		- get color shifting to work with the startAt method (start at correct color)
		- old browsers verticals (IE6/7 - low priority)
		- fix bug: rebuilding vertical rotates again
		- range selector
		- fixes or implementations of these issues: http://stackoverflow.com/search?q=sglide

***********************************************************************************/

(function($){
	var valueObj	= {};
	var helpers		= {
		// 'isMobile'	: false,
		// 'buttons'	: false
	};
	var methods		= {
		destroy: function(){
			this.each(function(i, el){
				var self	= $(el);
				var id		= self.selector;
				var guid	= self.attr('id');

				// unwrap vertical
				var vertContainer = $('#'+guid+'_vert-marks');
				if (vertContainer[0]) self.unwrap();

				// unwrap buttons marks
				else {
					var buttonsContainer = $('#'+guid+'_button-marks');
					if (buttonsContainer[0]) self.unwrap();
				}

				// remove buttons
				if ($('#'+guid+'_plus')[0]) $('#'+guid+'_minus, #'+guid+'_plus').remove();

				var markers = $('#'+guid+'_markers');
				if (markers.length > 0) markers.remove();

				var mEvt = {
					'down'	: 'mousedown',
					'up'	: 'mouseup',
					'move'	: 'mousemove'
				};

				if (helpers[guid+'-isMobile']){
					mEvt.down = 'touchstart'; mEvt.up = 'touchend'; mEvt.move = 'touchmove';
				} else
					$(document).off('keydown.'+guid).off('keyup.'+guid);

				if (helpers[guid+'-buttons']){
					$('#'+guid+'_plus').off(mEvt.down).off(mEvt.up);
					$('#'+guid+'_minus').off(mEvt.down).off(mEvt.up);
				}
				
				$(document).off(mEvt.move+'.'+guid).off(mEvt.up+'.'+guid);
				$(window).off('orientationchange.'+guid);
				self.off(mEvt.down);
				self.children('.slider_knob').off(mEvt.up).off(mEvt.down).remove();
				self.children('.follow_bar').off(mEvt.down).remove();
				self.removeAttr('style');
			});
			return this;
		},
		startAt: function(pct, animated){
			this.each(function(i, el){
				var self		= $(el);
				var knob		= self.children('.slider_knob');
				var follow		= self.children('.follow_bar');
				var guid		= self.attr('id');

				valueObj[guid] = pct;

				// set pixel positions
				var selfWidth = self.width();
				var knobWidth = knob.width();

				// constraints
				if (pct <= 0)			pct = 0;
				else if (pct >= 100)	pct = 100;

				// set pixel positions
				var px = (selfWidth - knobWidth) * pct / 100 + (knobWidth / 2);
				var pxAdjust = px - knobWidth / 2;

				// gui
				if (animated){
					knob.animate({'left': pxAdjust+'px'}, 200);
					follow.animate({'width': px+'px'}, 200);
				} else {
					knob.css('left', pxAdjust+'px');
					follow.css('width', px+'px');
				}

				// color shifting
				if (helpers[guid+'-colorChange'])
					follow[0].children[0].style.opacity = pct / 100;
			});
			return this;
		},
		init: function(options){
			this.each(function(i, el){

				//------------------------------------------------------------------------------------------------------------------------------------
				// build skeleton

				var self	= $(el);
				var guid	= self.attr('id');

				// no id? give one!
				if (!guid){
					guid = 'sglide-'+i;
					self.attr('id', guid);
				}

				// add assets
				self.html('<div class="follow_bar"></div><div class="slider_knob"></div>');

				var knob	= self.children('.slider_knob');
				var follow	= self.children('.follow_bar');

				//------------------------------------------------------------------------------------------------------------------------------------
				// settings & variables

				var settings = $.extend({
					'startAt'		: 0,
					'image'			: 'none',	// full path of image
					'height'		: 40,
					'width'			: 100,
					'unit'			: '%',	// 'px' or '%'
					'pill'			: true,
					'snap'			: {
						'marks'		: false,
						'type'		: false,
						'points'	: 0
					},
					'disabled'		: false,
					'colorShift'	: [],
					'vertical'		: false,
					'showKnob'		: true,
					'buttons'		: false,
					'totalRange'	: [0,0],
					'retina'		: true
				}, options);

				self.removeAttr('style');	// remove user inline styles

				helpers[guid+'-isMobile'] = false;
				var mEvt		= {
						'down'	: 'mousedown',
						'up'	: 'mouseup',
						'move'	: 'mousemove'
					},
					uAgent		= navigator.userAgent;

				if (uAgent.match(/Android/i) ||
					uAgent.match(/webOS/i) ||
					uAgent.match(/iPhone/i) ||
					uAgent.match(/iPad/i) ||
					uAgent.match(/iPod/i) ||
					// uAgent.match(/Windows Phone/i) ||
					uAgent.match(/BlackBerry/i)){
					helpers[guid+'-isMobile'] = true;
					mEvt.down = 'touchstart'; mEvt.up = 'touchend'; mEvt.move = 'touchmove';
					if (window.navigator.msPointerEnabled){
						mEvt.down = 'MSPointerDown'; mEvt.up = 'MSPointerUp'; mEvt.move = 'MSPointerMove';
					}
					var touchX = null, touchY = null;
				} else if (uAgent.match(/Windows Phone/i)){
					if (window.navigator.msPointerEnabled){
						self.css({'-ms-touch-action': 'none'});
						mEvt.down = 'MSPointerDown'; mEvt.up = 'MSPointerUp'; mEvt.move = 'MSPointerMove';
					} else {
						mEvt.down = 'touchstart'; mEvt.up = 'touchend'; mEvt.move = 'touchmove';
					}
				}

				// variables
				var THE_VALUE		= valueObj[guid] = settings.startAt,
					result			= 0,
					vert			= settings.vertical,
					markers			= (settings.snap.points > 0 && settings.snap.points <= 9 && settings.snap.marks),
					snapType		= (settings.snap.type != 'hard' && settings.snap.type != 'soft') ? false : settings.snap.type,
					knob_bg			= '#333',
					knob_width		= (settings.showKnob && !settings.disabled ? '2%' : '0'),
					knob_height		= 'inherit',
					self_height		= Math.round(settings.height)+'px',
					r_corners		= settings.pill,
					imageBln		= (settings.image != 'none' && settings.image !== '' && !settings.disabled),
					resize			= false,
					keyCtrl			= (self.attr('data-keys') == 'true') ? true : false,
					keyCtrlShift	= (self.attr('data-keys') == 'shift') ? true : false,
					colorChangeBln	= (settings.colorShift.length > 1) ? true : false,
					isMobile		= helpers[guid+'-isMobile'],
					customRange		= (settings.totalRange[0] !== 0 || settings.totalRange[1] !== 0) && settings.totalRange[0] < settings.totalRange[1],
					retina			= (window.devicePixelRatio > 1) && settings.retina;

				helpers[guid+'-buttons']		= settings.buttons;
				helpers[guid+'-colorChange']	= colorChangeBln;

				//------------------------------------------------------------------------------------------------------------------------------------
				// image handling

				if (imageBln){	// if image
					img = settings.image;

					// retina handling
					if (retina){
						var rImgTemp = img.split('.');
						var rImgTemp_length = rImgTemp.length;

						rImgTemp[rImgTemp_length-2] = rImgTemp[rImgTemp_length-2] + '@2x';
						img = '';
						for (var i = 0; i < rImgTemp_length; i++){
							img += rImgTemp[i] + ((i < rImgTemp_length-1) ? '.' : '');
						}
					}

					knob.html('<img src="'+img+'" style="visibility:hidden; position:absolute" />');
					// self_height = 'auto';
					knob.children('img').load(function(){
						var imgEl = $(this);

						if (retina){
							imgEl[0].style.width = (imgEl[0].offsetWidth / 2) + 'px';
							// imgEl.style.height = (imgEl.offsetHeight / 2) + 'px';
						}

						// knob.css('width', 'auto');
						var thisHeight = imgEl[0].naturalHeight;
						knob_width = imgEl[0].naturalWidth+'px';
						knob_height = thisHeight+'px';

						knob_bg = 'url('+img+') no-repeat';
						var knob_bg_styles = {
							'width': knob_width,
							'height': knob_height,
							'background': knob_bg
						};
						if (retina) knob_bg_styles['background-size'] = '100%';

						knob.css(knob_bg_styles);
						follow.css({
							'height': knob_height,
							'border-radius': r_corners ? thisHeight / 2 + 'px 0 0 ' + thisHeight / 2 + 'px' : '0'
						});
						self.css({
							'height': knob_height,
							'border-radius': r_corners ? thisHeight / 2 + 'px' : '0'
						});

						imgEl.remove();

						if (thisHeight > settings.height){
							var knobMarginValue = 0;
							knobMarginValue = (thisHeight-settings.height)/2;
							self.css({
								// 'margin-top': knobMarginValue+'px',
								'height': settings.height+'px'
							});
							knob.css({
								'top': '-'+knobMarginValue+'px'
							});
							follow.css({
								'height': settings.height+'px',
								'border-radius': r_corners ? thisHeight / 2 + 'px 0 0 ' + thisHeight / 2 + 'px' : '0'
							});
						} else {
							// children stay inside parent
							self.css('overflow', 'hidden');

							// adjust color shifter height
							follow.find('div').css('height', knob_height);
						}

						$(el).trigger(eventMakeReady);
					});
				} else {
					var d = settings.height / 2;
					self.css({'border-radius': (r_corners ? d+'px' : '0'), 'overflow': 'hidden'});
					follow.css('border-radius', (r_corners ? d+'px 0 0 '+d+'px' : '0'));
					setTimeout(function(){
						$(el).trigger(eventMakeReady);
					}, 0);
				}

				//------------------------------------------------------------------------------------------------------------------------------------
				// styles

				// validate some user settings
				var unit = settings.unit, width = settings.width;
				if (unit != 'px' && unit != '%') unit = '%';
				else if (unit == 'px') width = Math.round(width);
				else if (unit == '%' && Math.round(width) > 100) width = 100;

				self.css({
					'width': width + unit,
					'height': self_height,
					'text-align': 'left',
					'margin': 'auto',
					'cursor': (!settings.disabled ? 'pointer' : 'default'),
					'z-index': '997',
					'position': 'relative',
					'background':'#e2e2e2',
					'-webkit-touch-callout': 'none'
				});

				var self_width = self.width();

				var cssContentBox = {
					'-webkit-box-sizing': 'content-box',
					'-khtml-box-sizing': 'content-box',
					'-moz-box-sizing': 'content-box',
					'-ms-box-sizing': 'content-box',
					'box-sizing': 'content-box'
				}, cssUserSelect = {
					'-webkit-user-select': 'none',
					'-khtml-user-select': 'none',
					'-moz-user-select': 'none',
					'-ms-user-select': 'none',
					'user-select': 'none'
				}, cssRotate = {
					'-webkit-transform': 'rotate(-90deg)',
					'-khtml-transform': 'rotate(-90deg)',
					'-moz-transform': 'rotate(-90deg)',
					'-ms-transform': 'rotate(-90deg)',
					'transform': 'rotate(-90deg)',
					'-webkit-transform-origin': self_width+'px 0',
					'-khtml-transform-origin': self_width+'px 0',
					'-moz-transform-origin': self_width+'px 0',
					'-ms-transform-origin': self_width+'px 0',
					'transform-origin': self_width+'px 0',
					'filter': 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)'
				};

				self.css(cssContentBox).css(cssUserSelect);

				knob.css({
					'width': knob_width,
					'background': knob_bg,
					'height': knob_height,
					'display': (!settings.disabled ? 'inline-block' : 'none'),
					'font-size': '0',
					'position': 'relative',
					'z-index': '1'
				}).css(cssContentBox);

				follow.css({
					'position': (!settings.disabled ? 'absolute' : 'static'),	// static when 'disabled' for self.overflow.hidden to work in Chrome
					'height': knob.height()+'px',
					'width': '0'
				}).css(cssContentBox);

				//------------------------------------------------------------------------------------------------------------------------------------
				// snap marks, buttons, vertical

				// snap to
				var snapping_on = false;
				var snaps = Math.round(settings.snap.points);
				var snapPctValues = [0];
				var drawSnapmarks = function(resize){
					if (snaps === 1) snaps = 2;
				
					// pixels
					var kw = knob.width();
					var w = self_width - kw;
					var increment = w / (snaps - 1);
					var snapValues = [0];
					var step = increment;
					while (step <= w+2){	// added 2px to fix glitch when drawing last mark at 7 or 8 snaps (accounts for decimal)
						snapValues.push(step);
						step += increment;
					}
					// percentage
					increment = 100 / (snaps - 1);
					step = increment;
					while (step <= 101){	// added 1% to fix glitch when drawing last mark at 7 or 8 snaps (accounts for decimal)
						snapPctValues.push(step);
						step += increment;
					}

					snapping_on = true;

					// markers
					if (markers){
						var marks = null;
						if (!resize){
							self.after('<div id="'+guid+'_markers"></div>');
							
							marks = $('#'+guid+'_markers');
							
							marks.css({
								'width': self_width+'px', //settings.width + unit,
								'margin': 'auto',
								'padding-left': (kw/2)+'px',
								'-webkit-touch-callout': 'none',
								'box-sizing': 'border-box'
							}).css(cssUserSelect);
						} else {
							marks = $('#'+guid+'_markers');
							marks.html('');
						}

						var str = '';

						for (var i = 0; i < snapValues.length; i++)
							str += '<div style="display:inline-block; width:0; height:5px; border-left:#333 solid 1px; position:relative; left:'+
								(snapValues[i]-i)+'px; float:left"></div>';

						marks.html(str);
					}
				};

				// -----------

				// vertical
				var verticalTransform = function(){
					if (markers && snaps > 0 && snaps < 10){
						var a = $('#'+guid+', #'+guid+'_markers');

						a.wrapAll('<div id="'+guid+'_vert-marks" style="margin:0; z-index:997; width:'+width+unit+
							'; -webkit-backface-visibility:hidden; -moz-backface-visibility:hidden; -ms-backface-visibility:hidden; backface-visibility:hidden"></div>');

						var vmarks = $('#'+guid+'_vert-marks');

						self.css('width', '100%');
						vmarks.css(cssContentBox).css(cssRotate);

						for (var i = 0; i < a.length; i++)
							a.css('margin', '0');
					} else {
						// check whether even by even or odd by odd to fix blurred elements
						self.css({
							'margin': '0', 'top': '0', 'left': '0',
							'backface-visibility': 'hidden'
						}).css(cssRotate);
					}
				};

				// -----------

				// buttons
				var drawButtons = function(){
					knob_adjust = knob.width() / self_width * 50;

					var vertStyles	= '; z-index:1000; position:relative; top:30px',
						plusStr		= '<div class="sglide-buttons" id="'+guid+'_plus" style="display:inline-block; cursor:pointer'+(vert ? vertStyles : '')+'">&nbsp;+&nbsp;</div>',
						minusStr	= '<div class="sglide-buttons" id="'+guid+'_minus" style="display:inline-block; cursor:pointer'+(vert ? vertStyles : '')+'">&nbsp;&minus;&nbsp;</div>';

					if (markers){
						var q = null;
						if (!vert){
							self.css('width', 'auto');
							var a = (vert) ? $('#'+guid+'_vert-marks') : $('#'+guid+', #'+guid+'_markers');
							a.wrapAll('<div id="'+guid+'_button-marks" style="display:inline-block; vertical-align:middle; width:'+width+unit+'"></div>');
							q = $('#'+guid+'_button-marks');
						} else {
							q = $('#'+guid+'_vert-marks');
						}

						q.after(plusStr);
						q.before(minusStr);
					} else {
						self.css({
							'display': (!vert) ? 'inline-block' : 'block',
							'vertical-align': 'middle'
						});

						self.after(plusStr);
						self.before(minusStr);
					}

					var plusBtn		= $('#'+guid+'_plus'),
						minusBtn	= $('#'+guid+'_minus');

					minusBtn.css(cssUserSelect);
					plusBtn.css(cssUserSelect);

					if (!settings.disabled){
						plusBtn.on(mEvt.down, function(){
							eventPlusMinusMouseDown('>');
						}).on(mEvt.up, btnClearAction);

						minusBtn.on(mEvt.down, function(){
							eventPlusMinusMouseDown('<');
						}).on(mEvt.up, btnClearAction);
					}
				}, btnTriggers = function(direction, smoothBln){
					var knobWidth = knob.width();
					var set_value = THE_VALUE = valueObj[guid];
					if (btn_snap){
						var intvl = 100 / (settings.snap.points - 1);
						var p = intvl;
						for (var i = 0; i < settings.snap.points; i++){
							if (intvl >= THE_VALUE){
								if (direction == '>')	THE_VALUE = (Math.round(intvl) > Math.round(THE_VALUE) ? intvl : intvl+p);
								else					THE_VALUE = intvl-p;
								break;
							} else intvl += p;
						}
					} else {
						if (direction == '>')	THE_VALUE += (smoothBln ? 1 : 10);
						else					THE_VALUE -= (smoothBln ? 1 : 10);
					}

					set_value = THE_VALUE;	// leave THE_VALUE out of visual adjustments

					// constraints
					if ((THE_VALUE+knob_adjust) > 100)	{ THE_VALUE = 100; set_value = 100 /*- knob_adjust*/; }
					else if (THE_VALUE-knob_adjust < 0)	{ THE_VALUE = 0; set_value = 0 /*+ knob_adjust*/; }

					// set pixel positions
					var px = (self_width - knobWidth) * set_value / 100 + (knobWidth / 2);
					var pxAdjust = px - knobWidth / 2;

					// gui
					knob.css('left', pxAdjust+'px');
					follow.css('width', px+'px');
					if (colorChangeBln) colorChange({'percent': set_value});

					// output
					// if (options.onButton) options.onButton({'id':guid, 'value':THE_VALUE, 'el':self});
					if (options.onButton) options.onButton(updateME(getPercent(pxAdjust)));
					else valueObj[guid] = THE_VALUE;
				}, btnHold = function(dir){
					var btnHold_timer = setInterval(function(){
						if (btn_is_down) btnTriggers(dir, (btn_snap ? false : true));
						else clearInterval(btnHold_timer);
					}, (btn_snap ? 201 : 10));
				}, btnClearAction = function(){
					btn_is_down = false;
					clearTimeout(btn_timers);
				}, knob_adjust = 0, btn_is_down = false, btn_timers = null;
				var btn_snap = (settings.snap.points > 0 && settings.snap.points <= 9 && (snapType == 'hard' || snapType == 'soft'));

				//------------------------------------------------------------------------------------------------------------------------------------
				// events

				// knob
				var is_down = false;

				knob.on(mEvt.down, function(){
					is_down = true;
					self.data('state', 'active');
				}).on(mEvt.up, function(){
					is_down = false;
				});

				// snapping
				var storedSnapValue = 's-1';
				var doSnap = function(kind, m){
					if (snaps > 0 && snaps < 10){	// min 1, max 9
						var knobWidth = knob.width(),
							pctFive = self_width * (10-snaps) / 100 - 2;

						// % to px
						var snapPixelValues = [];
						for (var i = 0; i < snapPctValues.length; i++){
							snapPixelValues.push((self_width - knobWidth) * snapPctValues[i] / 100);
							// snapPixelValues.push(snapValues[i] - knobWidth*i);
						}

						// get closest px mark, and set %
						var closest = null, pctVal = 0;
						$.each(snapPixelValues, function(i){
							if (closest === null || Math.abs(this - m) < Math.abs(closest - m)){
								closest = this | 0;
								pctVal = snapPctValues[i];
							}
						});

						// physically snap it
						if (kind == 'drag'){
							if (snapType == 'hard'){
								updateSnap(closest, (closest+knobWidth/2));
								doOnSnap(closest, pctVal);
							} else {
								if (Math.round(Math.abs(closest - m)) < pctFive){
									updateSnap(closest, (closest+knobWidth/2));
									doOnSnap(closest, pctVal);
								} else storedSnapValue = 's-1';
							}
						} else if (kind == 'hard'){
							updateSnap(closest, (closest+knobWidth/2));
							return closest;
						} else {
							updateSnap(closest, (closest+knobWidth/2), true);
							return closest;
						}
					}
				}, doOnSnap = function(a, b){ // callback: onSnap
					if (options.onSnap && 's'+a !== storedSnapValue){
						if (b > 100) b = 100;	// patch
						storedSnapValue = 's'+a;
						options.onSnap(updateME(getPercent(a)));
					}
				}, updateSnap = function(knobPos, followPos, animateBln){
					if (!animateBln){
						knob.css('left', knobPos+'px');
						follow.css('width', followPos+'px');
					} else {
						knob.animate({'left': knobPos+'px'}, 'fast');
						follow.animate({'width': followPos+'px'}, 'fast');
					}
				};

				var eventWindowResize = function(){
					self.sGlide('startAt', valueObj[guid]);
					if (markers) drawSnapmarks(true);
				};

				if (!isMobile && (keyCtrl || keyCtrlShift)){
					var keycode, keydown = false,
						codeBack	= (vert) ? 40 : 37,
						codeFwd		= (vert) ? 38 : 39;

					$(document).on('keydown.'+guid, function(e){
						if (!keydown && !settings.disabled){
							if (window.event){
								keycode = window.event.keyCode;
								if (keyCtrlShift && !window.event.shiftKey) return false;
							} else if (e){
								keycode = e.which;
								if (keyCtrlShift && !e.shiftKey) return false;
							}

							if (keycode == codeBack){
								eventPlusMinusMouseDown('<');
								keydown = true;
							} else if (keycode == codeFwd){
								eventPlusMinusMouseDown('>');
								keydown = true;
							}
						}
					}).on('keyup.'+guid, function(){
						keydown = false;
						btnClearAction();
					});
				}

				// button and arrow key events
				var eventPlusMinusMouseDown = function(dir){
					btn_is_down = true;
					btnTriggers(dir);
					btn_timers = setTimeout(function(){
						btnHold(dir);
					}, 500);
				};

				if (isMobile){
					$(document).on(mEvt.down+'.'+guid, function(e){
						// is_down = false;
						touchX = e.originalEvent.touches[0].pageX;
						touchY = e.originalEvent.touches[0].pageY;
					});
				}
				if (isMobile || uAgent.match(/Windows Phone/i)){
					// orientation
					$(window).on('orientationchange.'+guid, eventWindowResize);
				}

				$(document).on(mEvt.move+'.'+guid, function(e){
					if (is_down){
						e = e || event;	// ie fix

						// e.preventDefault();
						// e.stopPropagation();

						var x			= null,
							knobWidth	= knob.width();

						if (vert){
							var base = self.position().top + self_width;
							if (isMobile){
								touchY = e.originalEvent.touches[0].pageY;
								x = base - touchY;
							} else x = base - e.pageY;
						} else {
							if (isMobile){
								touchX = e.originalEvent.touches[0].pageX;
								x = touchX - self.offset().left;
							} else x = e.pageX - self.offset().left;
						}

						var stopper	= knobWidth / 2,
							m		= x - stopper;

						// if(event.preventDefault) event.preventDefault();
						if (e.returnValue) e.returnValue = false;

						// constraint & position
						if (x <= stopper){
							knob.css('left', '0');
							follow.css('width', stopper+'px');
						} else if (x >= self_width-stopper){
							knob.css('left', (self_width-knobWidth)+'px');
							follow.css('width', (self_width-stopper)+'px');
						} else {
							knob.css('left', (x-stopper)+'px');
							follow.css('width', x+'px');
							// if (!settings.snap.onlyOnDrop) doSnap('drag', m);
							if (!snapType || snapType == 'hard') doSnap('drag', m);
						}

						result = knob[0].style.left;	//knob.css('left');
						result = result.replace('px', '');

						var state = self.data('state');

						// update values
						if (options.drag && state == 'active')
							options.drag(updateME(getPercent(result)));

						// color change
						if (colorChangeBln && state == 'active')
							colorChange(getPercent(result));
					}
				}).on(mEvt.up+'.'+guid, function(e){
					var state = self.data('state');
					is_down = false;
					if (state == 'active'){
						e = e || event;	// ie fix
						var x = null, base = 0;

						if (vert){
							base = self.position().top + self_width;
							x = base - ((!isMobile ? e.pageY : touchY)-2);
						} else x = (!isMobile ? e.pageX : touchX) - self.offset().left;
						
						var knobWidth	= knob.width(),
							stopper		= knobWidth / 2,
							m			= x - stopper;	// true position of knob

						// snap to
						if (snaps > 0 && snaps < 10 && (snapType == 'soft' || snapType == 'hard'))	// min 1, max 9
							result = doSnap((snapType == 'hard') ? 'hard' : 'soft', m);
						else
							result = (m < 0 ? 0 : m);

						if (options.drop) options.drop(updateME(getPercent(result)));
						if (options.drag && state == 'active') options.drag(updateME(getPercent(result)));
						self.data('state', 'inactive');

						// color change
						if (colorChangeBln) colorChange(getPercent(result));
					}

					// if button pressed but released off button, clear button action
					if (btn_is_down) btnClearAction();
				});

				//------------------------------------------------------------------------------------------------------------------------------------
				// functions

				if (customRange){
					var cstmStart = settings.totalRange[0];
					var diff = settings.totalRange[1] - cstmStart;
				}
				var sendData = {};
				var getPercent = function(o){
					var cstm = 0;
					o = parseFloat(o, 10);

					// calculate percentage
					var pct = o / (self[0].offsetWidth - knob[0].offsetWidth) * 100;
					pct = Math.min(pct, 100);

					// calculate unit
					if (customRange) cstm = diff * pct / 100 + cstmStart;

					THE_VALUE = valueObj[guid] = pct;

					// set data to send
					sendData.percent = pct;
					if (customRange) sendData.custom = cstm;

					return sendData;
				};

				var updateME = function(o){
					o.id = guid;
					o.el = self;
					return o;
				};

				// color change
				var colorShiftInit = function(){
					var selfHeightHalf = self.offsetHeight / 2;
					var borderRadius = 'border-radius: '+(r_corners ? selfHeightHalf + 'px 0 0 ' + selfHeightHalf + 'px' : '0');
					follow.css({
						'overflow': 'hidden',
						'background-color': settings.colorShift[0]
					});

					follow.html('<div style="opacity:'+(settings.startAt/100)+'; height:100%; background-color:'+settings.colorShift[1]+'; "></div>');
				};
				var colorChange = function(o){
					// follow.find('div').css('opacity', ''+(o.percent/100));
					follow[0].children[0].style.opacity = o.percent / 100;
				};

				// bar
				var eventBarMouseDown = function(e){
					e = e || event;	// ie fix
					e.stopPropagation();
					e.preventDefault();
					if (e.returnValue) e.returnValue = false;	// wp

					var kw = knob.width(),
						sw = self.width();

					is_down = true;
					self.data('state', 'active');

					if (!isMobile && snapType !== 'hard'){
						var x = null;
						if (vert){
							var base = self.position().top + sw;
							x = base - (e.pageY-2);
						} else x = e.pageX - self.offset().left;
						var m = x - (kw / 2);	// true position of knob

						// constraint
						if (m < 0){
							m = 0;
							knob.css('left', '0');
						} else if (m >= sw-kw){
							m = sw-kw;
							knob.css('left', sw-kw+'px');
						}

						knob.css('left', m+'px');
						follow.css('width', m+(kw/2)+'px');
						

						// color change
						if (colorChangeBln) colorChange(getPercent(m));
					}
				};

				if (!settings.disabled){
					self.on(mEvt.down, eventBarMouseDown);
					follow.on(mEvt.down, eventBarMouseDown);
				}

				//------------------------------------------------------------------------------------------------------------------------------------
				// start

				var setStartAt = function(e){
					var num = valueObj[guid];

					var rlt = updateME({'percent':num});

					// inits
					if (snaps > 0 && snaps < 10)	drawSnapmarks();
					if (vert)						verticalTransform();
					if (helpers[guid+'-buttons'])	drawButtons();
					if (colorChangeBln)				colorShiftInit();
					if (options.onload)				options.onload(rlt);

					self.sGlide('startAt', num);

					$(el).off('makeready.'+guid, setStartAt);
				};

				// Listen for image loaded
				var eventMakeReady = $.Event('makeready.'+guid);
				$(el).on('makeready.'+guid, setStartAt);
			});
		}
	};
	$.fn.sGlide = function(params){
		// Method calling logic
		if (methods[params]){
			return methods[params].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof params === 'object' || !params){
			return methods.init.apply(this, arguments);
		} else {
			$.error('Method '+params+' does not exist on jQuery.sGlide');
		}
	};
})(jQuery);