File: JS_toolbucket/freqdec-fd-slider-01084d3/js/fd-slider_SM-enhanced.js

Recommend this page to a friend!
  Classes of Joseph   JS Master Color Picker script   JS_toolbucket/freqdec-fd-slider-01084d3/js/fd-slider_SM-enhanced.js   Download  
File: JS_toolbucket/freqdec-fd-slider-01084d3/js/fd-slider_SM-enhanced.js
Role: Auxiliary script
Content type: text/plain
Description: support for Internet Exploder 9
Class: JS Master Color Picker script
Pick colors from a palette
Author: By
Last change:
Date: 11 years ago
Size: 59,220 bytes
 

Contents

Class file image Download
/*! Unobtrusive Slider Control / HTML5 Input Range polyfill - MIT/GPL2 @freqdec */ /* with modifications by Joe Golembieski, SoftMoon-WebWare.com to: */ /* 1. fix what appears to be a bug based on the infamous "hasLayout" bug. */ /* 2. enhance the fdSlider to be used with CSS3 "transform: rotate();" */ /* 3. be more friendly with events: */ /* • trigger all standard mouse events on the original input: */ /* onMouseOver, onMouseMove, onMouseOut, onMouseDown, onMouseUp, onClick */ /* • trigger events in both DOM systems if they exist: */ /* standard DOM2 and old MSIE */ /* 4. modified auto-finding behavior to target ONLY <input type="range" /> */ /* whereas before inputs with min and max were targeted. This allows */ /* <input type="number" /> to work alongside. */ /* 5. fixed what appears to be a typo in the pixelsToPercent function */ // character encoding: UTF-8 UNIX, tab-spacing: 2, word-wrap: no var fdSlider = (function() { var sliders = {}, uniqueid = 0, mouseWheelEnabled = true, fullARIA = true, describedBy = "fd-slider-describedby", varSetRules = { onfocus:true, onvalue:true }, noRangeBar = false, html5Animation = "jump", isOpera = Object.prototype.toString.call(window.opera) === "[object Opera]", fpRegExp = /^([-]{0,1}[0-9]+(\.[0-9]+){0,1})$/, stepRegExp = /^([0-9]+(\.[0-9]+){0,1})$/; //************ added by SoftMoon-WebWare ****************\\ //I'm finding offsetWidths of 0 with some layouts I'm using in IE9 (and I think down to 6) ...yet another "hasLayout" bug? //This problem causes the fdSlider to be inoperable. //Same prob with FireFox, methinks. //And at times it seems RANDOM, sliders don't work, reset-page, they work, reset page, they don't! // // ! ! So all fdSlider code that uses .offsetWidth or .offsetHeight has been modified to use these functions instead. // //**BUT** this means that some layouts will REQUIRE an absolute (numerical) value given by CSS (or whatever) for width/height, // or getComputedStyle(element).width;height returns "auto" and parseInt("auto")=NaN and we're back where we began. //No guarantees; this solved all of my problems; hopefully it proves to be robust. if (window.getComputedStyle) { var getWidth = function(element) { return element.offsetWidth || parseInt(getComputedStyle(element).width); }; var getHeight = function(element) { return element.offsetHeight || parseInt(getComputedStyle(element).height); }; } else { //essentially no fix for complex layouts on older browsers var getWidth = function(element) {return element.offsetWidth;}; var getHeight = function(element) {return element.offsetHeight;}; } //******************************************************/ // moved from the “locate” function deep below to become its own function by SoftMoon-WebWare: var getDocPos = function(element) { var pos={left: 0, top: 0}; // Try catch for IE's benefit try { while (element.offsetParent) { pos.left += element.offsetLeft; pos.top += element.offsetTop; element = element.offsetParent; }; } catch(err) {}; return pos; } //******************************************************/ var parseJSON = function(str) { // Check we have a String if(typeof str !== 'string' || str == "") { return {}; }; try { // Does a JSON (native or not) Object exist if(typeof JSON === "object" && JSON.parse) { return window.JSON.parse(str); // Genious code taken from: http://kentbrewster.com/badges/ } else if(/mousewheelenabled|fullaria|describedby|norangebar|html5animation|varsetrules/.test(str.toLowerCase())) { var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,', 'Array,String,Math,RegExp,Image,ActiveXObject;', 'return (' , str.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function-') , ');'].join('')); return f(); }; } catch (e) { }; return {"err":"Could not parse the JSON object"}; }; var affectJSON = function(json) { if(typeof json !== "object") { return; }; for(key in json) { value = json[key]; switch(key.toLowerCase()) { case "mousewheelenabled": mouseWheelEnabled = !!value; break; case "fullaria": fullARIA = !!value; break; case "describedby": describedBy = String(value); break; case "norangebar": noRangeBar = !!value; break; case "html5animation": html5Animation = String(value).search(/^(jump|tween|timed)$/i) != -1 ? String(value).toLowerCase() : "jump"; break; case "varsetrules": if("onfocus" in value) { varSetRules.onfocus = !!value.onfocus; }; if("onvalue" in value) { varSetRules.onvalue = !!value.onvalue; }; break; }; }; }; // Classic event functions var addEvent = function(obj, type, fn) { if( obj.attachEvent ) { obj.attachEvent( "on"+type, fn ); } else { obj.addEventListener( type, fn, true ); } }; /**********modified by SoftMoon-WebWare: Newer MSIE has both the old MSIE event interface, and DOM2 standard. */ var removeEvent = function(obj, type, fn) { try {obj.detachEvent( "on"+type, fn );} catch(err) {}; try {obj.removeEventListener( type, fn, true );} catch(err) {}; }; var stopEvent = function(e) { e = e || window.event; if(e.stopPropagation) { e.stopPropagation(); e.preventDefault(); }; /*@cc_on@*/ /*@if(@_win32) e.cancelBubble = true; e.returnValue = false; /*@end@*/ return false; }; var preventDefault = function(e) { e = e || window.event; if(e.preventDefault) { e.preventDefault(); return; }; e.returnValue = false; }; // Add/Remove classname utility functions var addClass = function(e,c) { if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) { return; }; e.className += ( e.className ? " " : "" ) + c; }; var removeClass = function(e,c) { //this was causing a stack overflow! with MSIE9 and my complex page. rewritten by SoftMoon-WebWare // e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, ''); if (!c) e.className=""; else { e.className=e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " "); e.className=e.className.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); } }; // Returns an Object of key value pairs indicating which sliders have values // that have been "set" by the user var getValueSet = function() { var obj = {}; for(id in sliders) { obj[id] = sliders[id].getValueSet(); }; return obj; }; // Sets the valueSet variable for a specific slider var setValueSet = function(sliderId, tf) { sliders[sliderId].setValueSet(!!tf); }; // Does the slider exist in memory var sliderExists = function(slider) { return !!(slider in sliders && sliders.hasOwnProperty(slider)); }; // Javascript instantiation of a slider (input type="text" or select list) var createSlider = function(options) { if(!options || !options.inp || !options.inp.tagName || options.inp.tagName.search(/^input|select/i) == -1) { return false; }; options.html5Shim = false; if(options.inp.tagName.toLowerCase() == "select") { if(options.inp.options.length < 2) { return false; }; options.min = 0; options.max = options.inp.options.length - 1; options.step = 1; options.precision = 0; options.scale = false; options.forceValue = true; } else { if(String(options.inp.type).search(/^text$/i) == -1) { return false; }; options.min = options.min && String(options.min).search(fpRegExp) != -1 ? +options.min : 0; options.max = options.max && String(options.max).search(fpRegExp) != -1 ? +options.max : 100; options.step = options.step && String(options.step).search(stepRegExp) != -1 ? options.step : 1; options.precision = options.precision && String(options.precision).search(/^[0-9]+$/) != -1 ? options.precision : (String(options.step).search(/\.([0-9]+)$/) != -1 ? String(options.step).match(/\.([0-9]+)$/)[1].length : 0); options.scale = options.scale || false; options.forceValue = ("forceValue" in options) ? !!options.forceValue : false; }; options.maxStep = options.maxStep && String(options.maxStep).search(stepRegExp) != -1 ? +options.maxStep : +options.step * 2; options.classNames = options.classNames || ""; options.callbacks = options.callbacks || false; destroySingleSlider(options.inp.id); sliders[options.inp.id] = new fdRange(options); return true; }; var getAttribute = function(elem, att) { return elem.getAttribute(att) || ""; }; // HTML5 input type="range" shim - called onload or onDomReady var init = function() { var inputs = document.getElementsByTagName("input"), options; for(var i = 0, inp; inp = inputs[i]; i++) { if(inp.tagName.toLowerCase() == "input" && /* inp.type.toLowerCase() == "text" *///modified by SoftMoon-WebWare inp.getAttribute('type').toLowerCase() == "range" && (getAttribute(inp, "min") && getAttribute(inp, "min").search(fpRegExp) != -1 || getAttribute(inp, "max") && getAttribute(inp, "max").search(fpRegExp) != -1 || getAttribute(inp, "step") && getAttribute(inp, "step").search(/^(any|([0-9]+(\.[0-9]+){0,1}))$/i) != -1 )) { // Skip elements that have already been created are are resident in the DOM if(inp.id && document.getElementById("fd-slider-"+inp.id)) { continue; // Destroy elements that have already been created but not resident in the DOM } else if(inp.id && !document.getElementById("fd-slider-"+inp.id)) { destroySingleSlider(inp.id); }; // Create an id for the form element if necessary if(!inp.id) { inp.id = "fd-slider-form-elem-" + uniqueid++; }; // Basic option Object options = { inp: inp, callbacks: [], animation: html5Animation, vertical: getAttribute(inp, "data-fd-slider-vertical") ? true : !!(getHeight(inp) > getWidth(inp)), classNames: getAttribute(inp, "data-fd-slider-vertical"), html5Shim: true }; if(options.vertical && !getAttribute(inp, "data-fd-slider-vertical")) { options.inpHeight = getHeight(inp); }; //NOTE properties added by SoftMoon-WebWare: Element has been rotated (using CSS -ms-transform: rotate();) var transformer, transformer_rotate=0, fmod=function(n,m) {return n-Math.floor(n/m)*m;}; if (true //navigator && navigator.userAgent.match( /MSIE/i ) && parseInt(navigator.appVersion)===9 && inp.hasAttribute("data-fd-slider-transformer") && (transformer=document.getElementById(inp.getAttribute("data-fd-slider-transformer"))) && transformer.hasAttribute("data-fd-slider-rotate") ) transformer_rotate=parseFloat(getAttribute(transformer, "data-fd-slider-rotate")) || 0; //value passed should match the degrees value used in the CSS file. Wish we could simply use getComputedStyle().msTransform…the calculus genius might though. options.rotate = (options.vertical ? 90 : 0) + transformer_rotate; options.rotate = (fmod(options.rotate, 360)/360)*Math.PI*2; options.transformer=transformer; options.min = getAttribute(inp, "min") || 0; options.max = getAttribute(inp, "max") || 100; options.step = getAttribute(inp, "step").search(/^any$/i) != -1 ? options.max - options.min : getAttribute(inp, "step").search(stepRegExp) != -1 ? inp.getAttribute("step") : 1; options.precision = String(options.step).search(/\.([0-9]+)$/) != -1 ? String(options.step).match(/\.([0-9]+)$/)[1].length : 0; options.maxStep = options.step * 2; destroySingleSlider(options.inp.id); sliders[options.inp.id] = new fdRange(options); }; }; return true; }; var destroySingleSlider = function(id) { if(id in sliders && sliders.hasOwnProperty(id)) { sliders[id].destroy(); delete sliders[id]; return true; }; return false; }; var destroyAllsliders = function(e) { for(slider in sliders) { if(sliders.hasOwnProperty(slider)) { sliders[slider].destroy(); }; }; sliders = []; }; var unload = function(e) { destroyAllsliders(); sliders = null; }; var resize = function(e) { for(slider in sliders) { if(sliders.hasOwnProperty(slider)) { sliders[slider].onResize(); }; }; }; var onDomReady = function() { removeEvent(window, "load", init); init(); }; var removeOnLoadEvent = function() { removeEvent(window, "load", init); }; function fdRange(options) { var inp = options.inp, disabled = false, tagName = inp.tagName.toLowerCase(), min = +options.min, max = +options.max, rMin = +options.min, rMax = +options.max, range = Math.abs(max - min), step = tagName == "select" ? 1 : +options.step, maxStep = options.maxStep ? +options.maxStep : step * 2, precision = options.precision || 0, steps = Math.ceil(range / step), scale = options.scale || false, hideInput = !!options.hideInput, animation = options.animation || "", vertical = !!options.vertical, rotate = options.rotate, //added by SoftMoon-WebWare value in radians, 0 @ 3:00 progressing counter-clockwise transformer = options.transformer, //added by SoftMoon-WebWare - this may be modified after the fd-slider is actually created (below) horizontal = (rotate<=Math.PI/4 || (rotate>=Math.PI*3/4 && rotate<=Math.PI*5/4) || rotate>=Math.PI*7/4), //added by SoftMoon-WebWare callbacks = options.callbacks || {}, classNames = options.classNames || "", html5Shim = !!options.html5Shim, defaultVal = max < min ? min : min + ((max - min) / 2), resetDef = tagName == "select" ? inp.selectedIndex : inp.defaultValue || defaultVal, forceValue = html5Shim || !!options.forceValue, inpHeight = html5Shim && vertical && ("inpHeight" in options) ? options.inpHeight : false, timer = null, kbEnabled = true, initialVal = tagName == "select" ? inp.selectedIndex : inp.value, sliderH = 0, sliderW = 0, tweenX = 0, tweenB = 0, tweenC = 0, tweenD = 0, frame = 0, // modified by SoftMoon-WebWare sliderBar = {x: 0, y: 0}, // x = 0, // y = 0, rMaxPx = 0, rMinPx = 0, handlePos = 0, destPos = 0, mousePos = {x: 0, y: 0}, // mousePos = 0, stepPx = 0, userSet = false, touchEvents = false, outerWrapper, wrapper, handle, rangeBar, bar; // For the reset event to work we have set a defaultValue if(tagName == "input" && forceValue && !inp.defaultValue) { inp.defaultValue = getWorkingValueFromInput(); }; // Make sure we have a negative step if the max < min if(max < min) { step = -Math.abs(step); maxStep = -Math.abs(maxStep); }; // Add the 100% scale mark if needs be if(scale) { scale[100] = max; }; // Set the "userSet" variable programmatically for this slider function valueSet(tf) { tf = !!tf; if(tf != userSet) { userSet = tf; valueToPixels(getWorkingValueFromInput()); }; }; function disableSlider(noCallback) { if(disabled && !noCallback) { return; }; try { setTabIndex(handle, -1); removeEvent(handle, "focus", onFocus); removeEvent(handle, "blur", onBlur); if(!isOpera) { removeEvent(handle, "keydown", onKeyDown); removeEvent(handle, "keypress", onKeyPress); } else { removeEvent(handle, "keypress", onKeyDown); }; removeEvent(outerWrapper, "mouseover", onMouseOver); removeEvent(outerWrapper, "mouseout", onMouseOut); removeEvent(outerWrapper, "mousedown", onMouseDown); //NOTE mouseup and click added removeEvent(outerWrapper, "mouseup", onMouseUp); removeEvent(outerWrapper, "click", onClick); removeEvent(outerWrapper, "mousemove", onMouseMove); removeEvent(outerWrapper, "touchstart", onMouseDown); if(mouseWheelEnabled) { if (window.addEventListener && !window.devicePixelRatio) window.removeEventListener('DOMMouseScroll', trackMouseWheel, false); else { removeEvent(document, "mousewheel", trackMouseWheel); removeEvent(window, "mousewheel", trackMouseWheel); }; }; } catch(err) {}; removeClass(outerWrapper, "fd-slider-focused"); removeClass(outerWrapper, "fd-slider-active"); addClass(outerWrapper, "fd-slider-disabled"); outerWrapper.setAttribute("aria-disabled", true); inp.disabled = disabled = true; clearTimeout(timer); if(!noCallback) { callback("disable"); }; }; function enableSlider(noCallback) { if(!disabled && !noCallback) { return; }; setTabIndex(handle, 0); addEvent(handle, "focus", onFocus); addEvent(handle, "blur", onBlur); if(!isOpera) { addEvent(handle, "keydown", onKeyDown); addEvent(handle, "keypress", onKeyPress); } else { addEvent(handle, "keypress", onKeyDown); }; addEvent(outerWrapper, "touchstart", onMouseDown); addEvent(outerWrapper, "mousedown", onMouseDown); //NOTE mouseup and click added addEvent(outerWrapper, "mouseup", onMouseUp); addEvent(outerWrapper, "click", onClick); addEvent(outerWrapper, "mousemove", onMouseMove); addEvent(outerWrapper, "mouseover", onMouseOver); addEvent(outerWrapper, "mouseout", onMouseOut); removeClass(outerWrapper, "fd-slider-disabled"); outerWrapper.setAttribute("aria-disabled", false); inp.disabled = disabled = touchEvents = false; if(!noCallback) { callback("enable"); }; }; // Destroys a slider function destroySlider() { // Clear any timeouts clearTimeout(timer); // Remove pointers to DOM nodes wrapper = bar = handle = outerWrapper = timer = null; // Call the "destroy" callback callback("destroy"); // Delete the callback functions callbacks = null; }; // Calculates the pixel increment etc function redraw() { locate(); // Internet Explorer requires the try catch as hidden // elements throw errors try { var sW = getWidth(outerWrapper), sH = getHeight(outerWrapper), hW = getWidth(handle), hH = getHeight(handle), bH = getHeight(bar), bW = getWidth(bar), mPx = vertical ? sH - hH : sW - hW; stepPx = mPx / steps; rMinPx = Math.max(scale ? percentToPixels(valueToPercent(rMin)) : Math.abs((rMin - min) / step) * stepPx, 0); rMaxPx = Math.min(scale ? percentToPixels(valueToPercent(rMax)) : Math.abs((rMax - min) / step) * stepPx, Math.floor(vertical ? sH - hH : sW - hW)); sliderW = sW; sliderH = sH; // Use the input value valueToPixels(forceValue ? getWorkingValueFromInput() : (tagName == "select" ? inp.selectedIndex : parseFloat(inp.value))); } catch(err) {}; callback("redraw"); }; // Calls a callback function /*****modified by SoftMoon-WebWare to pass mouse-events. */ function callback(type) { if(!html5Shim) { if(callbacks.hasOwnProperty(type)) { var cbObj = {"disabled":disabled, "elem":inp, "value":tagName == "select" ? inp.options[inp.selectedIndex].value : inp.value}; // Call all functions in sequence for(var i = 0, func; func = callbacks[type][i]; i++) { func.call(inp, cbObj); }; }; /**********modified by SoftMoon-WebWare: Newer MSIE has both old MSIE event interfaces, and DOM2 standard. */ /* If you use DOM2 to attach events, old MSIE fireEvent will not trigger the DOM2 event. */ /* So to cover the rear of all developers, instead of using either the DOM2 satandard or old MSIE, */ /* the “if-else-if” construct that only used one or the other, */ /* has been replaced with an “if, if” construct that uses BOTH .createEvent AND .createEventObject */ /* Also, we now allow mouse events to be triggered */ } else if(type.match(/^(blur|focus|change|mouse|click)/i)) { //was: (/^(blur|focus|change)$/i)) { if(typeof(document.createEvent) != 'undefined') { e = (type.match(/mouse|click/)) ? document.createEvent('MouseEvents') : document.createEvent('HTMLEvents'); e.initEvent(type, true, true); inp.dispatchEvent(e); } if(typeof(document.createEventObject) != 'undefined') { try { if (e === undefined) e = document.createEventObject(); inp.fireEvent('on' + type.toLowerCase(), e); } catch(err){ }; }; }; }; // FOCUS & BLUR events function onFocus(e) { addClass(outerWrapper, 'fd-slider-focused'); // Is the value said to have been set by the user onfocus if(varSetRules.onfocus) { userSet = true; valueToPixels(getWorkingValueFromInput()); }; // If mousewheel events required then add them if(mouseWheelEnabled) { addEvent(window, 'DOMMouseScroll', trackMouseWheel); addEvent(document, 'mousewheel', trackMouseWheel); if(!isOpera) { addEvent(window, 'mousewheel', trackMouseWheel); }; }; // Callback... callback("focus"); return true; }; function onBlur(e) { removeClass(outerWrapper, 'fd-slider-focused'); // Remove mousewheel events if necessary if(mouseWheelEnabled) { removeEvent(document, 'mousewheel', trackMouseWheel); removeEvent(window, 'DOMMouseScroll', trackMouseWheel); if(!isOpera) { removeEvent(window, 'mousewheel', trackMouseWheel); }; }; kbEnabled = true; // Callback... callback("blur"); }; // MOUSEWHEEL events function trackMouseWheel(e) { if(!kbEnabled) { return; }; e = e || window.event; var delta = 0, value; if (e.wheelDelta) { delta = e.wheelDelta/120; // Older versions of Opera require a small hack to inverse the delta if (isOpera && window.opera.version() < 9.2) { delta = -delta; }; } else if(e.detail) { delta = -e.detail/3; }; if(vertical) { delta = -delta; }; if(delta) { value = getWorkingValueFromInput(); value += (delta < 0) ? -step : step; userSet = true; valueToPixels(getValidValue(value)); }; return stopEvent(e); }; // KEYBOARD events function onKeyPress(e) { e = e || window.event; // Let all non-hijacked keyboard events pass if((e.keyCode >= 33 && e.keyCode <= 40) || !kbEnabled || e.keyCode == 45 || e.keyCode == 46) { return stopEvent(e); }; return true; }; function onKeyDown(e) { if(!kbEnabled) { return true; }; e = e || window.event; var kc = e.keyCode != null ? e.keyCode : e.charCode, value; if(kc < 33 || (kc > 40 && (kc != 45 && kc != 46))) { return true; }; value = getWorkingValueFromInput(); if( kc == 37 || kc == 40 || kc == 46 || kc == 34) { // left, down, ins, page down value -= (e.ctrlKey || kc == 34 ? +maxStep : +step); } else if( kc == 39 || kc == 38 || kc == 45 || kc == 33) { // right, up, del, page up value += (e.ctrlKey || kc == 33 ? +maxStep : +step); } else if( kc == 35 ) { // max value = rMax; } else if( kc == 36 ) { // min value = rMin; }; userSet = true; valueToPixels(getValidValue(value)); callback("update"); // Opera doesn't let us cancel key events so the up/down arrows and home/end buttons will scroll the screen - which sucks preventDefault(e); }; // MOUSE & TOUCH events // Mouseover the slider function onMouseOver(e) { //NOTE: added callback callback('mouseover'); addClass(outerWrapper, 'fd-slider-hover'); }; // Mouseout of the slider function onMouseOut(e) { // Should really check we are not still in the slider //NOTE: added callback callback('mouseout'); removeClass(outerWrapper, 'fd-slider-hover'); }; // Mousedown on the slider function onMouseDown(e) { e = e || window.event; // Stop page scrolling preventDefault(e); // Grab the event target var targ; if (e.target) { targ = e.target; } else if (e.srcElement) { targ = e.srcElement; }; if(targ && targ.nodeType == 3) { targ = targ.parentNode; }; // Are we using touchEvents if(e.touches) { // Skip gestures if(e.targetTouches && e.targetTouches.length != 1) { return false; }; e = e.touches[0]; touchEvents = true; }; // Stop any animation timers clearTimeout(timer); timer = null; // Not keyboard enabled kbEnabled = false; // User has set a value userSet = true; // Handle mousedown - initiate drag if(targ.className.search("fd-slider-handle") != -1) { //NOTE modified by SoftMoon-WebWare mousePos = {y: e.clientY, x: e.clientX}; // (vertical) ? e.clientY : e.clientX; handlePos = parseInt(vertical ? handle.offsetTop : handle.offsetLeft)||0; // Set a value on first click even if no movement trackMouse(e); if(!touchEvents) { addEvent(document, 'mousemove', trackMouse); addEvent(document, 'mouseup', stopDrag); } else { addEvent(document, 'touchmove', trackMouse); addEvent(document, 'touchend', stopDrag); // Remove mouseEvents to stop them firing after the touch event removeEvent(outerWrapper, "mousedown", onMouseDown); }; addClass(outerWrapper, 'fd-slider-active'); // addClass(document.body, "fd-slider-drag-" + (vertical ? "vertical" : "horizontal")); addClass(document.body, "fd-slider-drag-" + (horizontal ? "horizontal" : "vertical")); callback("dragstart"); // Wrapper mousedown - initiate animation to click point } else { locate(); var pos = 0, // was posx modified by SoftMoon-WebWare sLft = 0, sTop = 0; // Internet Explorer doctype woes if(document.documentElement && document.documentElement.scrollTop) { sTop = document.documentElement.scrollTop; sLft = document.documentElement.scrollLeft; } else if (document.body) { sTop = document.body.scrollTop; sLft = document.body.scrollLeft; }; if(e.pageX) { //NOTE modified by SoftMoon-WebWare and the lines below // (vertical) ? e.pageY : e.pageX; pos = {y: e.pageY, x: e.pageX}; } else if (e.clientX) { // (vertical) ? e.clientY + sTop : e.clientX + sLft; pos = {y: e.clientY + sTop, x: e.clientX + sLft}; }; //pos -= vertical ? y + Math.round(getHeight(handle) / 2) : x + Math.round(getWidth(handle) / 2); pos = Math.sqrt( Math.pow(pos.y - (sliderBar.y + Math.sin(rotate)*Math.round(getHeight(handle) / 2)), 2) + Math.pow(pos.x - (sliderBar.x + Math.cos(rotate)*Math.round(getWidth(handle) / 2)), 2) ); pos = snapToPxValue(Math.round(pos)); // var trs=getDocPos(transformer); //, div=getDocPos(transformer.previousChild) // alert('**slider** x,y' +'\n'+ sliderBar.x +'\n'+ sliderBar.y +'\n'+'**transformer** x,y' +'\n'+ trs.left +'\n'+ trs.top); // +'\n'+ div.left +'\n'+ div.top); // Tween animation to click point if(animation == "tween") { addClass(outerWrapper, 'fd-slider-active'); tweenTo(pos); // Progressive increment to click point } else if(animation == "timed") { addClass(outerWrapper, 'fd-slider-active'); addEvent(document, touchEvents ? 'touchend' : 'mouseup', onDocMouseUp); destPos = pos; onTimer(); // Immediate jump to click point } else { pixelsToValue(pos); //addEvent(document, touchEvents ? 'touchend' : 'mouseup', onMouseUp); }; }; //NOTE: added callback callback('mousedown'); return stopEvent(e); }; /*********synthetic events added by SoftMoon-WebWare - do not relate to fdSlider functionality */ /* these simply pass on events from the synthetic fdSlider input type range to the real input */ /* to facilitate their intended functionality. */ function onMouseUp(e) {callback('mouseup');} function onClick(e) {callback('click');} function onMouseMove(e) {callback('mousemove');} /************************************************************************************************/ // Progressive increment to click point - clear the animation timer and remove the mouseup/touchend event function onDocMouseUp( e ) { e = e || window.event; preventDefault(e); removeEvent(document, touchEvents ? 'touchend' : 'mouseup', onDocMouseUp); removeClass(outerWrapper, "fd-slider-active"); clearTimeout(timer); timer = null; kbEnabled = true; callback('mouseup'); //added by SoftMoon-WebWare return stopEvent(e); }; // Mouseup or touchend event on the document to stop drag function stopDrag(e) { e = e || window.event; preventDefault(e); if(touchEvents) { removeEvent(document, 'touchmove', trackMouse); removeEvent(document, 'touchend', stopDrag); } else { removeEvent(document, 'mousemove', trackMouse); removeEvent(document, 'mouseup', stopDrag); }; kbEnabled = true; // removeClass(document.body, "fd-slider-drag-" + (vertical ? "vertical" : "horizontal")); removeClass(document.body, "fd-slider-drag-" + (horizontal ? "horizontal" : "vertical")); removeClass(outerWrapper, "fd-slider-active"); callback('mouseup'); //added by SoftMoon-WebWare callback("dragend"); return stopEvent(e); }; // Mousemove or touchmove event on the drag handle function trackMouse(e) { e = e || window.event; preventDefault(e); if(e.touches) { // Skip gestures if(e.targetTouches && e.targetTouches.length != 1) { return false; }; e = e.touches[0]; }; //pixelsToValue(snapToPxValue(handlePos + (vertical) ? (e.clientY - mousePos.y) : (e.clientX - mousePos.x))); var off; if (horizontal) { //closer to horizontal off=(e.clientX - mousePos.x)/Math.cos(rotate); } else { //closer to vertical off=(e.clientY - mousePos.y)/Math.sin(rotate); } pixelsToValue(snapToPxValue(handlePos + off)); return false; }; // Increments the slider by "inc" steps function increment(inc) { var value = getWorkingValueFromInput(); userSet = true; value += inc * step; valueToPixels(getValidValue(value)); }; // Attempts to locate the on-screen position of the slider function locate(){ /*** var curleft = 0, curtop = 0, obj = outerWrapper; // Try catch for IE's benefit try { while (obj.offsetParent) { curleft += obj.offsetLeft; curtop += obj.offsetTop; obj = obj.offsetParent; }; } catch(err) {}; x = curleft; y = curtop; */// modified by SoftMoon-WebWare var pos = getDocPos(outerWrapper); sliderBar.x = pos.left; sliderBar.y = pos.top; if (rotate && transformer) { var tempX, rCenter = getDocPos(transformer); rCenter.left += getWidth(transformer)/2; rCenter.top += getHeight(transformer)/2; sliderBar.x = sliderBar.x - rCenter.left; sliderBar.y = sliderBar.y - rCenter.top; tempX = rCenter.left + (sliderBar.x*Math.cos(rotate) - sliderBar.y*Math.sin(rotate)); sliderBar.y = rCenter.top + (sliderBar.y*Math.cos(rotate) + sliderBar.x*Math.sin(rotate)); sliderBar.x = tempX; } }; // Used during the progressive animation to click point function onTimer() { var xtmp = parseInt(vertical ? handle.offsetTop : handle.offsetLeft, 10); xtmp = Math.round((destPos < xtmp) ? Math.max(destPos, Math.floor(xtmp - stepPx)) : Math.min(destPos, Math.ceil(xtmp + stepPx))); pixelsToValue(snapToPxValue(xtmp)); if(xtmp != destPos) { timer = setTimeout(onTimer, steps > 20 ? 50 : 100); } else { kbEnabled = true; removeClass(outerWrapper, "fd-slider-active"); callback("finalise"); }; }; var tween = function(){ frame++; var c = tweenC, d = 20, t = frame, b = tweenB, x = Math.ceil((t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b); pixelsToValue(t == d ? tweenX : x); if(t!=d) { // Call the "move" callback on each animation increment callback("move"); timer = setTimeout(tween, 20); } else { clearTimeout(timer); timer = null; kbEnabled = true; removeClass(outerWrapper, "fd-slider-focused"); removeClass(outerWrapper, "fd-slider-active"); // Call the "finalise" callback whenever the animation is complete callback("finalise"); }; }; function tweenTo(tx){ kbEnabled = false; tweenX = parseInt(tx, 10); tweenB = parseInt(vertical ? handle.offsetTop : handle.offsetLeft, 10); tweenC = tweenX - tweenB; tweenD = 20; frame = 0; if(!timer) { timer = setTimeout(tween, 20); }; }; // Returns a value within the range & sets the userSet var // i.e. has the user entered a valid value function checkValue(value) { if(isNaN(value) || value === "" || typeof value == "undefined") { userSet = false; return defaultVal; } else if(value < Math.min(rMin,rMax)) { userSet = false; return Math.min(rMin,rMax); } else if(value > Math.max(rMin,rMax)) { userSet = false; return Math.max(rMin,rMax); }; userSet = true; return value; }; // Returns a value within a range - uses the form element value as base function getWorkingValueFromInput() { return getValidValue(tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex); }; // Returns a value within the range function getValidValue(value) { return (isNaN(value) || value === "" || typeof value == "undefined") ? defaultVal : Math.min(Math.max(value, Math.min(rMin,rMax)), Math.max(rMin,rMax)); }; // Calculates value according to pixel position of slider handle function pixelsToValue(px) { var val = getValidValue(scale ? percentToValue(pixelsToPercent(px)) : vertical ? max - (Math.round(px / stepPx) * step) : min + (Math.round(px / stepPx) * step)); handle.style[vertical ? "top" : "left"] = (px || 0) + "px"; redrawRange(); setInputValue((tagName == "select" || step == 1) ? Math.round(val) : val); }; // Calculates pixel position according to form element value function valueToPixels(val) { var clearVal = false, value; // Allow empty values for non-polyfill sliders if((typeof val == "undefined" || isNaN(val) || val === "") && tagName == "input" && !forceValue) { value = defaultVal; clearVal = true; userSet = false; } else { value = checkValue(val); }; handle.style[vertical ? "top" : "left"] = (scale ? percentToPixels(valueToPercent(value)) : vertical ? Math.round(((max - value) / step) * stepPx) : Math.round(((value - min) / step) * stepPx)) + "px"; redrawRange(); setInputValue(clearVal ? "" : value); }; // Rounds a pixel value to the nearest "snap" point on the slider scale function snapToPxValue(px) { if(scale) { return Math.max(Math.min(rMaxPx, px), rMinPx); } else { var rem = px % stepPx; if(rem && rem >= (stepPx / 2)) { px += (stepPx - rem); } else { px -= rem; }; if(px < Math.min(Math.abs(rMinPx), Math.abs(rMaxPx))) { px = Math.min(Math.abs(rMinPx), Math.abs(rMaxPx)); } else if(px > Math.max(Math.abs(rMinPx), Math.abs(rMaxPx))) { px = Math.max(Math.abs(rMinPx), Math.abs(rMaxPx)); }; return Math.min(Math.max(px, 0), rMaxPx); }; }; // Calculates a value according to percentage of distance handle has travelled function percentToValue(pct) { var st = 0, fr = min, value; for(var s in scale) { if(!scale.hasOwnProperty(s)) { continue; }; if(pct >= st && pct <= +s ) { value = fr + ((pct - st) * (+scale[s] - fr) ) / (+s - st); }; st = +s; fr = +scale[s]; }; return value; }; // Calculates the percentage handle position according to form element value function valueToPercent(value) { var st = 0, fr = min, pct = 0; for(var s in scale) { if(!scale.hasOwnProperty(s)) { continue; }; if(value >= fr && value <= +scale[s]){ pct = st + (value - fr) * (+s - st) / (+scale[s] - fr); }; st = +s; fr = +scale[s]; }; return pct; }; function percentToPixels(percent) { // return ((outerWrapper[vertical ? "offsetHeight" : "offsetWidth"] - handle[vertical ? "offsetHeight" : "offsetWidth"]) / 100) * percent; return (vertical ? (getWidth(outerWrapper) - getWidth(handle)) : (getHeight(outerWrapper) - getHeight(handle)) / 100) * percent; }; function pixelsToPercent(pixels) { // return pixels / ((outerWrapper[vertical ? "offsetHeight" : "offsetWidth"] - outerWrapper[handle ? "offsetHeight" : "offsetWidth"]) / 100); //****** NOTE! bug?!! above should be: handle[vertical ? // Is there a bug above? I think so; here is a bug fix AND an MSIE hasLayout-bug fix return pixels / ((vertical ? (getWidth(outerWrapper) - getWidth(handle)) : (getHeight(outerWrapper) - getHeight(handle))) / 100); }; // Sets the form element with a valid value function setInputValue(val) { // The update callback doesn't mean the input value has changed callback("update"); // If the user has not set this value or has entered an incorrect value then set a class // to enable styling of the slider if(!userSet) { addClass(outerWrapper, "fd-slider-no-value"); } else { removeClass(outerWrapper, "fd-slider-no-value"); }; if(tagName == "select") { try { val = parseInt(val, 10); if(inp.selectedIndex === val) { updateAriaValues(); return; }; inp.options[val].selected = true; } catch (err) {}; } else { if(val != "") { val = (min + (Math.round((val - min) / step) * step)).toFixed(precision); }; if(inp.value === val) { updateAriaValues(); return; }; inp.value = val; }; updateAriaValues(); callback("change"); }; function checkInputValue(value) { return !(isNaN(value) || value === "" || value < Math.min(rMin,rMax) || value > Math.max(rMin,rMax)); }; function setSliderRange(newMin, newMax) { if(rMin > rMax) { newMin = Math.min(min, Math.max(newMin, newMax)); newMax = Math.max(max, Math.min(newMin, newMax)); rMin = Math.max(newMin, newMax); rMax = Math.min(newMin, newMax); } else { newMin = Math.max(min, Math.min(newMin, newMax)); newMax = Math.min(max, Math.max(newMin, newMax)); rMin = Math.min(newMin, newMax); rMax = Math.max(newMin, newMax); }; if(defaultVal < Math.min(rMin, rMax)) { defaultVal = Math.min(rMin, rMax); } else if(defaultVal > Math.max(rMin, rMax)) { defaultVal = Math.max(rMin, rMax); }; handle.setAttribute("aria-valuemin", rMin); handle.setAttribute("aria-valuemax", rMax); checkValue(tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex); redraw(); }; function redrawRange() { if(noRangeBar) { return; }; if(vertical) { rangeBar.style["height"] = (getHeight(bar) - handle.offsetTop) + "px"; } else { rangeBar.style["width"] = handle.offsetLeft + "px"; }; }; function findLabel() { var label = false, labelList = document.getElementsByTagName('label'); // loop through label array attempting to match each 'for' attribute to the id of the current element for(var i = 0, lbl; lbl = labelList[i]; i++) { // Internet Explorer requires the htmlFor test if((lbl['htmlFor'] && lbl['htmlFor'] == inp.id) || (lbl.getAttribute('for') == inp.id)) { label = lbl; break; }; }; if(label && !label.id) { label.id = inp.id + "_label"; }; return label; }; function updateAriaValues() { handle.setAttribute("aria-valuenow", tagName == "select" ? inp.options[inp.selectedIndex].value : inp.value); handle.setAttribute("aria-valuetext", tagName == "select" ? (inp.options[inp.selectedIndex].text ? inp.options[inp.selectedIndex].text : inp.options[inp.selectedIndex].value) : inp.value); }; function onInputChange(e) { userSet = true; valueToPixels(tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex); updateAriaValues(); }; function onReset(e) { if(tagName == "input") { inp.value = inp.defaultValue; } else { inp.selectedIndex = resetDef; }; checkValue(tagName == "select" ? inp.options[inp.selectedIndex].value : inp.value); redraw(); updateAriaValues(); }; function valueSet(tf) { userSet = !!tf; }; // Sets a tabindex attribute on an element, bends over for IE. function setTabIndex(e, i) { e.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", i); e.tabIndex = i; }; (function() { if(html5Shim || hideInput) { addClass(inp, "fd-form-element-hidden"); } else { addEvent(inp, 'change', onInputChange); }; // Add stepUp & stepDown methods to input element if using the html5Shim if(html5Shim) { inp.stepUp = function(n) { increment(n||1); }; inp.stepDown = function(n) { increment(n||-1); }; }; outerWrapper = document.createElement('span'); // modified by SoftMoon-WebWare: outerWrapper.className = "fd-slider" + (vertical ? "-vertical " : " ") + (horizontal ? "" : "fd-slider-upright ") // we now add another classname when applicable + (!html5Shim ? "fd-slider-no-value " : "") + classNames; outerWrapper.id = "fd-slider-" + inp.id; if(vertical && inpHeight) { outerWrapper.style.height = inpHeight + "px"; }; wrapper = document.createElement('span'); wrapper.className = "fd-slider-inner"; bar = document.createElement('span'); bar.className = "fd-slider-bar"; if(!noRangeBar) { rangeBar = document.createElement('span'); rangeBar.className = "fd-slider-range"; }; if(fullARIA) { handle = document.createElement('span'); } else { handle = document.createElement('a'); handle.setAttribute("href", "#"); addEvent(handle, "click", stopEvent); }; setTabIndex(handle, 0); handle.className = "fd-slider-handle"; handle.appendChild(document.createTextNode(String.fromCharCode(160))); outerWrapper.appendChild(wrapper); if(!noRangeBar) { outerWrapper.appendChild(rangeBar); }; outerWrapper.appendChild(bar); outerWrapper.appendChild(handle); inp.parentNode.insertBefore(outerWrapper, inp); /*@cc_on@*/ /*@if(@_win32) handle.unselectable = "on"; if(!noRangeBar) rangeBar.unselectable = "on"; bar.unselectable = "on"; wrapper.unselectable = "on"; outerWrapper.unselectable = "on"; /*@end@*/ // Add ARIA accessibility info programmatically outerWrapper.setAttribute("role", "application"); handle.setAttribute("role", "slider"); handle.setAttribute("aria-valuemin", tagName == "select" ? inp.options[0].value : min); handle.setAttribute("aria-valuemax", tagName == "select" ? inp.options[inp.options.length - 1].value : max); var lbl = findLabel(); if(lbl) { handle.setAttribute("aria-labelledby", lbl.id); handle.id = "fd-slider-handle-" + inp.id; /*@cc_on /*@if(@_win32) lbl.setAttribute("htmlFor", handle.id); @else @*/ lbl.setAttribute("for", handle.id); /*@end @*/ }; // Are there page instructions if(document.getElementById(describedBy)) { handle.setAttribute("aria-describedby", describedBy); }; // Is the form element initially disabled if(inp.getAttribute("disabled") == true) { disableSlider(true); } else { enableSlider(true); }; // Does an initial form element value mean the user has set a valid value? // Also called onload in case browsers have automatically set the input value if(varSetRules.onvalue) { userSet = true; checkValue(tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex); }; if(inp.form) { addEvent(inp.form, "reset", onReset); }; updateAriaValues(); callback("create"); redraw(); })(); // added by SoftMoon-WebWare if (transformer===options.inp) transformer=outerWrapper; return { onResize: function(e) { if(getHeight(outerWrapper) != sliderH || getWidth(outerWrapper) != sliderW) { redraw(); }; }, destroy: function() { destroySlider(); }, reset: function() { valueToPixels(tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex); }, stepUp: function(n) { increment(Math.abs(n)||1); }, stepDown: function(n) { increment(-Math.abs(n)||-1); }, increment: function(n) { increment(n); }, disable: function() { disableSlider(); }, enable: function() { enableSlider(); }, setRange: function(mi, mx) { setSliderRange(mi, mx); }, getValueSet: function() { return !!userSet; }, setValueSet: function(tf) { valueSet(tf); }, checkValue: function() { if(varSetRules.onvalue) { userSet = true; checkValue(tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex); }; updateAriaValues(); redraw(); } }; }; addEvent(window, "load", init); addEvent(window, "load", function() { setTimeout(function() { var slider; for(slider in sliders) { sliders[slider].checkValue(); } }, 0); }); addEvent(window, "resize", resize); addEvent(window, "unload", unload); // Have we been passed JSON within the including script tag (function() { var scriptFiles = document.getElementsByTagName('script'), scriptInner = String(scriptFiles[scriptFiles.length - 1].innerHTML).replace(/[\n\r\s\t]+/g, " ").replace(/^\s+/, "").replace(/\s+$/, ""), json = parseJSON(scriptInner); if(typeof json === "object" && !("err" in json)) { affectJSON(json); }; })(); // Add oldie class if needed for IE < 9 /*@cc_on @if (@_jscript_version < 9) addClass(document.documentElement, "oldie"); @end @*/ return { createSlider: function(opts) { return createSlider(opts); }, onDomReady: function() { onDomReady(); }, destroyAll: function() { destroyAllsliders(); }, destroySlider: function(id) { return destroySingleSlider(id); }, redrawAll: function() { resize(); }, addEvent: addEvent, removeEvent: removeEvent, stopEvent: stopEvent, increment: function(id, numSteps) { if(!sliderExists(id)) { return false; }; sliders[id].increment(numSteps); }, stepUp: function(id, n) { if(!sliderExists(id)) { return false; }; sliders[id].stepUp(Math.abs(n)||1); }, stepDown: function(id, n) { if(!sliderExists(id)) { return false; }; sliders[id].stepDown(-Math.abs(n)||-1); }, setRange: function(id, newMin, newMax) { if(!sliderExists(id)) { return false; }; sliders[id].setRange(newMin, newMax); }, updateSlider: function(id) { if(!sliderExists(id)) { return false; }; sliders[id].reset(); }, disable: function(id) { if(!sliderExists(id)) { return false; }; sliders[id].disable(); }, enable: function(id) { if(!sliderExists(id)) { return false; }; sliders[id].enable(); }, getValueSet: function() { return getValueSet(); }, setValueSet: function(a, tf) { if(!sliderExists(id)) { return false; }; setValueSet(a, tf); }, setGlobalVariables: function(json) { affectJSON(json); }, removeOnload: function() { removeOnLoadEvent(); } }; })();