From: Joar Wandborg Date: Wed, 18 Apr 2012 10:06:10 +0000 (+0200) Subject: Added html5slider to support range inputs in Firefox X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=1d83cf8a8156054a4e1f3d23f23dac7aa33e69e7;p=mediagoblin.git Added html5slider to support range inputs in Firefox --- diff --git a/extlib/html5slider/html5slider.js b/extlib/html5slider/html5slider.js new file mode 100644 index 00000000..e69c3d2b --- /dev/null +++ b/extlib/html5slider/html5slider.js @@ -0,0 +1,268 @@ +/* +html5slider - a JS implementation of for Firefox 4 and up +https://github.com/fryn/html5slider + +Copyright (c) 2010-2011 Frank Yan, + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +(function() { + +// test for native support +var test = document.createElement('input'); +try { + test.type = 'range'; + if (test.type == 'range') + return; +} catch (e) { + return; +} + +// test for required property support +if (!document.mozSetImageElement || !('MozAppearance' in test.style)) + return; + +var scale; +var isMac = navigator.platform == 'MacIntel'; +var thumb = { + radius: isMac ? 9 : 6, + width: isMac ? 22 : 12, + height: isMac ? 16 : 20 +}; +var track = '-moz-linear-gradient(top, transparent ' + (isMac ? + '6px, #999 6px, #999 7px, #ccc 9px, #bbb 11px, #bbb 12px, transparent 12px' : + '9px, #999 9px, #bbb 10px, #fff 11px, transparent 11px') + + ', transparent)'; +var styles = { + 'min-width': thumb.width + 'px', + 'min-height': thumb.height + 'px', + 'max-height': thumb.height + 'px', + padding: 0, + border: 0, + 'border-radius': 0, + cursor: 'default', + 'text-indent': '-999999px' // -moz-user-select: none; breaks mouse capture +}; +var onChange = document.createEvent('HTMLEvents'); +onChange.initEvent('change', true, false); + +if (document.readyState == 'loading') + document.addEventListener('DOMContentLoaded', initialize, true); +else + initialize(); + +function initialize() { + // create initial sliders + Array.forEach(document.querySelectorAll('input[type=range]'), transform); + // create sliders on-the-fly + document.addEventListener('DOMNodeInserted', onNodeInserted, true); +} + +function onNodeInserted(e) { + check(e.target); + if (e.target.querySelectorAll) + Array.forEach(e.target.querySelectorAll('input'), check); +} + +function check(input, async) { + if (input.localName != 'input' || input.type == 'range'); + else if (input.getAttribute('type') == 'range') + transform(input); + else if (!async) + setTimeout(check, 0, input, true); +} + +function transform(slider) { + + var isValueSet, areAttrsSet, isChanged, isClick, prevValue, rawValue, prevX; + var min, max, step, range, value = slider.value; + + // lazily create shared slider affordance + if (!scale) { + scale = document.body.appendChild(document.createElement('hr')); + style(scale, { + '-moz-appearance': isMac ? 'scale-horizontal' : 'scalethumb-horizontal', + display: 'block', + visibility: 'visible', + opacity: 1, + position: 'fixed', + top: '-999999px' + }); + document.mozSetImageElement('__sliderthumb__', scale); + } + + // reimplement value and type properties + var getValue = function() { return '' + value; }; + var setValue = function setValue(val) { + value = '' + val; + isValueSet = true; + draw(); + delete slider.value; + slider.value = value; + slider.__defineGetter__('value', getValue); + slider.__defineSetter__('value', setValue); + }; + slider.__defineGetter__('value', getValue); + slider.__defineSetter__('value', setValue); + slider.__defineGetter__('type', function() { return 'range'; }); + + // sync properties with attributes + ['min', 'max', 'step'].forEach(function(prop) { + if (slider.hasAttribute(prop)) + areAttrsSet = true; + slider.__defineGetter__(prop, function() { + return this.hasAttribute(prop) ? this.getAttribute(prop) : ''; + }); + slider.__defineSetter__(prop, function(val) { + val === null ? this.removeAttribute(prop) : this.setAttribute(prop, val); + }); + }); + + // initialize slider + slider.readOnly = true; + style(slider, styles); + update(); + + slider.addEventListener('DOMAttrModified', function(e) { + // note that value attribute only sets initial value + if (e.attrName == 'value' && !isValueSet) { + value = e.newValue; + draw(); + } + else if (~['min', 'max', 'step'].indexOf(e.attrName)) { + update(); + areAttrsSet = true; + } + }, true); + + slider.addEventListener('mousedown', onDragStart, true); + slider.addEventListener('keydown', onKeyDown, true); + slider.addEventListener('focus', onFocus, true); + slider.addEventListener('blur', onBlur, true); + + function onDragStart(e) { + isClick = true; + setTimeout(function() { isClick = false; }, 0); + if (e.button || !range) + return; + var width = parseFloat(getComputedStyle(this, 0).width); + var multiplier = (width - thumb.width) / range; + if (!multiplier) + return; + // distance between click and center of thumb + var dev = e.clientX - this.getBoundingClientRect().left - thumb.width / 2 - + (value - min) * multiplier; + // if click was not on thumb, move thumb to click location + if (Math.abs(dev) > thumb.radius) { + isChanged = true; + this.value -= -dev / multiplier; + } + rawValue = value; + prevX = e.clientX; + this.addEventListener('mousemove', onDrag, true); + this.addEventListener('mouseup', onDragEnd, true); + } + + function onDrag(e) { + var width = parseFloat(getComputedStyle(this, 0).width); + var multiplier = (width - thumb.width) / range; + if (!multiplier) + return; + rawValue += (e.clientX - prevX) / multiplier; + prevX = e.clientX; + isChanged = true; + this.value = rawValue; + } + + function onDragEnd() { + this.removeEventListener('mousemove', onDrag, true); + this.removeEventListener('mouseup', onDragEnd, true); + } + + function onKeyDown(e) { + if (e.keyCode > 36 && e.keyCode < 41) { // 37-40: left, up, right, down + onFocus.call(this); + isChanged = true; + this.value = value + (e.keyCode == 38 || e.keyCode == 39 ? step : -step); + } + } + + function onFocus() { + if (!isClick) + this.style.boxShadow = !isMac ? '0 0 0 2px #fb0' : + '0 0 2px 1px -moz-mac-focusring, inset 0 0 1px -moz-mac-focusring'; + } + + function onBlur() { + this.style.boxShadow = ''; + } + + // determines whether value is valid number in attribute form + function isAttrNum(value) { + return !isNaN(value) && +value == parseFloat(value); + } + + // validates min, max, and step attributes and redraws + function update() { + min = isAttrNum(slider.min) ? +slider.min : 0; + max = isAttrNum(slider.max) ? +slider.max : 100; + if (max < min) + max = min > 100 ? min : 100; + step = isAttrNum(slider.step) && slider.step > 0 ? +slider.step : 1; + range = max - min; + draw(true); + } + + // recalculates value property + function calc() { + if (!isValueSet && !areAttrsSet) + value = slider.getAttribute('value'); + if (!isAttrNum(value)) + value = (min + max) / 2;; + // snap to step intervals (WebKit sometimes does not - bug?) + value = Math.round((value - min) / step) * step + min; + if (value < min) + value = min; + else if (value > max) + value = min + ~~(range / step) * step; + } + + // renders slider using CSS background ;) + function draw(attrsModified) { + calc(); + if (isChanged && value != prevValue) + slider.dispatchEvent(onChange); + isChanged = false; + if (!attrsModified && value == prevValue) + return; + prevValue = value; + var position = range ? (value - min) / range * 100 : 0; + var bg = '-moz-element(#__sliderthumb__) ' + position + '% no-repeat, '; + style(slider, { background: bg + track }); + } + +} + +function style(element, styles) { + for (var prop in styles) + element.style.setProperty(prop, styles[prop], 'important'); +} + +})(); diff --git a/mediagoblin/static/js/audio.js b/mediagoblin/static/js/audio.js index 217a2160..50d58cd9 100644 --- a/mediagoblin/static/js/audio.js +++ b/mediagoblin/static/js/audio.js @@ -210,14 +210,8 @@ var audioPlayer = new Object(); $('
').appendTo(im.parent()); $('
▶
').appendTo(im.parent()); $('
00:00
').appendTo(im.parent()); - if (navigator && /Firefox/.test(navigator.userAgent)) { - $('

Sorry, Firefox does not support the ' - + 'range input type, you won\'t be able to change the volume

') - .appendTo(im.parent().parent()); - } else { - $('').appendTo(im.parent()); - } + $('').appendTo(im.parent()); $('.audio-spectrogram').trigger('attachedControls'); }; })(audioPlayer); diff --git a/mediagoblin/static/js/extlib/html5slider.js b/mediagoblin/static/js/extlib/html5slider.js new file mode 120000 index 00000000..feae2cb8 --- /dev/null +++ b/mediagoblin/static/js/extlib/html5slider.js @@ -0,0 +1 @@ +../../../../extlib/html5slider/html5slider.js \ No newline at end of file diff --git a/mediagoblin/templates/mediagoblin/media_displays/audio.html b/mediagoblin/templates/mediagoblin/media_displays/audio.html index 3efd80e1..06d4f882 100644 --- a/mediagoblin/templates/mediagoblin/media_displays/audio.html +++ b/mediagoblin/templates/mediagoblin/media_displays/audio.html @@ -21,6 +21,8 @@ {% block mediagoblin_head %} {{ super() }} + {% endblock %}