<commit_before>require('enyo');

/**
* Contains the declaration for the {@link enyo.Video} kind.
* @module enyo/Video
*/

var
	kind = require('./kind'),
	dispatcher = require('./dispatcher'),
	path = require('./path'),
	platform = require('./platform'),
	utils = require('./utils');
var
	Control = require('./Control'),
	MediaSource = require('./MediaSource'),
	Job = require('./job');

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to an integer greater than `1`.
*
* @event enyo.Video#onFastforward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to a value between `0` and `1`.
*
* @event enyo.Video#onSlowforward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to an integer less than `-1`.
*
* @event enyo.Video#onRewind
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to a value less than `0`
* but greater than or equal to `-1`.
*
* @event enyo.Video#onSlowrewind
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [jumpForward()]{@link enyo.Video#jumpForward} is called.
*
* @event enyo.Video#onJumpForward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [jumpBackward()]{@link enyo.Video#jumpBackward} is called.
*
* @event enyo.Video#onJumpBackward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when EventData is changed.
*
* @event enyo.Video#onPlay
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when the [image]{@link enyo.Image} has loaded.
*
* @event enyo.Video#onStart
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* {@link enyo.Video} is a [control]{@link enyo.Control} that allows you to play video.
* It is an abstraction of HTML 5 [Video]{@glossary video}.
*
* Initialize a video [component]{@link enyo.Component} as follows:
*
* ```
* {kind: 'Video', src: 'http://www.w3schools.com/html/movie.mp4'}
* ```
* 
* To play a video, call `this.$.video.play()`.
* 
* To get a reference to the actual HTML 5 Video element, call `this.$.video.hasNode()`.
*
* @namespace enyo
* @class enyo.Video
* @extends enyo.Control
* @ui
* @definedby module:enyo/Video
* @public
*/
module.exports = kind(
	/** @lends enyo.Video.prototype */ {

	/**
	* @private
	*/
	name: 'enyo.Video',

	/**
	* @private
	*/
	kind: Control,

	/**
	* @private
	*/
	published: 
		/** @lends enyo.Video.prototype */ {

		/**
		* Source URL of the video file; may be relative to the application's HTML file.
		* 
		* @type {String}
		* @default ''
		* @public
		*/
		src: '',

		/**
		* An [object]{@glossary Object} that may be used to specify multiple sources for the
		* same video file.
		* 
		* @type {Object}
		* @default null
		* @public
		*/
		sourceComponents: null,

		/**
		* Source of image file to show when video is not available.
		* 
		* @type {String}
		* @default ''
		* @public
		*/
		poster: '',

		/**
		* If `true`, controls for starting and stopping the video player are shown.
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		showControls: false,

		/**
		* Determines how (or whether) the {@link enyo.Video} object is preloaded.
		* Possible values:
		* - `'auto'`: Preload the video data as soon as possible.
		* - `'metadata'`: Preload only the video metadata.
		* - `'none'`: Do not preload any video data.
		* 
		* @type {String}
		* @default 'metadata'
		* @public
		*/
		preload: 'metadata',

		/**
		* If `true`, video will automatically start playing.
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		autoplay: false,

		/**
		* If `true`, when playback is finished, the video player will restart from
		* the beginning.
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		loop: false,

		/**
		* If `true`, video will be stretched to fill the entire window (webOS only).
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		fitToWindow: false,

		/**
		* The video aspect ratio, expressed as `width:height`.
		* 
		* @type {Number}
		* @default 0
		* @public
		*/
		aspectRatio: null,

		/**
		* Number of seconds to jump forward or backward.
		* 
		* @type {Number}
		* @default 30
		* @public
		*/
		jumpSec: 30,

		/**
		* Video playback rate.
		* 
		* @type {Number}
		* @default 1
		* @public
		*/
		playbackRate: 1,

		/**
		* Mapping of playback rate names to playback rate values that may be set.
		* ```
		* {
		*	fastForward: ['2', '4', '8', '16'],
		*	rewind: ['-2', '-4', '-8', '-16'],
		*	slowForward: ['1/4', '1/2', '1'],
		*	slowRewind: ['-1/2', '-1']
		* }
		* ```
		* 
		* @type {Object}
		* @default {
		*	fastForward: ['2', '4', '8', '16'],
		*	rewind: ['-2', '-4', '-8', '-16'],
		*	slowForward: ['1/4', '1/2', '1'],
		*	slowRewind: ['-1/2', '-1']
		* }
		* @public
		*/
		playbackRateHash: {
			fastForward: ['2', '4', '8', '16'],
			rewind: ['-2', '-4', '-8', '-16'],
			slowForward: ['1/4', '1/2', '1'],
			slowRewind: ['-1/2', '-1']
		}
	},

	/**
	* @private
	*/
	events: {
		onFastforward: '',
		onSlowforward: '',
		onRewind: '',
		onSlowrewind: '',
		onJumpForward: '',
		onJumpBackward: '',
		onPlay: '',
		onStart: ''
	},

	/**
	* @private
	*/
	handlers: {
		//* Catch video _loadedmetadata_ event
		onloadedmetadata: 'metadataLoaded',
		ontimeupdate: 'timeupdate',
		onratechange: 'ratechange',
		onplay: '_play',
		onChangeSource: 'load'
	},

	/**
	* @private
	*/
	observers: {
		updateSource: ['src', 'sourceComponents']
	},

	/**
	* @private
	*/
	tag: 'video',

	defaultKind: MediaSource,

	/**
	* @private
	*/
	_playbackRateArray: null,

	/**
	* @private
	*/
	_speedIndex: 0,

	/**
	* @method
	* @private
	*/
	create: kind.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			this.posterChanged();
			this.showControlsChanged();
			this.preloadChanged();
			this.autoplayChanged();
			this.loopChanged();
			// If no source has be specified, <source> elements may have been added directly
			// to the components block so skip updating sources to avoid erasing those
			// components.
			if(this.src || this.sourceComponents) {
				this.updateSource();
			}
		};
	}),

	/**
	* @method
	* @private
	*/
	rendered: kind.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			this.hookupVideoEvents();
		};
	}),

	/**
	* @method
	* @private
	*/
	updateSource: function (old, value, source) {
		var src = this.get('src');
		var sources = this.get('sourceComponents');

		// if called due to a property change, clear the other property
		if(source === 'src' || (!source && src)) {
			this.sourceComponents = null;
			sources = [{src: src}];
		} else if(source === 'sourceComponents' || (!source && sources)) {
			src = this.src = '';
			if (!!this.getAttribute('src')) {
				this.setAttribute('src', '');
			}
		}

		// Always wipe out any previous sources before setting src or new sources
		this.destroyClientControls();
		if(sources) {
			this.createComponents(sources);
			if(this.hasNode()) {
				this.render();
			}
		}

		this.load();
	},

	/**
	* @private
	*/
	posterChanged: function () {
		if (this.poster) {
			var p = path.rewrite(this.poster);
			this.setAttribute('poster', p);
		}
		else {
			this.setAttribute('poster', null);
		}
	},

	/**
	* @private
	*/
	showControlsChanged: function () {
		this.setAttribute('controls', this.showControls ? 'controls' : null);
	},

	/**
	* @private
	*/
	preloadChanged: function () {
		this.setAttribute('preload', this.preload ? this.preload : null);
	},

	/**
	* @private
	*/
	autoplayChanged: function () {
		this.setAttribute('autoplay', this.autoplay ? 'autoplay' : null);
		this._prevCommand = this.autoplay ? 'play' : 'pause';
	},

	/**
	* @private
	*/
	loopChanged: function () {
		this.setAttribute('loop', this.loop ? 'loop' : null);
	},

	/**
	* @private
	*/
	fitToWindowChanged: function () {
		if (!this.hasNode()) {
			return;
		}
	},
	
	/**
	* Loads the current video [source]{@link enyo.Video#src}.
	* 
	* @public
	*/
	load: function () {
		if(this.hasNode()) { this.hasNode().load(); }
	},

	/**
	* Unloads the current video [source]{@link enyo.Video#src}, stopping all
	* playback and buffering.
	* 
	* @public
	*/
	unload: function() {
		this.src ='';
		this.sourceComponents = null;
		this.setAttribute('src', '');
		this.destroyClientControls();
		this.load();
	},

	/**
	* Initiates playback of the video data.
	* 
	* @public
	*/
	play: function () {
		if (!this.hasNode()) {
			return;
		}
		this._speedIndex = 0;
		this.setPlaybackRate(1);
		this.node.play();
		this._prevCommand = 'play';
	},

	/**
	* Pauses video playback.
	* 
	* @public
	*/
	pause: function () {
		if (!this.hasNode()) {
			return;
		}
		this._speedIndex = 0;
		this.setPlaybackRate(1);
		this.node.pause();
		this._prevCommand = 'pause';
	},

	/**
	* Changes the playback speed via [selectPlaybackRate()]{@link enyo.Video#selectPlaybackRate}.
	*
	* @public
	*/
	fastForward: function () {
		var node = this.hasNode(),
			isNeedPlay = false;

		if (!node) {
			return;
		}
		switch (this._prevCommand) {
		case 'slowForward':
			if (this._speedIndex == this._playbackRateArray.length - 1) {
				// reached to the end of array => go to fastforward
				this.selectPlaybackRateArray('fastForward');
				this._speedIndex = 0;
				this._prevCommand = 'fastForward';
			} else {
				this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
				this._prevCommand = 'slowForward';
			}
			break;
		case 'pause':
			this.selectPlaybackRateArray('slowForward');
			this._speedIndex = 0;
			if (this.isPaused()) {
				isNeedPlay = true;
			}
			this._prevCommand = 'slowForward';
			break;
		case 'rewind':
			var pbNumber = this.calcNumberValueOfPlaybackRate(this.playbackRate);
			if (pbNumber < 0) {
				this.selectPlaybackRateArray('slowForward');
				this._prevCommand = 'slowForward';
			} else {
				this.selectPlaybackRateArray('fastForward');
				this._prevCommand = 'fastForward';
			}
			this._speedIndex = 0;
			break;
		case 'fastForward':
			this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
			this._prevCommand = 'fastForward';
			break;
		default:
			this.selectPlaybackRateArray('fastForward');
			this._speedIndex = 0;
			this._prevCommand = 'fastForward';
			break;
		}

		this.setPlaybackRate(this.selectPlaybackRate(this._speedIndex));

		isNeedPlay && node.play();
	},

	/**
	* Changes the playback speed via [selectPlaybackRate()]{@link enyo.Video#selectPlaybackRate}.
	* 
	* @public
	*/
	rewind: function () {
		var node = this.hasNode(),
			isNeedPlay = false;
		if (!node) {
			return;
		}
		switch (this._prevCommand) {
		case 'slowRewind':
			if (this._speedIndex == this._playbackRateArray.length - 1) {
				// reached to the end of array => go to rewind
				this.selectPlaybackRateArray('rewind');
				this._speedIndex = 0;
				this._prevCommand = 'rewind';
			} else {
				this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
				this._prevCommand = 'slowRewind';
			}
			break;
		case 'pause':
			this.selectPlaybackRateArray('slowRewind');
			this._speedIndex = 0;
			if (this.isPaused() && this.node.duration > this.node.currentTime) {
				isNeedPlay = true;
			}
			this._prevCommand = 'slowRewind';
			break;
		case 'rewind':
			this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
			this._prevCommand = 'rewind';
			break;
		default:
			this.selectPlaybackRateArray('rewind');
			this._speedIndex = 0;
			this._prevCommand = 'rewind';
			break;
		}


		this.setPlaybackRate(this.selectPlaybackRate(this._speedIndex));

		isNeedPlay && node.play();
	},

	/**
	* Jumps backward [jumpSec]{@link enyo.Video#jumpSec} seconds from the current time.
	*
	* @fires enyo.Video#doJumpBackward
	* @public
	*/
	jumpBackward: function () {
		var node = this.hasNode();

		if (!node) {
			return;
		}

		this.setPlaybackRate(1);
		node.currentTime -= this.jumpSec;
		this._prevCommand = 'jumpBackward';

		this.doJumpBackward(utils.mixin(this.createEventData(), {jumpSize: this.jumpSec}));
	},

	/**
	* Jumps forward [jumpSec]{@link enyo.Video#jumpSec} seconds from the current time.<commit_msg>Revert "ENYO-1725:  enyo.Video: setPlaybackRate should be called ahead of play in a certain case"<commit_after>require('enyo');

/**
* Contains the declaration for the {@link enyo.Video} kind.
* @module enyo/Video
*/

