diff --git a/css/main-window.css b/css/main-window.css
index bc53e21b..69a5206b 100755
--- a/css/main-window.css
+++ b/css/main-window.css
@@ -364,6 +364,11 @@
#winamp2-js .stop #position::-webkit-slider-thumb { visibility: hidden; }
#winamp2-js .stop #position::-moz-range-thumb { visibility: hidden; }
+/* For some reason this is needed for the position slider to show up now that
+ * we are using React.
+ */
+#winamp2-js .play #position::-webkit-slider-thumb { visibility: visible; }
+
#winamp2-js .actions div {
height: 18px;
width: 23px;
diff --git a/js/Position.jsx b/js/Position.jsx
new file mode 100644
index 00000000..8b83bfb9
--- /dev/null
+++ b/js/Position.jsx
@@ -0,0 +1,77 @@
+// Single line text display that can animate and hold multiple registers
+import React from 'react';
+import {connect} from 'react-redux';
+
+import {getTimeStr} from './utils';
+
+class Position extends React.Component {
+ constructor(props) {
+ super(props);
+ // Consider moving to global state. Currently nobody else cares.
+ this.state = {
+ mouseIsDown: false,
+ currentPosition: 0
+ };
+ this.setPosition = this.setPosition.bind(this);
+ this.showMarquee = this.showMarquee.bind(this);
+ this.hideMarquee = this.hideMarquee.bind(this);
+ this.onMouseUp = this.onMouseUp.bind(this);
+ }
+
+ setPosition(e) {
+ this.props.dispatch({type: 'SHOW_MARQUEE_REGISTER', register: 'message'});
+ this.setState({
+ mouseIsDown: true,
+ currentPosition: e.target.value
+ });
+ var newPercentComplete = e.target.value;
+ var newElapsed = getTimeStr(this.props.length * newPercentComplete / 100);
+ var duration = getTimeStr(this.props.length);
+ var message = `Seek to: ${newElapsed}/${duration} (${newPercentComplete}%)`;
+ this.props.dispatch({type: 'SET_MARQUEE_REGISTER', register: 'message', text: message});
+ }
+
+ onMouseUp(e) {
+ this.props.dispatch({type: 'SHOW_MARQUEE_REGISTER', register: 'songTitle'});
+ this.props.dispatch({type: 'SET_POSITION', position: e.target.value});
+ this.setState({mouseIsDown: false});
+ }
+
+ showMarquee() {
+ }
+
+ hideMarquee() {
+ }
+
+ render() {
+ const position = this.props.length ?
+ (this.props.timeElapsed / this.props.length) * 100 :
+ 0;
+
+ // In shade mode, the position slider shows up differently depending on if
+ // it's near the start, middle or end of its progress
+ let className = '';
+ if (position <= 33) {
+ className = 'left';
+ } else if (position >= 66) {
+ className = 'right';
+ }
+
+ const displayedPosition = this.state.mouseIsDown ? this.state.currentPosition : position;
+ return ;
+ }
+}
+
+module.exports = connect(state => state.media)(Position);
diff --git a/js/main-window-dom.js b/js/main-window-dom.js
index cfb8c7d3..cafad569 100644
--- a/js/main-window-dom.js
+++ b/js/main-window-dom.js
@@ -58,7 +58,7 @@ module.exports = el('div', {id: 'main-window', class: 'loading stop'}, [
el('div', {id: 'equalizer-button'}),
el('div', {id: 'playlist-button'})
]),
- el('input', {id: 'position', type: 'range', min: '0', max: '100', step: '1', value: '0'}),
+ el('div', {id: 'position-holder'}),
el('div', {id: 'actions-holder'}),
el('div', {id: 'eject'}),
el('div', {class: 'shuffle-repeat'}, [
diff --git a/js/main-window.js b/js/main-window.js
index 8da08497..b38a189b 100644
--- a/js/main-window.js
+++ b/js/main-window.js
@@ -7,6 +7,7 @@ import Kbps from './Kbps.jsx';
import Khz from './Khz.jsx';
import Volume from './Volume.jsx';
import Balance from './Balance.jsx';
+import Position from './Position.jsx';
import '../css/main-window.css';
@@ -17,7 +18,6 @@ module.exports = {
close: document.getElementById('close'),
shade: document.getElementById('shade'),
buttonD: document.getElementById('button-d'),
- position: document.getElementById('position'),
visualizer: document.getElementById('visualizer'),
eject: document.getElementById('eject'),
repeat: document.getElementById('repeat'),
@@ -40,6 +40,7 @@ module.exports = {
this.winamp.renderTo(, document.getElementById('khz-holder'));
this.winamp.renderTo(, document.getElementById('volume-holder'));
this.winamp.renderTo(, document.getElementById('balance-holder'));
+ this.winamp.renderTo(, document.getElementById('position-holder'));
this._registerListeners();
return this;
@@ -73,33 +74,6 @@ module.exports = {
self.winamp.toggleDoubledMode();
};
- this.nodes.position.onmousedown = function() {
- if (!self.nodes.window.classList.contains('stop')){
- self.winamp.dispatch({type: 'SHOW_MARQUEE_REGISTER', register: 'position'});
- self.nodes.window.classList.add('setting-position');
- }
- };
-
- this.nodes.position.onmouseup = function() {
- self.winamp.dispatch({type: 'SHOW_MARQUEE_REGISTER', register: 'songTitle'});
- self.nodes.window.classList.remove('setting-position');
- };
-
- this.nodes.position.oninput = function() {
- var newPercentComplete = self.nodes.position.value;
- var newFractionComplete = newPercentComplete / 100;
- var newElapsed = self._timeString(self.winamp.getDuration() * newFractionComplete);
- var duration = self._timeString(self.winamp.getDuration());
- var message = 'Seek to: ' + newElapsed + '/' + duration + ' (' + newPercentComplete + '%)';
- self.winamp.dispatch({type: 'SET_MARQUEE_REGISTER', register: 'message', text: message});
- };
-
- this.nodes.position.onchange = function() {
- if (self.winamp.getState() !== 'stop'){
- self.winamp.seekToPercentComplete(this.value);
- }
- };
-
this.nodes.eject.onclick = function() {
self.winamp.dispatch({type: 'OPEN_FILE_DIALOG'});
};
@@ -116,9 +90,6 @@ module.exports = {
self.winamp.toggleVisualizer();
};
- window.addEventListener('timeUpdated', function() {
- self.updateTime();
- });
window.addEventListener('startWaiting', function() {
self.setWorkingIndicator();
});
@@ -167,30 +138,6 @@ module.exports = {
this.nodes.window.classList.add('closed');
},
- updatePosition: function() {
- if (!this.nodes.window.classList.contains('setting-position')) {
- this.nodes.position.value = this.winamp.getPercentComplete();
- }
- },
-
- // In shade mode, the position slider shows up differently depending on if
- // it's near the start, middle or end of its progress
- updateShadePositionClass: function() {
- var position = this.nodes.position;
-
- position.removeAttribute('class');
- if (position.value <= 33) {
- position.classList.add('left');
- } else if (position.value >= 66) {
- position.classList.add('right');
- }
- },
-
- updateTime: function() {
- this.updateShadePositionClass();
- this.updatePosition();
- },
-
setWorkingIndicator: function() {
this.nodes.workIndicator.classList.add('selected');
},
diff --git a/js/reducers.js b/js/reducers.js
index ae9cf952..3781edb2 100644
--- a/js/reducers.js
+++ b/js/reducers.js
@@ -91,7 +91,7 @@ const media = (state, action) => {
return {
timeMode: 'ELAPSED',
timeElapsed: 0,
- length: null,
+ length: null, // Consider renaming to "duration"
kbps: null,
khz: null,
volume: 50,
@@ -146,6 +146,9 @@ const createReducer = (winamp) => {
case 'SET_BALANCE':
winamp.setBalance(action.balance);
return state;
+ case 'SET_POSITION':
+ winamp.seekToPercentComplete(action.position);
+ return state;
case 'CLOSE_WINAMP':
winamp.close();
return state;