pure_player
Version:
HTML5 Javascript player
482 lines (420 loc) • 17.8 kB
JavaScript
/**
* @author Oleksii Svyrydenko aka Alex Slipknot
* @copyright Oleksii Svyrydenko
* @version 1.0
*/
;
(function () {
Element.prototype.on = function (eventName, eventListener, useCapture) {
this.addEventListener(eventName, eventListener, useCapture);
return this;
};
HTMLElement.prototype.purePlayer = function (params) {
var defaultParams = {
format: 'video/mp4',
video: {
'': {
'src': this.src,
'subtitles': 'link_to_subtitles'
}
}
};
params = params || defaultParams;
for (var attr in defaultParams) {
params[attr] = typeof params[attr] !== 'undefined' ? params[attr] : defaultParams[attr];
}
var me = this,
preloaderItem = document.createElement('div'),
preloader = document.createElement('div'),
source = document.createElement('source'),
video = document.createElement('video'),
player = document.createElement('div'),
controlsBar = document.createElement('div'),
progressWrapper = document.createElement('div'),
hovered = document.createElement('span'),
progressCommon = document.createElement('div'),
progressPosition = document.createElement('div'),
progressLoader = document.createElement('div'),
playButton = document.createElement('div'),
timeProgress = document.createElement('span'),
controlButtons = document.createElement('div'),
speedBox = document.createElement('ul'),
selectedSpeed = document.createElement('li'),
speedBoxHidden = document.createElement('li'),
speed = document.createElement('ul'),
speedItem = document.createElement('li'),
qualityBox = document.createElement('ul'),
selectedQuality = document.createElement('li'),
qualityBoxHidden = document.createElement('li'),
quality = document.createElement('ul'),
volumeBox = document.createElement('div'),
volumeIcon = document.createElement('div'),
volumeBar = document.createElement('div'),
volume = document.createElement('div'),
fullSizeIcon = document.createElement('div'),
hideTimer,
fingerX = 0,
stopRewind = true,
rewindWorking = false;
var fn = {
toggleFullScreen: function () {
if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
if (player.webkitEnterFullscreen) {
player.webkitEnterFullscreen();
} else if (player.requestFullscreen) {
player.requestFullscreen();
} else if (player.mozRequestFullScreen) {
player.mozRequestFullScreen();
} else if (player.webkitRequestFullscreen) {
player.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
if (player.webkitExitFullscreen) {
player.webkitExitFullscreen();
} else if (document.cancelFullScreen) {
document.cancelFullScreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
}
}
},
convertTime: function (sec) {
var hh = Math.floor(sec / 60 / 60).toFixed();
var mm = Math.floor((sec - hh * 60 * 60) / 60).toFixed();
var ss = (((sec - hh * 60 * 60) - mm * 60)).toFixed();
if (mm < 10) {
mm = '0' + mm;
}
if (ss < 10) {
ss = '0' + ss;
}
if (hh > 0) {
return hh + ':' + mm + ':' + ss;
} else {
return mm + ':' + ss;
}
},
setVolume: function (i) {
i = i || 0;
if (i > 1) i = 1;
if (i < 0) i = 0;
if (i > 0) {
volumeIcon.className = 'volume-icon';
} else {
volumeIcon.className = 'volume-icon volume-off';
}
video.volume = i;
volume.style.width = video.volume * 100 + '%';
document.cookie = 'volume=' + i;
},
volumeInc: function (i) {
i = i || 0.2;
fn.setVolume(video.volume + i);
},
volumeDec: function (i) {
i = i || 0.2;
fn.setVolume(video.volume - i);
},
toggleMute: function () {
if (video.volume > 0) {
fn.setVolume(0);
} else {
fn.setVolume(1);
}
},
updatePosition: function () {
progressPosition.style.width = video.currentTime / video.duration * 100 + '%';
timeProgress.innerHTML = fn.convertTime(video.currentTime || 0) + ' / ' + fn.convertTime(video.duration || 0);
if (video.duration - video.currentTime === 0 && typeof me.onend === 'function') {
me.onend();
}
},
rewind: function (backward) {
rewindWorking = true;
if (!video.paused) {
video.pause();
}
if (!backward) {
video.currentTime += 10;
} else {
video.currentTime -= 10;
}
this.updatePosition();
this.showPanel();
if (!stopRewind) {
setTimeout(function () {
fn.rewind(backward)
}, 100);
} else {
rewindWorking = false;
}
},
hidePanel: function () {
controlsBar.className = 'controls-bar zero-opacity';
document.body.style.cursor = 'none';
},
showPanel: function () {
controlsBar.className = 'controls-bar';
document.body.style.cursor = 'auto';
clearTimeout(hideTimer);
hideTimer = setTimeout(fn.hidePanel, 2000);
}
};
preloaderItem.className = 'facebook_blockG';
preloader.className = 'preloader';
preloader.appendChild(preloaderItem);
preloader.appendChild(preloaderItem.cloneNode(true));
preloader.appendChild(preloaderItem.cloneNode(true));
source.setAttribute('type', params.format);
video.appendChild(source);
video.oncontextmenu = function () {
return false;
};
video.on('click', function () {
if (this.paused) {
this.play();
} else {
this.pause();
}
}).on('progress', function () {
if (this.buffered.length > 0) {
progressLoader.style.left = this.buffered.start(0) / this.duration * 100 + '%';
progressLoader.style.width = this.buffered.end(this.buffered.length - 1) / this.duration * 100 - parseFloat(progressLoader.style.left) + '%';
}
}).on('loadstart', function () {
preloader.style.display = 'block';
}).on('loadeddata', function () {
volume.style.width = this.volume * 100 + '%';
preloader.style.display = 'none';
timeProgress.innerHTML = fn.convertTime(this.currentTime) + ' / ' + fn.convertTime(this.duration);
}).on('pause', function () {
playButton.className = 'play-button';
if (typeof me.onpause === 'function') {
me.onpause(video.currentTime);
}
}).on('play', function () {
playButton.className = 'play-button pause';
fn.setVolume(document.cookie.replace(/(?:(?:^|.*;\s*)volume\s*\=\s*([^;]*).*$)|^.*$/, "$1"));
if (typeof me.onplay === 'function') {
me.onplay(video.currentTime);
}
}).on('timeupdate', function () {
if (this.duration) {
fn.updatePosition();
}
}).on('dblclick', fn.toggleFullScreen).on('touchstart', function (e) {
if (e.targetTouches.length === 1) {
fingerX = e.targetTouches[0].pageX;
}
}).on('touchmove', function (e) {
if (e.targetTouches.length === 1) {
stopRewind = false;
if (e.targetTouches[0].pageX > fingerX + 20 && !rewindWorking) {
fn.rewind(false)
} else if (e.targetTouches[0].pageX < fingerX - 20 && !rewindWorking) {
fn.rewind(true)
}
}
}).on('touchend', function () {
stopRewind = true;
});
playButton.className = 'play-button';
playButton.on('click', function () {
if (video.paused) {
video.play();
} else {
video.pause();
}
});
hovered.className = 'hovered';
timeProgress.className = 'time';
progressPosition.className = 'progress-position';
progressLoader.className = 'loader';
controlsBar.className = 'controls-bar';
selectedSpeed.className = 'selected-speed';
selectedSpeed.innerText = 'x1.0';
selectedSpeed.on('click', function (e) {
e.stopPropagation();
if (speedBoxHidden.style.display === 'block') {
speedBoxHidden.style.display = 'none';
} else {
speedBoxHidden.style.display = 'block';
}
});
selectedQuality.className = 'selected-quality';
selectedQuality.on('click', function (e) {
e.stopPropagation();
if (qualityBoxHidden.style.display === 'block') {
qualityBoxHidden.style.display = 'none';
} else {
qualityBoxHidden.style.display = 'block';
}
});
for (var i = 0.5; i <= 2; i += 0.5) {
var si = speedItem.cloneNode(true);
si.dataset.v = i;
si.innerText = 'x' + (i.toString().length === 1 ? i + '.0' : i);
si.on('click', function () {
var all = speed.childNodes;
[].forEach.call(all, function (i) {
i.className = '';
});
video.playbackRate = this.dataset.v;
this.className = 'active';
selectedSpeed.innerHTML = this.innerText;
speedBoxHidden.style.display = 'none';
});
speed.appendChild(si);
}
speed.className = 'speed';
speed.querySelector('[data-v="1"]').className = 'active';
speedBoxHidden.className = 'speed-box-hidden';
speedBoxHidden.appendChild(speed);
volumeIcon.className = 'volume-icon';
volumeIcon.on('click', function () {
fn.toggleMute();
});
volume.className = 'volume';
volumeBar.className = 'volume-bar';
volumeBar.appendChild(volume);
volumeBar.on('click', function (e) {
var i = e.offsetX / this.offsetWidth;
fn.setVolume(i);
});
volumeBox.className = 'volume-box';
volumeBox.appendChild(volumeIcon);
volumeBox.appendChild(volumeBar);
quality.className = 'quality';
for (var q in params.video) {
var qi = document.createElement('li');
qi.innerText = q;
qi.on('click', function () {
var all = quality.childNodes,
isPaused = video.paused,
currentTime = video.currentTime;
[].forEach.call(all, function (i) {
i.className = '';
});
this.className = 'active';
selectedQuality.innerText = this.innerText;
source.setAttribute('src', params.video[this.innerText].src);
video.src = params.video[this.innerText].src;
video.currentTime = currentTime;
if (!isPaused) video.play();
qualityBoxHidden.style.display = 'none';
});
quality.appendChild(qi);
}
qualityBoxHidden.className = 'quality-box-hidden';
qualityBoxHidden.appendChild(quality);
qualityBox.className = 'quality-box';
qualityBox.appendChild(selectedQuality);
qualityBox.appendChild(qualityBoxHidden);
speedBox.className = 'speed-box';
speedBox.appendChild(selectedSpeed);
speedBox.appendChild(speedBoxHidden);
fullSizeIcon.className = 'full-size-icon';
fullSizeIcon.on('click', fn.toggleFullScreen);
controlButtons.className = 'control-buttons';
controlButtons.appendChild(speedBox);
controlButtons.appendChild(qualityBox);
controlButtons.appendChild(volumeBox);
controlButtons.appendChild(fullSizeIcon);
progressCommon.className = 'progress';
progressCommon.appendChild(progressPosition);
progressCommon.appendChild(progressLoader);
progressWrapper.className = 'progress-wrap';
progressWrapper.appendChild(hovered);
progressWrapper.appendChild(progressCommon);
progressWrapper.on('click', function (e) {
video.currentTime = video.duration * e.layerX / (e.target.offsetWidth - e.target.offsetLeft);
}).on('mousemove', function (e) {
hovered.innerText = fn.convertTime((video.duration || 0) * e.layerX / (e.target.offsetWidth - e.target.offsetLeft));
hovered.style.left = ((e.layerX + e.target.offsetLeft) - hovered.offsetWidth / 2) + 'px';
});
controlsBar.appendChild(progressWrapper);
controlsBar.appendChild(playButton);
controlsBar.appendChild(timeProgress);
controlsBar.appendChild(controlButtons);
player.className = 'pure-player';
player.appendChild(preloader);
player.appendChild(video);
player.appendChild(controlsBar);
player.on('mousemove', fn.showPanel).on('click', function () {
qualityBoxHidden.style.display = 'none';
speedBoxHidden.style.display = 'none';
}).on('keydown', function (e) {
e = e || window.event;
e.preventDefault();
switch (e.which || e.keyCode) {
case 37:
if (video.currentTime - 5 > 0) {
video.currentTime -= 5;
} else {
video.currentTime = 0;
}
break;
case 39:
if (video.currentTime + 5 < video.duration) {
video.currentTime += 5;
} else {
video.currentTime = video.duration;
}
break;
case 38:
fn.volumeInc();
break;
case 40:
fn.volumeDec();
break;
case 32:
playButton.click();
break;
case 77:
fn.toggleMute();
break;
default:
break;
}
fn.showPanel();
}).on('mousewheel', function (e) {
e.preventDefault();
if (e.wheelDelta < 0) {
fn.volumeDec();
} else {
fn.volumeInc();
}
});
this.init = function () {
this.style.display = 'none';
this.parentNode.insertBefore(player, this.parentNode.nextSibling);
quality.firstChild.click();
fn.setVolume(document.cookie.replace(/(?:(?:^|.*;\s*)volume\s*\=\s*([^;]*).*$)|^.*$/, "$1") || 1);
if (params.autoplay) {
video.play();
}
};
this.destroy = function () {
video.pause();
this.parentNode.removeChild(player);
this.style.display = '';
};
this.play = function () {
video.play();
};
this.pause = function () {
video.pause();
};
this.stop = function () {
video.pause();
video.currentTime = 0;
};
this.setPosition = function (i) {
video.currentTime = i;
};
this.init();
return this;
};
})();