var
	kind = require('./kind'),
	dispatcher = require('./dispatcher'),
	path = require('./path'),
	platform = require('./platform'),
	utils = require('./utils');
var
	Control = require('./Control'),
	MediaSource = require('./MediaSource'),
	Job = require('./job');

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to an integer greater than `1`.
*
* @event enyo.Video#onFastforward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to a value between `0` and `1`.
*
* @event enyo.Video#onSlowforward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to an integer less than `-1`.
*
* @event enyo.Video#onRewind
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [playbackRate]{@link enyo.Video#playbackRate} is changed to a value less than `0`
* but greater than or equal to `-1`.
*
* @event enyo.Video#onSlowrewind
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [jumpForward()]{@link enyo.Video#jumpForward} is called.
*
* @event enyo.Video#onJumpForward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when [jumpBackward()]{@link enyo.Video#jumpBackward} is called.
*
* @event enyo.Video#onJumpBackward
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when EventData is changed.
*
* @event enyo.Video#onPlay
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* Fires when the [image]{@link enyo.Image} has loaded.
*
* @event enyo.Video#onStart
* @type {Object}
* @property {Object} sender - The [component]{@link enyo.Component} that most recently
*	propagated the {@glossary event}.
* @property {Object} event - An [object]{@glossary Object} containing event information.
* @public
*/

/**
* {@link enyo.Video} is a [control]{@link enyo.Control} that allows you to play video.
* It is an abstraction of HTML 5 [Video]{@glossary video}.
*
* Initialize a video [component]{@link enyo.Component} as follows:
*
* ```
* {kind: 'Video', src: 'http://www.w3schools.com/html/movie.mp4'}
* ```
* 
* To play a video, call `this.$.video.play()`.
* 
* To get a reference to the actual HTML 5 Video element, call `this.$.video.hasNode()`.
*
* @namespace enyo
* @class enyo.Video
* @extends enyo.Control
* @ui
* @definedby module:enyo/Video
* @public
*/
module.exports = kind(
	/** @lends enyo.Video.prototype */ {

	/**
	* @private
	*/
	name: 'enyo.Video',

	/**
	* @private
	*/
	kind: Control,

	/**
	* @private
	*/
	published: 
		/** @lends enyo.Video.prototype */ {

		/**
		* Source URL of the video file; may be relative to the application's HTML file.
		* 
		* @type {String}
		* @default ''
		* @public
		*/
		src: '',

		/**
		* An [object]{@glossary Object} that may be used to specify multiple sources for the
		* same video file.
		* 
		* @type {Object}
		* @default null
		* @public
		*/
		sourceComponents: null,

		/**
		* Source of image file to show when video is not available.
		* 
		* @type {String}
		* @default ''
		* @public
		*/
		poster: '',

		/**
		* If `true`, controls for starting and stopping the video player are shown.
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		showControls: false,

		/**
		* Determines how (or whether) the {@link enyo.Video} object is preloaded.
		* Possible values:
		* - `'auto'`: Preload the video data as soon as possible.
		* - `'metadata'`: Preload only the video metadata.
		* - `'none'`: Do not preload any video data.
		* 
		* @type {String}
		* @default 'metadata'
		* @public
		*/
		preload: 'metadata',

		/**
		* If `true`, video will automatically start playing.
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		autoplay: false,

		/**
		* If `true`, when playback is finished, the video player will restart from
		* the beginning.
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		loop: false,

		/**
		* If `true`, video will be stretched to fill the entire window (webOS only).
		* 
		* @type {Boolean}
		* @default false
		* @public
		*/
		fitToWindow: false,

		/**
		* The video aspect ratio, expressed as `width:height`.
		* 
		* @type {Number}
		* @default 0
		* @public
		*/
		aspectRatio: null,

		/**
		* Number of seconds to jump forward or backward.
		* 
		* @type {Number}
		* @default 30
		* @public
		*/
		jumpSec: 30,

		/**
		* Video playback rate.
		* 
		* @type {Number}
		* @default 1
		* @public
		*/
		playbackRate: 1,

		/**
		* Mapping of playback rate names to playback rate values that may be set.
		* ```
		* {
		*	fastForward: ['2', '4', '8', '16'],
		*	rewind: ['-2', '-4', '-8', '-16'],
		*	slowForward: ['1/4', '1/2', '1'],
		*	slowRewind: ['-1/2', '-1']
		* }
		* ```
		* 
		* @type {Object}
		* @default {
		*	fastForward: ['2', '4', '8', '16'],
		*	rewind: ['-2', '-4', '-8', '-16'],
		*	slowForward: ['1/4', '1/2', '1'],
		*	slowRewind: ['-1/2', '-1']
		* }
		* @public
		*/
		playbackRateHash: {
			fastForward: ['2', '4', '8', '16'],
			rewind: ['-2', '-4', '-8', '-16'],
			slowForward: ['1/4', '1/2', '1'],
			slowRewind: ['-1/2', '-1']
		}
	},

	/**
	* @private
	*/
	events: {
		onFastforward: '',
		onSlowforward: '',
		onRewind: '',
		onSlowrewind: '',
		onJumpForward: '',
		onJumpBackward: '',
		onPlay: '',
		onStart: ''
	},

	/**
	* @private
	*/
	handlers: {
		//* Catch video _loadedmetadata_ event
		onloadedmetadata: 'metadataLoaded',
		ontimeupdate: 'timeupdate',
		onratechange: 'ratechange',
		onplay: '_play',
		onChangeSource: 'load'
	},

	/**
	* @private
	*/
	observers: {
		updateSource: ['src', 'sourceComponents']
	},

	/**
	* @private
	*/
	tag: 'video',

	defaultKind: MediaSource,

	/**
	* @private
	*/
	_playbackRateArray: null,

	/**
	* @private
	*/
	_speedIndex: 0,

	/**
	* @method
	* @private
	*/
	create: kind.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			this.posterChanged();
			this.showControlsChanged();
			this.preloadChanged();
			this.autoplayChanged();
			this.loopChanged();
			// If no source has be specified, <source> elements may have been added directly
			// to the components block so skip updating sources to avoid erasing those
			// components.
			if(this.src || this.sourceComponents) {
				this.updateSource();
			}
		};
	}),

	/**
	* @method
	* @private
	*/
	rendered: kind.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			this.hookupVideoEvents();
		};
	}),

	/**
	* @method
	* @private
	*/
	updateSource: function (old, value, source) {
		var src = this.get('src');
		var sources = this.get('sourceComponents');

		// if called due to a property change, clear the other property
		if(source === 'src' || (!source && src)) {
			this.sourceComponents = null;
			sources = [{src: src}];
		} else if(source === 'sourceComponents' || (!source && sources)) {
			src = this.src = '';
			if (!!this.getAttribute('src')) {
				this.setAttribute('src', '');
			}
		}

		// Always wipe out any previous sources before setting src or new sources
		this.destroyClientControls();
		if(sources) {
			this.createComponents(sources);
			if(this.hasNode()) {
				this.render();
			}
		}

		this.load();
	},

	/**
	* @private
	*/
	posterChanged: function () {
		if (this.poster) {
			var p = path.rewrite(this.poster);
			this.setAttribute('poster', p);
		}
		else {
			this.setAttribute('poster', null);
		}
	},

	/**
	* @private
	*/
	showControlsChanged: function () {
		this.setAttribute('controls', this.showControls ? 'controls' : null);
	},

	/**
	* @private
	*/
	preloadChanged: function () {
		this.setAttribute('preload', this.preload ? this.preload : null);
	},

	/**
	* @private
	*/
	autoplayChanged: function () {
		this.setAttribute('autoplay', this.autoplay ? 'autoplay' : null);
		this._prevCommand = this.autoplay ? 'play' : 'pause';
	},

	/**
	* @private
	*/
	loopChanged: function () {
		this.setAttribute('loop', this.loop ? 'loop' : null);
	},

	/**
	* @private
	*/
	fitToWindowChanged: function () {
		if (!this.hasNode()) {
			return;
		}
	},
	
	/**
	* Loads the current video [source]{@link enyo.Video#src}.
	* 
	* @public
	*/
	load: function () {
		if(this.hasNode()) { this.hasNode().load(); }
	},

	/**
	* Unloads the current video [source]{@link enyo.Video#src}, stopping all
	* playback and buffering.
	* 
	* @public
	*/
	unload: function() {
		this.src ='';
		this.sourceComponents = null;
		this.setAttribute('src', '');
		this.destroyClientControls();
		this.load();
	},

	/**
	* Initiates playback of the video data.
	* 
	* @public
	*/
	play: function () {
		if (!this.hasNode()) {
			return;
		}
		this._speedIndex = 0;
		this.setPlaybackRate(1);
		this.node.play();
		this._prevCommand = 'play';
	},

	/**
	* Pauses video playback.
	* 
	* @public
	*/
	pause: function () {
		if (!this.hasNode()) {
			return;
		}
		this._speedIndex = 0;
		this.setPlaybackRate(1);
		this.node.pause();
		this._prevCommand = 'pause';
	},

	/**
	* Changes the playback speed via [selectPlaybackRate()]{@link enyo.Video#selectPlaybackRate}.
	*
	* @public
	*/
	fastForward: function () {
		var node = this.hasNode();

		if (!node) {
			return;
		}
		switch (this._prevCommand) {
		case 'slowForward':
			if (this._speedIndex == this._playbackRateArray.length - 1) {
				// reached to the end of array => go to fastforward
				this.selectPlaybackRateArray('fastForward');
				this._speedIndex = 0;
				this._prevCommand = 'fastForward';
			} else {
				this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
				this._prevCommand = 'slowForward';
			}
			break;
		case 'pause':
			this.selectPlaybackRateArray('slowForward');
			this._speedIndex = 0;
			if (this.isPaused()) {
				node.play();
			}
			this._prevCommand = 'slowForward';
			break;
		case 'rewind':
			var pbNumber = this.calcNumberValueOfPlaybackRate(this.playbackRate);
			if (pbNumber < 0) {
				this.selectPlaybackRateArray('slowForward');
				this._prevCommand = 'slowForward';
			} else {
				this.selectPlaybackRateArray('fastForward');
				this._prevCommand = 'fastForward';
			}
			this._speedIndex = 0;
			break;
		case 'fastForward':
			this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
			this._prevCommand = 'fastForward';
			break;
		default:
			this.selectPlaybackRateArray('fastForward');
			this._speedIndex = 0;
			this._prevCommand = 'fastForward';
			break;
		}

		this.setPlaybackRate(this.selectPlaybackRate(this._speedIndex));

	},

	/**
	* Changes the playback speed via [selectPlaybackRate()]{@link enyo.Video#selectPlaybackRate}.
	* 
	* @public
	*/
	rewind: function () {
		var node = this.hasNode();

		if (!node) {
			return;
		}
		switch (this._prevCommand) {
		case 'slowRewind':
			if (this._speedIndex == this._playbackRateArray.length - 1) {
				// reached to the end of array => go to rewind
				this.selectPlaybackRateArray('rewind');
				this._speedIndex = 0;
				this._prevCommand = 'rewind';
			} else {
				this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
				this._prevCommand = 'slowRewind';
			}
			break;
		case 'pause':
			this.selectPlaybackRateArray('slowRewind');
			this._speedIndex = 0;
			if (this.isPaused() && this.node.duration > this.node.currentTime) {
				node.play();
			}
			this._prevCommand = 'slowRewind';
			break;
		case 'rewind':
			this._speedIndex = this.clampPlaybackRate(this._speedIndex+1);
			this._prevCommand = 'rewind';
			break;
		default:
			this.selectPlaybackRateArray('rewind');
			this._speedIndex = 0;
			this._prevCommand = 'rewind';
			break;
		}


		this.setPlaybackRate(this.selectPlaybackRate(this._speedIndex));
	},

	/**
	* Jumps backward [jumpSec]{@link enyo.Video#jumpSec} seconds from the current time.
	*
	* @fires enyo.Video#doJumpBackward
	* @public
	*/
	jumpBackward: function () {
		var node = this.hasNode();

		if (!node) {
			return;
		}

		this.setPlaybackRate(1);
		node.currentTime -= this.jumpSec;
		this._prevCommand = 'jumpBackward';

		this.doJumpBackward(utils.mixin(this.createEventData(), {jumpSize: this.jumpSec}));
	},

	/**
	* Jumps forward [jumpSec]{@link enyo.Video#jumpSec} seconds from the current time.
{'fraction_non_alphanumeric': 0.16850405198218588, 'fraction_numerical': 0.006789807987150471, 'mean_word_length': 3.43069707261847, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6664351', 'n_tokens_mistral': 10629, 'n_tokens_neox': 9478, 'n_words': 2320}