<commit_before>/**
 * A media element corresponding to a <video> element.
 *
 * It is implemented as a collection of mediaSource objects. The media sources
 * will be initialized from the <video> element, its child <source> elements,
 * and/or the ROE file referenced by the <video> element.
 *
 * @param {element}
 *      videoElement <video> element used for initialization.
 * @constructor
 */
( function( mw, $ ) {

mw.MediaElement = function( element ) {
	this.init( element );
};

mw.MediaElement.prototype = {

	// The array of mediaSource elements.
	sources: null,

	// flag for ROE data being added.
	addedROEData: false,

	// Selected mediaSource element.
	selectedSource: null,

	/**
	 * Media Element constructor
	 *
	 * Sets up a mediaElement from a provided top level "video" element adds any
	 * child sources that are found
	 *
	 * @param {Element}
	 *      videoElement Element that has src attribute or has children
	 *      source elements
	 */
	init: function( videoElement ) {
		var _this = this;
		mw.log( "EmbedPlayer::mediaElement:init:" + videoElement.id );
		this.parentEmbedId = videoElement.id;
		this.sources = new Array();

		// Process the videoElement as a source element:
		if( videoElement ){
			if ( $( videoElement ).attr( "src" ) ) {
				_this.tryAddSource( videoElement );
			}
			// Process elements source children
			$( videoElement ).find( 'source,track' ).each( function( ) {
				_this.tryAddSource( this );
			} );
		}
	},

	/**
	 * Updates the time request for all sources that have a standard time
	 * request argument (ie &t=start_time/end_time)
	 *
	 * @param {String}
	 *      startNpt Start time in npt format
	 * @param {String}
	 *      endNpt End time in npt format
	 */
	updateSourceTimes: function( startNpt, endNpt ) {
		var _this = this;
		$.each( this.sources, function( inx, mediaSource ) {
			mediaSource.updateSrcTime( startNpt, endNpt );
		} );
	},

	/**
	 * Get Text tracks
	 */
	getTextTracks: function(){
		var textTracks = [];
		$.each( this.sources, function(inx, source ){
			if (  source.nodeName == 'track' || ( source.mimeType && source.mimeType.indexOf('text/') !== -1 )){
				textTracks.push( source );
			}
		});
		return textTracks;
	},
	/**
	 * Returns the array of mediaSources of this element.
	 *
	 * @param {String}
	 *      [mimeFilter] Filter criteria for set of mediaSources to return
	 * @return {Array} mediaSource elements.
	 */
	getSources: function( mimeFilter ) {
		if ( !mimeFilter ) {
			return this.sources;
		}
		// Apply mime filter:
		var source_set = new Array();
		for ( var i = 0; i < this.sources.length ; i++ ) {
			if ( this.sources[i].mimeType &&
				 this.sources[i].mimeType.indexOf( mimeFilter ) != -1 )
			{
				source_set.push( this.sources[i] );
			}
		}
		return source_set;
	},

	/**
	 * Selects a source by id
	 *
	 * @param {String}
	 *      sourceId Id of the source to select.
	 * @return {MediaSource} The selected mediaSource or null if not found
	 */
	getSourceById:function( sourceId ) {
		for ( var i = 0; i < this.sources.length ; i++ ) {
			if ( this.sources[i].id == sourceId ) {
				return this.sources[i];
			}
		}
		return null;
	},

	/**
	 * Selects a particular source for playback updating the "selectedSource"
	 *
	 * @param {Number}
	 *      index Index of source element to set as selectedSource
	 */
	setSourceByIndex: function( index ) {
		mw.log( 'EmbedPlayer::mediaElement:selectSource: ' + index );
		var oldSrc = this.selectedSource.getSrc();
		var playableSources = this.getPlayableSources();
		for ( var i = 0; i < playableSources.length; i++ ) {
			if ( i == index ) {
				this.selectedSource = playableSources[i];
				break;
			}
		}
		if( oldSrc !=  this.selectedSource.getSrc() ){
			$( '#' + this.parentEmbedId ).trigger( 'SourceChange');
		}
	},
	/**
	 * Sets a the selected source to passed in source object
	 * @param {Object} Source
	 */
	setSource: function( source ){
		var oldSrc = this.selectedSource.getSrc();
		this.selectedSource = source;
		if( oldSrc !=  this.selectedSource.getSrc() ){
			$( '#' + this.parentEmbedId ).trigger( 'SourceChange');
		}
	},

	/**
	 * Selects the default source via cookie preference, default marked, or by
	 * id order
	 */
	autoSelectSource: function() {
		mw.log( 'EmbedPlayer::mediaElement::autoSelectSource' );
		var _this = this;
		// Select the default source
		var playableSources = this.getPlayableSources();
		var flash_flag = ogg_flag = false;

		// Check if there are any playableSources
		if( playableSources.length == 0 ){
			return false;
		}
		var setSelectedSource = function( source ){
			_this.selectedSource = source;
			return _this.selectedSource;
		};
		// Set via user-preference
		$.each( playableSources, function( inx, source ){
			var mimeType =source.mimeType;
			if ( mw.EmbedTypes.getMediaPlayers().preference[ 'format_preference' ] == mimeType ) {
				 mw.log( 'MediaElement::autoSelectSource: Set via format_preference: ' + source.mimeType );
				 return setSelectedSource( source );
			}
		});

		// Set via module driven preference:
		$( this ).trigger( 'AutoSelectSource', playableSources );

		if( _this.selectedSource ){
			mw.log('MediaElement::autoSelectSource: Set via trigger::' + _this.selectedSource.getTitle() );
			return _this.selectedSource;
		}

		// Set via marked default:
		$.each( playableSources, function( inx, source ){
			if ( source.markedDefault ) {
				mw.log( 'MediaElement::autoSelectSource: Set via marked default: ' + source.markedDefault );
				return setSelectedSource( source );;
			}
		});

		// Set apple adaptive ( if available )
		var vndSources = this.getPlayableSources('application/vnd.apple.mpegurl')
		if( vndSources.length && mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'application/vnd.apple.mpegurl' ).length ){
			// Check for device flags:
			var desktopVdn, mobileVdn;
			$.each( vndSources, function( inx, source) {
				// kaltura tags vdn sources with iphonenew
				if( source.getFlavorId() && source.getFlavorId().toLowerCase() == 'iphonenew' ){
					mobileVdn = source;
				} else {
					desktopVdn = source;
				}
			})
			// NOTE: We really should not have two vdn sources the point of vdn is to be a set of adaptive streams.
			// This work around is a result of kaltura HLS stream tagging
			if( mw.isIphone() && mobileVdn ){
				setSelectedSource( mobileVdn );
			} else if( desktopVdn ){
				setSelectedSource( desktopVdn );
			}
		}
		if ( this.selectedSource ) {
			mw.log('MediaElement::autoSelectSource: Set via Adaptive HLS: source flavor id:' + _this.selectedSource.getFlavorId() + ' src: ' + _this.selectedSource.getSrc() );
			return this.selectedSource;
		}

		//Set via user bandwidth pref will always set source to closest bandwidth allocation while not going over  EmbedPlayer.UserBandwidth
		if( $.cookie('EmbedPlayer.UserBandwidth') ){
			var currentMaxBadwith = 0;
			$.each( playableSources, function(inx, source ){
				if( source.bandwidth ){
					if( source.bandwidth > currentMaxBadwith && source.bandwidth <= $.cookie('EmbedPlayer.UserBandwidth') ){
						currentMaxBadwith = source.bandwidth;
						setSelectedSource( source );
					}
				}
			});
		}
		if ( this.selectedSource ) {
			mw.log('MediaElement::autoSelectSource: Set via bandwidth prefrence: source ' + source.bandwidth + ' user: ' + $.cookie('EmbedPlayer.UserBandwidth') );
			return this.selectedSource;
		}

		// If we have at least one native source, throw out non-native sources
		// for size based source selection:
		var nativePlayableSources = [];
		$.each( playableSources, function(inx, source ){
			var mimeType = source.mimeType;
			var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
			if ( player && player.library == 'Native'	) {
				nativePlayableSources.push( source );
			}
		});

		// if we have native sources to chouse from chouse based on embed size:
		if( nativePlayableSources.length ){
			// Set via embed resolution closest to relative to display size
			var minSizeDelta = null;
			if( this.parentEmbedId ){
				var displayWidth = $('#' + this.parentEmbedId).width();
				$.each( nativePlayableSources, function(inx, source ){
					if( source.width && displayWidth ){
						var sizeDelta =  Math.abs( source.width - displayWidth );
						mw.log('MediaElement::autoSelectSource: size delta : ' + sizeDelta + ' for s:' + source.width );
						if( minSizeDelta == null ||  sizeDelta < minSizeDelta){
							minSizeDelta = sizeDelta;
							setSelectedSource( source );
						}
					}
				});
			}
		}
		// If we found a source via display resolution return true
		if ( this.selectedSource ) {
			mw.log('MediaElement::autoSelectSource: Set via embed resolution:' + this.selectedSource.width + ' close to: ' + displayWidth );
			return this.selectedSource;
		}

		// Prefer native playback ( and prefer WebM over ogg and h.264 )
		var namedSources = {};
		$.each( playableSources, function(inx, source ){
			var mimeType = source.mimeType;
			var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
			if ( player && player.library == 'Native'	) {
				switch( player.id	){
					case 'mp3Native':
						namedSources['mp3'] = source;
						break;
					case 'oggNative':
						namedSources['ogg'] = source;
						break;
					case 'webmNative':
						namedSources['webm'] = source;
						break;
					case 'h264Native':
						namedSources['h264'] = source;
						break;
					case 'appleVdn':
						namedSources['appleVdn'] = source;
						break;
				}
			}
		});

		var codecPref = mw.getConfig( 'EmbedPlayer.CodecPreference');
		for(var i =0; i < codecPref.length; i++){
			var codec = codecPref[ i ];
			if( namedSources[ codec ]){
				mw.log('MediaElement::autoSelectSource: set via EmbedPlayer.CodecPreference: ' + namedSources[ codec ].getTitle() );
				return setSelectedSource( namedSources[ codec ] );
			}
		};

		// Set h264 via native or flash fallback
		$.each( playableSources, function(inx, source ){
			var mimeType = source.mimeType;
			var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
			if ( mimeType == 'video/h264'
				&& player
				&& (
					player.library == 'Native'
					||
					player.library == 'Kplayer'
				)
			) {
				if( source ){
					mw.log('MediaElement::autoSelectSource: Set h264 via native or flash fallback:' + source.getTitle() );
					return setSelectedSource( source );
				}
			}
		});

		// Else just select the first playable source
		if ( !this.selectedSource && playableSources[0] ) {
			mw.log( 'MediaElement::autoSelectSource: Set via first source: ' + playableSources[0].getTitle() + ' mime: ' + playableSources[0].getMIMEType() );
			return setSelectedSource( playableSources[0] );
		}
		// No Source found so no source selected
		return false;
	},

	/**
	 * check if the mime is ogg
	 */
	isOgg: function( mimeType ){
		if ( mimeType == 'video/ogg'
			|| mimeType == 'ogg/video'
			|| mimeType == 'video/annodex'
			|| mimeType == 'application/ogg'
		) {
			return true;
		}
		return false;
	},

	/**
	 * Returns the thumbnail URL for the media element.
	 *
	 * @returns {String} thumbnail URL
	 */
	getPosterSrc: function( ) {
		return this.poster;
	},

	/**
	 * Checks whether there is a stream of a specified MIME type.
	 *
	 * @param {String}
	 *      mimeType MIME type to check.
	 * @return {Boolean} true if sources include MIME false if not.
	 */
	hasStreamOfMIMEType: function( mimeType )
	{
		for ( var i = 0; i < this.sources.length; i++ )
		{
			if ( this.sources[i].getMIMEType() == mimeType ){
				return true;
			}
		}
		return false;
	},

	/**
	 * Checks if media is a playable type
	 */
	isPlayableType: function( mimeType ) {
		//	mw.log("isPlayableType:: " + mimeType);
		if ( mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType ) ) {
			return true;
		} else {
			return false;
		}
	},

	/**
	 * Adds a single mediaSource using the provided element if the element has a
	 * 'src' attribute.
	 *
	 * @param {Element}
	 *      element <video>, <source> or <mediaSource> <text> element.
	 */
	tryAddSource: function( element ) {
		//mw.log( 'mw.MediaElement::tryAddSource:' + $( element ).attr( "src" ) );
		var newSrc = $( element ).attr( 'src' );
		if ( newSrc ) {
			// Make sure an existing element with the same src does not already exist:
			for ( var i = 0; i < this.sources.length; i++ ) {
				if ( this.sources[i].src == newSrc ) {
					// Source already exists update any new attr:
					this.sources[i].updateSource( element );
					return this.sources[i];
				}
			}
		}
		// Create a new source
		var source = new mw.MediaSource( element );

		this.sources.push( source );
		//mw.log( 'tryAddSource: added source ::' + source + 'sl:' + this.sources.length );
		return source;
	},

	/**
	 * Get playable sources
	 *
	 *@pram mimeFilter {=string} (optional) Filter the playable sources set by mime filter
	 *
	 * @returns {Array} of playable media sources
	 */
	getPlayableSources: function( mimeFilter ) {
		 var playableSources = [];
		 for ( var i = 0; i < this.sources.length; i++ ) {
			 if ( this.isPlayableType( this.sources[i].mimeType )
					 &&
				( !mimeFilter || this.sources[i].mimeType.indexOf( mimeFilter) != -1  )
			){
				 playableSources.push( this.sources[i] );
			}
		 };
		 mw.log( "MediaElement::GetPlayableSources playable "+ playableSources.length + ' sources playable out of ' +  this.sources.length  + " mimeFilter:" + mimeFilter );
		 return playableSources;
	}
};

} )( mediaWiki, jQuery );

<commit_msg>commented out broken user preference code.. needs to be cleaned up with a more structured preference , i.e { 'size': 480P, 'format': 'video/h.264'} <commit_after>/**
 * A media element corresponding to a <video> element.
 *
 * It is implemented as a collection of mediaSource objects. The media sources
 * will be initialized from the <video> element, its child <source> elements,
 * and/or the ROE file referenced by the <video> element.
 *
 * @param {element}
 *      videoElement <video> element used for initialization.
 * @constructor
 */
( function( mw, $ ) {

mw.MediaElement = function( element ) {
	this.init( element );
};

mw.MediaElement.prototype = {

	// The array of mediaSource elements.
	sources: null,

	// flag for ROE data being added.
	addedROEData: false,

	// Selected mediaSource element.
	selectedSource: null,

	/**
	 * Media Element constructor
	 *
	 * Sets up a mediaElement from a provided top level "video" element adds any
	 * child sources that are found
	 *
	 * @param {Element}
	 *      videoElement Element that has src attribute or has children
	 *      source elements
	 */
	init: function( videoElement ) {
		var _this = this;
		mw.log( "EmbedPlayer::mediaElement:init:" + videoElement.id );
		this.parentEmbedId = videoElement.id;
		this.sources = new Array();

		// Process the videoElement as a source element:
		if( videoElement ){
			if ( $( videoElement ).attr( "src" ) ) {
				_this.tryAddSource( videoElement );
			}
			// Process elements source children
			$( videoElement ).find( 'source,track' ).each( function( ) {
				_this.tryAddSource( this );
			} );
		}
	},

	/**
	 * Updates the time request for all sources that have a standard time
	 * request argument (ie &t=start_time/end_time)
	 *
	 * @param {String}
	 *      startNpt Start time in npt format
	 * @param {String}
	 *      endNpt End time in npt format
	 */
	updateSourceTimes: function( startNpt, endNpt ) {
		var _this = this;
		$.each( this.sources, function( inx, mediaSource ) {
			mediaSource.updateSrcTime( startNpt, endNpt );
		} );
	},

	/**
	 * Get Text tracks
	 */
	getTextTracks: function(){
		var textTracks = [];
		$.each( this.sources, function(inx, source ){
			if (  source.nodeName == 'track' || ( source.mimeType && source.mimeType.indexOf('text/') !== -1 )){
				textTracks.push( source );
			}
		});
		return textTracks;
	},
	/**
	 * Returns the array of mediaSources of this element.
	 *
	 * @param {String}
	 *      [mimeFilter] Filter criteria for set of mediaSources to return
	 * @return {Array} mediaSource elements.
	 */
	getSources: function( mimeFilter ) {
		if ( !mimeFilter ) {
			return this.sources;
		}
		// Apply mime filter:
		var source_set = new Array();
		for ( var i = 0; i < this.sources.length ; i++ ) {
			if ( this.sources[i].mimeType &&
				 this.sources[i].mimeType.indexOf( mimeFilter ) != -1 )
			{
				source_set.push( this.sources[i] );
			}
		}
		return source_set;
	},

	/**
	 * Selects a source by id
	 *
	 * @param {String}
	 *      sourceId Id of the source to select.
	 * @return {MediaSource} The selected mediaSource or null if not found
	 */
	getSourceById:function( sourceId ) {
		for ( var i = 0; i < this.sources.length ; i++ ) {
			if ( this.sources[i].id == sourceId ) {
				return this.sources[i];
			}
		}
		return null;
	},

	/**
	 * Selects a particular source for playback updating the "selectedSource"
	 *
	 * @param {Number}
	 *      index Index of source element to set as selectedSource
	 */
	setSourceByIndex: function( index ) {
		mw.log( 'EmbedPlayer::mediaElement:selectSource: ' + index );
		var oldSrc = this.selectedSource.getSrc();
		var playableSources = this.getPlayableSources();
		for ( var i = 0; i < playableSources.length; i++ ) {
			if ( i == index ) {
				this.selectedSource = playableSources[i];
				break;
			}
		}
		if( oldSrc !=  this.selectedSource.getSrc() ){
			$( '#' + this.parentEmbedId ).trigger( 'SourceChange');
		}
	},
	/**
	 * Sets a the selected source to passed in source object
	 * @param {Object} Source
	 */
	setSource: function( source ){
		var oldSrc = this.selectedSource.getSrc();
		this.selectedSource = source;
		if( oldSrc !=  this.selectedSource.getSrc() ){
			$( '#' + this.parentEmbedId ).trigger( 'SourceChange');
		}
	},

	/**
	 * Selects the default source via cookie preference, default marked, or by
	 * id order
	 */
	autoSelectSource: function() {
		mw.log( 'EmbedPlayer::mediaElement::autoSelectSource' );
		var _this = this;
		// Select the default source
		var playableSources = this.getPlayableSources();
		var flash_flag = ogg_flag = false;

		// Check if there are any playableSources
		if( playableSources.length == 0 ){
			return false;
		}
		var setSelectedSource = function( source ){
			_this.selectedSource = source;
			return _this.selectedSource;
		};
		// TODO fix user pref
		/*$.each( playableSources, function( inx, source ){
			var mimeType = source.mimeType;
			var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
			if ( mw.EmbedTypes.getMediaPlayers().preference[ mimeType ] == player.id ) {
				 mw.log( 'MediaElement::autoSelectSource: Set via formatPreference ' + mimeType + ' matchs default player:' + player.id  );
				 return setSelectedSource( source );
			}
		});*/

		// Set via module driven preference:
		$( this ).trigger( 'AutoSelectSource', playableSources );

		if( _this.selectedSource ){
			mw.log('MediaElement::autoSelectSource: Set via trigger::' + _this.selectedSource.getTitle() );
			return _this.selectedSource;
		}

		// Set via marked default:
		$.each( playableSources, function( inx, source ){
			if ( source.markedDefault ) {
				mw.log( 'MediaElement::autoSelectSource: Set via marked default: ' + source.markedDefault );
				return setSelectedSource( source );;
			}
		});

		// Set apple adaptive ( if available )
		var vndSources = this.getPlayableSources('application/vnd.apple.mpegurl')
		if( vndSources.length && mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'application/vnd.apple.mpegurl' ).length ){
			// Check for device flags:
			var desktopVdn, mobileVdn;
			$.each( vndSources, function( inx, source) {
				// kaltura tags vdn sources with iphonenew
				if( source.getFlavorId() && source.getFlavorId().toLowerCase() == 'iphonenew' ){
					mobileVdn = source;
				} else {
					desktopVdn = source;
				}
			})
			// NOTE: We really should not have two vdn sources the point of vdn is to be a set of adaptive streams.
			// This work around is a result of kaltura HLS stream tagging
			if( mw.isIphone() && mobileVdn ){
				setSelectedSource( mobileVdn );
			} else if( desktopVdn ){
				setSelectedSource( desktopVdn );
			}
		}
		if ( this.selectedSource ) {
			mw.log('MediaElement::autoSelectSource: Set via Adaptive HLS: source flavor id:' + _this.selectedSource.getFlavorId() + ' src: ' + _this.selectedSource.getSrc() );
			return this.selectedSource;
		}

		//Set via user bandwidth pref will always set source to closest bandwidth allocation while not going over  EmbedPlayer.UserBandwidth
		if( $.cookie('EmbedPlayer.UserBandwidth') ){
			var currentMaxBadwith = 0;
			$.each( playableSources, function(inx, source ){
				if( source.bandwidth ){
					if( source.bandwidth > currentMaxBadwith && source.bandwidth <= $.cookie('EmbedPlayer.UserBandwidth') ){
						currentMaxBadwith = source.bandwidth;
						setSelectedSource( source );
					}
				}
			});
		}
		if ( this.selectedSource ) {
			mw.log('MediaElement::autoSelectSource: Set via bandwidth prefrence: source ' + source.bandwidth + ' user: ' + $.cookie('EmbedPlayer.UserBandwidth') );
			return this.selectedSource;
		}

		// If we have at least one native source, throw out non-native sources
		// for size based source selection:
		var nativePlayableSources = [];
		$.each( playableSources, function(inx, source ){
			var mimeType = source.mimeType;
			var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
			if ( player && player.library == 'Native'	) {
				nativePlayableSources.push( source );
			}
		});

		// if we have native sources to chouse from chouse based on embed size:
		if( nativePlayableSources.length ){
			// Set via embed resolution closest to relative to display size
			var minSizeDelta = null;
			if( this.parentEmbedId ){
				var displayWidth = $('#' + this.parentEmbedId).width();
				$.each( nativePlayableSources, function(inx, source ){
					if( source.width && displayWidth ){
						var sizeDelta =  Math.abs( source.width - displayWidth );
						mw.log('MediaElement::autoSelectSource: size delta : ' + sizeDelta + ' for s:' + source.width );
						if( minSizeDelta == null ||  sizeDelta < minSizeDelta){
							minSizeDelta = sizeDelta;
							setSelectedSource( source );
						}
					}
				});
			}
		}
		// If we found a source via display resolution return true
		if ( this.selectedSource ) {
			mw.log('MediaElement::autoSelectSource: Set via embed resolution:' + this.selectedSource.width + ' close to: ' + displayWidth );
			return this.selectedSource;
		}

		// Prefer native playback ( and prefer WebM over ogg and h.264 )
		var namedSources = {};
		$.each( playableSources, function(inx, source ){
			var mimeType = source.mimeType;
			var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
			if ( player && player.library == 'Native'	) {
				switch( player.id	){
					case 'mp3Native':
						namedSources['mp3'] = source;
						break;
					case 'oggNative':
						namedSources['ogg'] = source;
						break;
					case 'webmNative':
						namedSources['webm'] = source;
						break;
					case 'h264Native':
						namedSources['h264'] = source;
						break;
					case 'appleVdn':
						namedSources['appleVdn'] = source;
						break;
				}
			}
		});

		var codecPref = mw.getConfig( 'EmbedPlayer.CodecPreference');
		for(var i =0; i < codecPref.length; i++){
			var codec = codecPref[ i ];
			if( namedSources[ codec ]){
				mw.log('MediaElement::autoSelectSource: set via EmbedPlayer.CodecPreference: ' + namedSources[ codec ].getTitle() );
				return setSelectedSource( namedSources[ codec ] );
			}
		};

		// Set h264 via native or flash fallback
		$.each( playableSources, function(inx, source ){
			var mimeType = source.mimeType;
			var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
			if ( mimeType == 'video/h264'
				&& player
				&& (
					player.library == 'Native'
					||
					player.library == 'Kplayer'
				)
			) {
				if( source ){
					mw.log('MediaElement::autoSelectSource: Set h264 via native or flash fallback:' + source.getTitle() );
					return setSelectedSource( source );
				}
			}
		});

		// Else just select the first playable source
		if ( !this.selectedSource && playableSources[0] ) {
			mw.log( 'MediaElement::autoSelectSource: Set via first source: ' + playableSources[0].getTitle() + ' mime: ' + playableSources[0].getMIMEType() );
			return setSelectedSource( playableSources[0] );
		}
		// No Source found so no source selected
		return false;
	},

	/**
	 * check if the mime is ogg
	 */
	isOgg: function( mimeType ){
		if ( mimeType == 'video/ogg'
			|| mimeType == 'ogg/video'
			|| mimeType == 'video/annodex'
			|| mimeType == 'application/ogg'
		) {
			return true;
		}
		return false;
	},

	/**
	 * Returns the thumbnail URL for the media element.
	 *
	 * @returns {String} thumbnail URL
	 */
	getPosterSrc: function( ) {
		return this.poster;
	},

	/**
	 * Checks whether there is a stream of a specified MIME type.
	 *
	 * @param {String}
	 *      mimeType MIME type to check.
	 * @return {Boolean} true if sources include MIME false if not.
	 */
	hasStreamOfMIMEType: function( mimeType )
	{
		for ( var i = 0; i < this.sources.length; i++ )
		{
			if ( this.sources[i].getMIMEType() == mimeType ){
				return true;
			}
		}
		return false;
	},

	/**
	 * Checks if media is a playable type
	 */
	isPlayableType: function( mimeType ) {
		//	mw.log("isPlayableType:: " + mimeType);
		if ( mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType ) ) {
			return true;
		} else {
			return false;
		}
	},

	/**
	 * Adds a single mediaSource using the provided element if the element has a
	 * 'src' attribute.
	 *
	 * @param {Element}
	 *      element <video>, <source> or <mediaSource> <text> element.
	 */
	tryAddSource: function( element ) {
		//mw.log( 'mw.MediaElement::tryAddSource:' + $( element ).attr( "src" ) );
		var newSrc = $( element ).attr( 'src' );
		if ( newSrc ) {
			// Make sure an existing element with the same src does not already exist:
			for ( var i = 0; i < this.sources.length; i++ ) {
				if ( this.sources[i].src == newSrc ) {
					// Source already exists update any new attr:
					this.sources[i].updateSource( element );
					return this.sources[i];
				}
			}
		}
		// Create a new source
		var source = new mw.MediaSource( element );

		this.sources.push( source );
		//mw.log( 'tryAddSource: added source ::' + source + 'sl:' + this.sources.length );
		return source;
	},

	/**
	 * Get playable sources
	 *
	 *@pram mimeFilter {=string} (optional) Filter the playable sources set by mime filter
	 *
	 * @returns {Array} of playable media sources
	 */
	getPlayableSources: function( mimeFilter ) {
		 var playableSources = [];
		 for ( var i = 0; i < this.sources.length; i++ ) {
			 if ( this.isPlayableType( this.sources[i].mimeType )
					 &&
				( !mimeFilter || this.sources[i].mimeType.indexOf( mimeFilter) != -1  )
			){
				 playableSources.push( this.sources[i] );
			}
		 };
		 mw.log( "MediaElement::GetPlayableSources playable "+ playableSources.length + ' sources playable out of ' +  this.sources.length  + " mimeFilter:" + mimeFilter );
		 return playableSources;
	}
};

} )( mediaWiki, jQuery );

<|endoftext|>


<commit_before>$(function() {
  var error = function(msg) {
    $('.room-alert').text('Error: ' + msg);
  };

  var headerErrorTimeout = {};
  var headerError = function(header, msg) {
    if (headerErrorTimeout[header])
      clearTimeout(headerErrorTimeout[header]);

    var $error = header.find('.error');
    $error.text(msg).slideDown();

    headerErrorTimeout[header] = setTimeout(function() {
      delete headerErrorTimeout[header];
      $error.slideUp(function() {
        $error.text('');
      });
    }, 4000);
  };

  window.Connection = Backbone.Model.extend({

    /* Model */

    defaults: {
      connected: false
    },

    initialize: function() {
      this.connect();
    },

    /* Socket Commands */

    connect: function() {
      var doConnect = _.bind(function() {
        console.log('Connecting...');

        var socket = io.connect();

        this.set({ socket: socket });
        socket.on('connect', _.bind(this.handleConnect, this));
        socket.on('disconnect', _.bind(this.handleDisconnect, this));
        socket.on('error', _.bind(this.handleError, this));
        socket.on('kick', _.bind(this.handleKick, this));
        socket.on(
          'room:num_anonymous', _.bind(this.handleNumAnonymous, this));
        socket.on('room:user:join', _.bind(this.handleUserJoin, this));
        socket.on('room:user:leave', _.bind(this.handleUserLeave, this));
        socket.on('room:user:update', _.bind(this.handleUserUpdate, this));
        socket.on('room:users', _.bind(this.handleUserList, this));
      }, this);

      if (this.get('connected')) {
        this.once('disconnected', doConnect);
        this.disconnect();
      } else {
        doConnect();
      }
    },

    disconnect: function() {
      if (this.get('connected'))
        this.get('socket').disconnect();
    },

    authenticate: function() {
      var socket = this.get('socket');
      socket.emit('auth', {
        username: this.get('username'),
        hash: this.get('userhash')
      }, _.bind(function(data) {
        if (data.error) {
          error(data.error);
          socket.disconnect();
        } else {
          console.log('Authenticated.');
          this.joinRoom();
        }
      }, this));
    },

    joinRoom: function(shortname) {
      if (shortname)
        this.set({ room_shortname: shortname });

      var join = _.bind(function() {
        this.get('socket').emit(
            'room:join', this.get('room_shortname'), _.bind(function(data) {
          if (data.error) {
            this.handleError(data.error);
          } else {
            console.log('Joined room: ' + data.name);
            data.connected = true;
            if (!this.has('room')) {
              data.connection = this;
              this.set({ room: new models.Room(data) });
            } else {
              this.get('room').set(data);
            }
          }
        }, this));
      }, this);

      if (this.has('room'))
        this.leaveRoom(join);
      else
        join();
    },

    leaveRoom: function(next) {
      this.get('socket').emit('room:leave', _.bind(function() {
        console.log('Left room.');
        if (this.has('room'))
          this.get('room').reset();
        if (_.isFunction(next)) next();
      }, this));
    },

    beginDJ: function() {
      this.get('socket').emit('room:dj:begin', _.bind(function(data) {
        if (data.error)
          headerError($('#dj-header'), data.error);
      }, this));
    },

    endDJ: function() {
      this.get('socket').emit('room:dj:end', _.bind(function(data) {
        if (data.error)
          headerError($('#dj-header'), data.error);
      }, this));
    },

    /* Socket Handlers */

    handleConnect: function() {
      console.log('Connected.');

      this.set({ connected: true });
      if (this.has('username') && this.has('userhash'))
        this.authenticate();
      else
        this.joinRoom();

      this.trigger('connect');
    },

    handleDisconnect: function() {
      console.log('Disconnected.');

      this.set({ connected: false });
      if (this.has('room'))
        this.get('room').reset();

      this.trigger('disconnect');
    },

    handleError: function(err) {
      error(err);
      this.disconnect();
    },

    handleKick: function(msg) {
      this.get('room').set({ kick_message: msg || 'No reason supplied.' });
      this.get('socket').disconnect();
    },

    handleNumAnonymous: function(num) {
      this.get('room').set({ anonymous_listeners: num });
    },

    handleUserJoin: function(user) {
      this.get('room').addUser(user);
    },

    handleUserLeave: function(user) {
      this.get('room').removeUser(user.username);
    },

    handleUserUpdate: function(user) {
      this.get('room').getUser(user.username).set(user);
    },

    handleUserList: function(users) {
      var room = this.get('room');
      room.setUsers(users);
    }
  });
});

<commit_msg>Attempts reconnection every 5 seconds forever.<commit_after>$(function() {
  var error = function(msg) {
    console.error(msg);
    $('.room-alert').text('Error: ' + msg);
  };

  var headerErrorTimeout = {};
  var headerError = function(header, msg) {
    if (headerErrorTimeout[header])
      clearTimeout(headerErrorTimeout[header]);

    var $error = header.find('.error');
    $error.text(msg).slideDown();

    headerErrorTimeout[header] = setTimeout(function() {
      delete headerErrorTimeout[header];
      $error.slideUp(function() {
        $error.text('');
      });
    }, 4000);
  };

  window.Connection = Backbone.Model.extend({

    /* Model */

    defaults: {
      connected: false,
      reconnect_attempts: 0
    },

    initialize: function() {
      this.connect();
    },

    /* Socket Commands */

    connect: function() {
      var doConnect = _.bind(function() {
        console.log('Connecting...');

        var socket = io.connect();

        this.set({ socket: socket });
        socket.on('connect', _.bind(this.handleConnect, this));
        socket.on(
          'connect_failed', _.bind(this.handleConnectFailed, this));
        socket.on(
          'reconnect_failed', _.bind(this.handleConnectFailed, this));
        socket.on('reconnecting', _.bind(this.handleReconnecting, this));
        socket.on('disconnect', _.bind(this.handleDisconnect, this));
        socket.on('error', _.bind(this.handleError, this));
        socket.on('kick', _.bind(this.handleKick, this));
        socket.on(
          'room:num_anonymous', _.bind(this.handleNumAnonymous, this));
        socket.on('room:user:join', _.bind(this.handleUserJoin, this));
        socket.on('room:user:leave', _.bind(this.handleUserLeave, this));
        socket.on('room:user:update', _.bind(this.handleUserUpdate, this));
        socket.on('room:users', _.bind(this.handleUserList, this));
      }, this);

      if (this.get('connected')) {
        this.once('disconnected', doConnect);
        this.disconnect();
      } else {
        doConnect();
      }
    },

    disconnect: function() {
      if (this.get('connected'))
        this.get('socket').disconnect();
    },

    authenticate: function() {
      var socket = this.get('socket');
      socket.emit('auth', {
        username: this.get('username'),
        hash: this.get('userhash')
      }, _.bind(function(data) {
        if (data.error) {
          error(data.error);
          socket.disconnect();
        } else {
          console.log('Authenticated.');
          this.joinRoom();
        }
      }, this));
    },

    joinRoom: function(shortname) {
      if (shortname)
        this.set({ room_shortname: shortname });

      var join = _.bind(function() {
        this.get('socket').emit(
            'room:join', this.get('room_shortname'), _.bind(function(data) {
          if (data.error) {
            this.handleError(data.error);
          } else {
            console.log('Joined room: ' + data.name);
            data.connected = true;
            if (!this.has('room')) {
              data.connection = this;
              this.set({ room: new models.Room(data) });
            } else {
              this.get('room').set(data);
            }
          }
        }, this));
      }, this);

      if (this.has('room'))
        this.leaveRoom(join);
      else
        join();
    },

    leaveRoom: function(next) {
      this.get('socket').emit('room:leave', _.bind(function() {
        console.log('Left room.');
        if (this.has('room'))
          this.get('room').reset();
        if (_.isFunction(next)) next();
      }, this));
    },

    beginDJ: function() {
      this.get('socket').emit('room:dj:begin', _.bind(function(data) {
        if (data.error)
          headerError($('#dj-header'), data.error);
      }, this));
    },

    endDJ: function() {
      this.get('socket').emit('room:dj:end', _.bind(function(data) {
        if (data.error)
          headerError($('#dj-header'), data.error);
      }, this));
    },

    /* Socket Handlers */

    handleConnect: function() {
      console.log('Connected.');

      this.set({
        connected: true,
        reconnect_attempts: 0
      });
      if (this.has('username') && this.has('userhash'))
        this.authenticate();
      else
        this.joinRoom();

      this.trigger('connect');
    },

    handleConnectFailed: function() {
      error('Failed to connect to server.');
    },

    handleDisconnect: function() {
      console.log('Disconnected.');

      this.set({ connected: false });
      if (this.has('room'))
        this.get('room').reset();

      this.trigger('disconnect');
    },

    handleReconnecting: function() {
      var attempts = this.get('reconnect_attempts') + 1;
      $('.room-alert').text(
        'Disconnected. Attempting to reconnect. (' + attempts + ')');
      this.set({ reconnect_attempts: attempts });

      // Constant reconnection attempt interval, unlimited attempts.
      this.get('socket').socket.reconnectionDelay = 5000;
      this.get('socket').socket.reconnectionAttempts = 1;
    },

    handleError: function(err) {
      error(err);
      this.disconnect();
    },

    handleKick: function(msg) {
      this.get('room').set({ kick_message: msg || 'No reason supplied.' });
      this.get('socket').disconnect();
    },

    handleNumAnonymous: function(num) {
      this.get('room').set({ anonymous_listeners: num });
    },

    handleUserJoin: function(user) {
      this.get('room').addUser(user);
    },

    handleUserLeave: function(user) {
      this.get('room').removeUser(user.username);
    },

    handleUserUpdate: function(user) {
      this.get('room').getUser(user.username).set(user);
    },

    handleUserList: function(users) {
      var room = this.get('room');
      room.setUsers(users);
    }
  });
});

<|endoftext|>


<commit_before>module.exports = function (grunt) {
	grunt.registerTask('linkAssets', [
		'sails-linker:devJs',
		'sails-linker:devStyles',
		'sails-linker:devTpl',
		'sails-linker:devJsJade',
		'sails-linker:devStylesJade',
		'sails-linker:devTplJade'
	]);
};
<commit_msg>modify for gulp<commit_after>// todo - modify fully for gulp
module.exports = function (gulp) {
	//grunt.registerTask('linkAssets', [
	//	'sails-linker:devJs',
	//	'sails-linker:devStyles',
	//	'sails-linker:devTpl',
	//	'sails-linker:devJsJade',
	//	'sails-linker:devStylesJade',
	//	'sails-linker:devTplJade'
	//]);
};
<|endoftext|>


<commit_before>// Copyright (C) 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @author maoziqing@gmail.com
 * @requires ___, bridal, Q, URI
 * @provides xhrModuleLoadMaker, scriptModuleLoadMaker, clearModuleCache
 * To obtain the dependencies of this file, load:
 *   cajita.js, bridal.js, uri.js, cajita-promise.js
 *
 * Each load maker object, given the absolute URL of the current module, an
 * identifier resolver, and a cajoler finder, returns a load object.
 *
 * A load object is a function object; load() returns a module object,
 * given its module identifier.
 * load.async() returns a promise to the module, given the module identifier.
 *
 * What a module identifier is is entirely up to the identifier resolver. The
 * identifier resolver is a function of two parameters, (current module
 * absolute URL, module identifier), returning the absolute URL for the
 * identified module. The default module identifier resolver considers the
 * module identifier to be a relative URL.
 *
 * The cajoler finder is a function which should take an absolute module URL
 * and return a URL for cajoled code to load as if by a <script> element. (It
 * need not point to an actual cajoling service and could instead use static
 * .out.js files, depending on the application.)
 *
 * Note that this system never actually fetches the module absolute URL, only
 * passes it to the cajoler. But it *is* used as a key in the cache of loaded
 * modules, so a module absolute URL should always have the same module.
 *
 * TODO(kpreid): explain static (sync) loading module id semantics.
 */
var xhrModuleLoadMaker;
var scriptModuleLoadMaker;
var defaultModuleIdResolver;
var defaultCajolerFinder;
var clearModuleCache;

(function() {
  // Map from absolute module URLs to module objects.
  var cache = {};

  defaultModuleIdResolver = function(thisModURL, mid) {
    return URI.resolve(URI.parse(thisModURL), URI.parse(mid)).toString();
  };

  defaultCajolerFinder = function(uncajoledSourceURL) {
    var inputMimeType;
    if (/\.js$/.test(uncajoledSourceURL)) {
      inputMimeType = 'application/javascript';
    } else if (/\.html$/.test(uncajoledSourceURL)) {
      inputMimeType = 'text/html';
    } else {
      inputMimeType = 'application/javascript';
    }

    return 'http://caja.appspot.com/cajole' +
        '?url=' + encodeURIComponent(uncajoledSourceURL) +
        '&input-mime-type=' + inputMimeType +
        '&output-mime-type=application/javascript';
  };

  function syncLoad(modURL) {
    if (cache[modURL] === undefined || Q.near(cache[modURL]).isPromise___) {
      throw new Error("The static module " + modURL + " cannot be resolved.");
    }
    return Q.near(cache[modURL]);
  }

  function loadMaker(thisModURL, midResolver, cajolerFinder, asyncLoad) {
    var load = function(mid) {
      return syncLoad(midResolver(thisModURL, mid));
    };

    var async = function(mid) {
      return asyncLoad(midResolver(thisModURL, mid),
                       midResolver, cajolerFinder);
    };

    var asyncAll = function(moduleNames) {
      var r = Q.defer();
      var i;
      var modulePromises = [];
      var modules = {};

      for (i = 0; i < moduleNames.length; ++i) {
        modulePromises[i] = async(moduleNames[i]);
      }

      var waitNext = function(idx) {
        if (idx === moduleNames.length) {
          r.resolve(modules);
        } else {
          Q.when( modulePromises[idx], function(theModule) {
            modules[moduleNames[idx]] = theModule;
            waitNext(idx + 1);
          }, function(reason) {
            r.resolve(Q.reject(reason));
          });
        }
      };
      waitNext(0);
      return r.promise();
    };

    load.FUNC___ = 'load';
    ___.setStatic(load, 'async', ___.markFuncFreeze(async));
    ___.setStatic(load, 'asyncAll', ___.markFuncFreeze(asyncAll));
    return ___.primFreeze(load);
  }

  function resolveDependency(module, load) {
    var r = Q.defer();
    if (module.includedModules !== undefined
        && module.includedModules.length !== 0) {
      var size = module.includedModules.length;
      var count = 0;
      for (var i = 0; i < size; i++) {
        var mid = module.includedModules[i];
        var m = load.async(mid);
        Q.when(m, function(childModule) {
                    count++;
                    if (count === size) {
                      r.resolve(true);
                    }
                  },
                  function(reason) {
                    r.resolve(Q.reject(
                        "Retrieving the module " + mid + " failed."));
                  });
      }
    } else {
      r.resolve(true);
    }
    return r.promise;
  }

  function noop() {}

  /**
   * Given a method of async loading, produce the load-maker that the client
   * will use.
   */
  function makeConcreteLoadMaker(asyncLoadFunction) {
    return ___.markFuncFreeze(function(mid, midResolver, cajolerFinder) {
      if (midResolver === undefined) {
        midResolver = defaultModuleIdResolver;
      }
      if (cajolerFinder === undefined) {
        cajolerFinder = defaultCajolerFinder;
      }

      return loadMaker(mid, midResolver, cajolerFinder, asyncLoadFunction);
    });
  }

  function xhrAsyncLoad(modURL, midResolver, cajolerFinder) {
    if (cache[modURL] !== undefined) {
      return cache[modURL];
    }

    var r = Q.defer();
    cache[modURL] = r.promise;

    var xhr = bridal.makeXhr();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        xhr.onreadystatechange = noop;  // avoid memory leak
        if (xhr.status === 200) {
          var savedModuleHandler = ___.getNewModuleHandler();
          ___.setNewModuleHandler(___.primFreeze({
            handle: ___.markFuncFreeze(function theHandler(module) {
              try {
                var load = loadMaker(modURL, midResolver, cajolerFinder,
                                     xhrAsyncLoad);
                module.moduleURL = modURL;
                var securedModule = ___.prepareModule(module, load);
                var dependency = resolveDependency(module, load);
                Q.when(dependency, function(result) {
                                     r.resolve(securedModule);
                                   },
                                   function(reason) {
                                     r.resolve(Q.reject(
                                         "Resolving dependency for the"
                                         + "module " + modURL + " failed."));
                                   });
              } catch (e) {
                r.resolve(Q.reject(e));
              }
            }),
            handleUncaughtException: savedModuleHandler.handleUncaughtException
          }));
          //TODO: validate the response before eval it
          try {
            eval(xhr.responseText);
          } finally {
            ___.setNewModuleHandler(savedModuleHandler);
          }
        } else {
          r.resolve(Q.reject(
              "Retrieving the module " + modURL + " failed, "
              + "status code = " + xhr.status));
        }
      }
    };

    var cajoledModURL = cajolerFinder(modURL);

    xhr.open("GET", cajoledModURL, true);
    if (typeof xhr.overrideMimeType === 'function') {
      xhr.overrideMimeType("application/javascript");
    }
    xhr.send(null);
    return r.promise;
  }

  xhrModuleLoadMaker = makeConcreteLoadMaker(xhrAsyncLoad);

  var head = 0;
  var queue = [];
  var busy = false;

  function scriptAsyncLoad(modURL, midResolver, cajolerFinder) {
    if (cache[modURL] !== undefined) {
      return cache[modURL];
    }

    var r = Q.defer();
    cache[modURL] = r.promise;

    function dequeue() {
      if (head < queue.length) {
        busy = true;
        var savedHead = head;

        // TODO(ihab.awad): Modifying a frozen structure (the new module
        // handler) in un-cajoled code is possible but poor practice.

        // TODO(ihab.awad): Consider refactoring the <script> module loader to
        // always use a JSONP-style callback function so that it does not
        // trample on code that uses the standard ___.loadModule().

        // TODO(ihab.awad): Consider deprecating the entire newModuleHandler
        // system since it relies on globally visible modifications to "___";
        // instead maybe we should just cajole to a well-known callback if none
        // is supplied, like:
        //     cajaModuleCallback___(___.prepareModule({ ... module ... }));

        var newModuleHandler = ___.makeNormalNewModuleHandler();
        newModuleHandler.handle = ___.markFuncFreeze(
            function theHandler(module) {
              if (savedHead === head) {
                var r = queue[head].defer;
                try {
                  var curModURL = queue[head].modURL;
                  var load = loadMaker(curModURL, midResolver, cajolerFinder,
                                       scriptAsyncLoad);
                  module.moduleURL = modURL;
                  var securedModule = ___.prepareModule(module, load);

                  var dependency = resolveDependency(module, load);
                  Q.when(dependency,
                      function(result) {
                        r.resolve(securedModule);
                      },
                      function(reason) {
                        r.resolve(Q.reject(
                            "Resolving dependency for the module "
                            + curModURL + " failed."));
                      });
                } catch (e) {
                  r.resolve(Q.reject(e));
                }
                delete queue[head];
                head++;
                dequeue();
              } else {
                throw new Error('Module queue got out of sync');
              }
            });
        // TODO: don't we need to save the old module handler here so we
        // can restore it in timeout.
        ___.setNewModuleHandler(newModuleHandler);

        var timeout = function () {
          // Don't prevent GC
          script.onreadystatechange = script.onerror = null;
          if (savedHead === head) {
            var r = queue[head].defer;
            r.resolve(Q.reject(
                "Retrieving the module " + queue[head].modURL + " failed."));
            delete queue[head];
            head++;
            dequeue();
          } else {
            // the module has been loaded successfully
          }
        };

        var script = document.createElement("script");
        script.src = cajolerFinder(queue[head].modURL);
        script.onerror = timeout;
        script.onreadystatechange = function() {
          if (script.readyState === 'loaded'
              || script.readyState === 'complete') {
            timeout();
          }
        };
        document.getElementsByTagName('head')[0].appendChild(script);
      } else {
        busy = false;
      }
    }

    queue.push({ modURL: modURL, defer: r });

    if (!busy) {
      dequeue();
    }

    return r.promise;
  }

  scriptModuleLoadMaker = makeConcreteLoadMaker(scriptAsyncLoad);

  clearModuleCache = ___.markFuncFreeze(function() {
    cajita.forOwnKeys(cache, ___.markFuncFreeze(function(k, v) {
      delete cache[k];
    }));
  });
})();
<commit_msg>cajita-module.js: make cajoling-service-contacting logic reusable for other than caja.appspot.com http://codereview.appspot.com/1692044<commit_after>// Copyright (C) 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @author maoziqing@gmail.com, kpreid@switchb.org
 * @requires ___, bridal, Q, URI
 * @provides xhrModuleLoadMaker, scriptModuleLoadMaker, clearModuleCache,
 *           defaultModuleIdResolver, defaultCajolerFinder,
 *           CajolingServiceFinder
 * To obtain the dependencies of this file, load:
 *   cajita.js, bridal.js, uri.js, cajita-promise.js
 *
 * Each load maker object, given the absolute URL of the current module, an
 * identifier resolver, and a cajoler finder, returns a load object.
 *
 * A load object is a function object; load() returns a module object,
 * given its module identifier.
 * load.async() returns a promise to the module, given the module identifier.
 *
 * What a module identifier is is entirely up to the identifier resolver. The
 * identifier resolver is a function of two parameters, (current module
 * absolute URL, module identifier), returning the absolute URL for the
 * identified module. The default module identifier resolver considers the
 * module identifier to be a relative URL.
 *
 * The cajoler finder is a function which should take an absolute module URL
 * and return a URL for cajoled code to load as if by a <script> element. (It
 * need not point to an actual cajoling service and could instead use static
 * .out.js files, depending on the application.)
 *
 * Note that this system never actually fetches the module absolute URL, only
 * passes it to the cajoler. But it *is* used as a key in the cache of loaded
 * modules, so a module absolute URL should always have the same module.
 *
 * TODO(kpreid): explain static (sync) loading module id semantics.
 */
var xhrModuleLoadMaker;
var scriptModuleLoadMaker;
var defaultModuleIdResolver;
var defaultCajolerFinder;
var CajolingServiceFinder;
var clearModuleCache;

(function() {
  // Map from absolute module URLs to module objects.
  var cache = {};

  defaultModuleIdResolver = function(thisModURL, mid) {
    return URI.resolve(URI.parse(thisModURL), URI.parse(mid)).toString();
  };

  /**
   * Constructor for a cajoler finder given the URL of a cajoling service.
   */
  CajolingServiceFinder = function(serviceURL) {
    function cajolingServiceFinder(uncajoledSourceURL) {
      var inputMimeType;
      if (/\.js$/.test(uncajoledSourceURL)) {
        inputMimeType = 'application/javascript';
      } else if (/\.html$/.test(uncajoledSourceURL)) {
        inputMimeType = 'text/html';
      } else {
        inputMimeType = 'application/javascript';
      }

      return serviceURL +
          '?url=' + encodeURIComponent(uncajoledSourceURL) +
          '&input-mime-type=' + inputMimeType +
          '&output-mime-type=application/javascript';
    }
    return cajolingServiceFinder;
  };
  defaultCajolerFinder = new CajolingServiceFinder(
      'http://caja.appspot.com/cajole');

  function syncLoad(modURL) {
    if (cache[modURL] === undefined || Q.near(cache[modURL]).isPromise___) {
      throw new Error("The static module " + modURL + " cannot be resolved.");
    }
    return Q.near(cache[modURL]);
  }

  function loadMaker(thisModURL, midResolver, cajolerFinder, asyncLoad) {
    var load = function(mid) {
      return syncLoad(midResolver(thisModURL, mid));
    };

    var async = function(mid) {
      return asyncLoad(midResolver(thisModURL, mid),
                       midResolver, cajolerFinder);
    };

    var asyncAll = function(moduleNames) {
      var r = Q.defer();
      var i;
      var modulePromises = [];
      var modules = {};

      for (i = 0; i < moduleNames.length; ++i) {
        modulePromises[i] = async(moduleNames[i]);
      }

      var waitNext = function(idx) {
        if (idx === moduleNames.length) {
          r.resolve(modules);
        } else {
          Q.when( modulePromises[idx], function(theModule) {
            modules[moduleNames[idx]] = theModule;
            waitNext(idx + 1);
          }, function(reason) {
            r.resolve(Q.reject(reason));
          });
        }
      };
      waitNext(0);
      return r.promise();
    };

    load.FUNC___ = 'load';
    ___.setStatic(load, 'async', ___.markFuncFreeze(async));
    ___.setStatic(load, 'asyncAll', ___.markFuncFreeze(asyncAll));
    return ___.primFreeze(load);
  }

  function resolveDependency(module, load) {
    var r = Q.defer();
    if (module.includedModules !== undefined
        && module.includedModules.length !== 0) {
      var size = module.includedModules.length;
      var count = 0;
      for (var i = 0; i < size; i++) {
        var mid = module.includedModules[i];
        var m = load.async(mid);
        Q.when(m, function(childModule) {
                    count++;
                    if (count === size) {
                      r.resolve(true);
                    }
                  },
                  function(reason) {
                    r.resolve(Q.reject(
                        "Retrieving the module " + mid + " failed."));
                  });
      }
    } else {
      r.resolve(true);
    }
    return r.promise;
  }

  function noop() {}

  /**
   * Given a method of async loading, produce the load-maker that the client
   * will use.
   */
  function makeConcreteLoadMaker(asyncLoadFunction) {
    return ___.markFuncFreeze(function(mid, midResolver, cajolerFinder) {
      if (midResolver === undefined) {
        midResolver = defaultModuleIdResolver;
      }
      if (cajolerFinder === undefined) {
        cajolerFinder = defaultCajolerFinder;
      }

      return loadMaker(mid, midResolver, cajolerFinder, asyncLoadFunction);
    });
  }

  function xhrAsyncLoad(modURL, midResolver, cajolerFinder) {
    if (cache[modURL] !== undefined) {
      return cache[modURL];
    }

    var r = Q.defer();
    cache[modURL] = r.promise;

    var xhr = bridal.makeXhr();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        xhr.onreadystatechange = noop;  // avoid memory leak
        if (xhr.status === 200) {
          var savedModuleHandler = ___.getNewModuleHandler();
          ___.setNewModuleHandler(___.primFreeze({
            handle: ___.markFuncFreeze(function theHandler(module) {
              try {
                var load = loadMaker(modURL, midResolver, cajolerFinder,
                                     xhrAsyncLoad);
                module.moduleURL = modURL;
                var securedModule = ___.prepareModule(module, load);
                var dependency = resolveDependency(module, load);
                Q.when(dependency, function(result) {
                                     r.resolve(securedModule);
                                   },
                                   function(reason) {
                                     r.resolve(Q.reject(
                                         "Resolving dependency for the"
                                         + "module " + modURL + " failed."));
                                   });
              } catch (e) {
                r.resolve(Q.reject(e));
              }
            }),
            handleUncaughtException: savedModuleHandler.handleUncaughtException
          }));
          //TODO: validate the response before eval it
          try {
            eval(xhr.responseText);
          } finally {
            ___.setNewModuleHandler(savedModuleHandler);
          }
        } else {
          r.resolve(Q.reject(
              "Retrieving the module " + modURL + " failed, "
              + "status code = " + xhr.status));
        }
      }
    };

    var cajoledModURL = cajolerFinder(modURL);

    xhr.open("GET", cajoledModURL, true);
    if (typeof xhr.overrideMimeType === 'function') {
      xhr.overrideMimeType("application/javascript");
    }
    xhr.send(null);
    return r.promise;
  }

  xhrModuleLoadMaker = makeConcreteLoadMaker(xhrAsyncLoad);

  var head = 0;
  var queue = [];
  var busy = false;

  function scriptAsyncLoad(modURL, midResolver, cajolerFinder) {
    if (cache[modURL] !== undefined) {
      return cache[modURL];
    }

    var r = Q.defer();
    cache[modURL] = r.promise;

    function dequeue() {
      if (head < queue.length) {
        busy = true;
        var savedHead = head;

        // TODO(ihab.awad): Modifying a frozen structure (the new module
        // handler) in un-cajoled code is possible but poor practice.

        // TODO(ihab.awad): Consider refactoring the <script> module loader to
        // always use a JSONP-style callback function so that it does not
        // trample on code that uses the standard ___.loadModule().

        // TODO(ihab.awad): Consider deprecating the entire newModuleHandler
        // system since it relies on globally visible modifications to "___";
        // instead maybe we should just cajole to a well-known callback if none
        // is supplied, like:
        //     cajaModuleCallback___(___.prepareModule({ ... module ... }));

        var newModuleHandler = ___.makeNormalNewModuleHandler();
        newModuleHandler.handle = ___.markFuncFreeze(
            function theHandler(module) {
              if (savedHead === head) {
                var r = queue[head].defer;
                try {
                  var curModURL = queue[head].modURL;
                  var load = loadMaker(curModURL, midResolver, cajolerFinder,
                                       scriptAsyncLoad);
                  module.moduleURL = modURL;
                  var securedModule = ___.prepareModule(module, load);

                  var dependency = resolveDependency(module, load);
                  Q.when(dependency,
                      function(result) {
                        r.resolve(securedModule);
                      },
                      function(reason) {
                        r.resolve(Q.reject(
                            "Resolving dependency for the module "
                            + curModURL + " failed."));
                      });
                } catch (e) {
                  r.resolve(Q.reject(e));
                }
                delete queue[head];
                head++;
                dequeue();
              } else {
                throw new Error('Module queue got out of sync');
              }
            });
        // TODO: don't we need to save the old module handler here so we
        // can restore it in timeout.
        ___.setNewModuleHandler(newModuleHandler);

        var timeout = function () {
          // Don't prevent GC
          script.onreadystatechange = script.onerror = null;
          if (savedHead === head) {
            var r = queue[head].defer;
            r.resolve(Q.reject(
                "Retrieving the module " + queue[head].modURL + " failed."));
            delete queue[head];
            head++;
            dequeue();
          } else {
            // the module has been loaded successfully
          }
        };

        var script = document.createElement("script");
        script.src = cajolerFinder(queue[head].modURL);
        script.onerror = timeout;
        script.onreadystatechange = function() {
          if (script.readyState === 'loaded'
              || script.readyState === 'complete') {
            timeout();
          }
        };
        document.getElementsByTagName('head')[0].appendChild(script);
      } else {
        busy = false;
      }
    }

    queue.push({ modURL: modURL, defer: r });

    if (!busy) {
      dequeue();
    }

    return r.promise;
  }

  scriptModuleLoadMaker = makeConcreteLoadMaker(scriptAsyncLoad);

  clearModuleCache = ___.markFuncFreeze(function() {
    cajita.forOwnKeys(cache, ___.markFuncFreeze(function(k, v) {
      delete cache[k];
    }));
  });
})();
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  name: 'Something',

  requires: [
    'foam.util.Timer'
  ],

  properties: [
    {
      name: 'value',
      value: 'aaaa'
    },
    {
      name: 'timer',
      factory: function() {
        return this.Timer.create();
      }
    }
  ],

  methods: [
    function init() {
      this.timer.start();
      this.timer.propertyChange.sub('second', this.onTimer);
    }
  ],

  listeners: [
    function onTimer() {
      this.value = this.value === 'aaaa' ? 'bbbb' : 'aaaa';
    }
  ]
});

var timer = foam.util.Timer.create();
timer.start();

var E = foam.__context__.E.bind(foam.__context__);

E('b').add(
  'bold',
  E('br'),
  'HTML Injection Attempt',
  E('br')
);

E().
  add('Entities: ').
  add('foo').
  nbsp().
  entity('amp').
  add(' bar ').
  entity('lt').
  entity('quot').
  entity("#039").
  tag('br').
  write();

E('b').add(
  'color: ',
  E('font').attrs({color: 'red'}).add('red', E('br'))).write();

var e = E('font').add('click me', E('br'));
console.log('id: ', e.id);
e.write();
e.attrs({color: 'orange'}).
  style({
    fontWeight: 'bold',
    fontSize:  '24pt'
  }).
  on('click', function() { console.log('clicked'); });

var e13 = E('div').add(
  'dynamic function: ',
  foam.core.ExpressionSlot.create({
    args: [ timer.second$ ],
    code: function(s) {
      return s % 2 ?
        E('span').add('PI', 'NG').style({color: 'aqua'}) :
        E('span').add('PONG').style({color: 'orange'});
    }
  }),
  ' dynamic function2: ',
  timer.second$.map(function(s) {
    return s % 2 ?
      E('span').add('PI', 'NG').style({color: 'aqua'}) :
      E('span').add('PONG').style({color: 'orange'});
  }),
  ' dynamic function3: ',
  timer.slot(function (second) {
    return second % 2 ?
      E('span').add('PI', 'NG').style({color: 'aqua'}) :
      E('span').add('PONG').style({color: 'orange'});
  }),
  ' dynamic function4: ',
  foam.core.ExpressionSlot.create({
    args: [ timer.second$ ],
    code: function(s) {
      return s % 2 ?
        E('span').add('PI', 'NG').style({color: 'aqua'}) :
        E('span').add('PONG').style({color: 'orange'});
    }
  }),
  E('br'),
  'dynamic value: ', timer.i$,
  E('br'));
e13.write();

var e2 = E('font').add('on click (before)', E('br')).on('click', function() { console.log('clicked, before'); });
e2.write();

var e2b = E('font').add('on click (after)');
e2b.write();
e2b.on('click', function() { console.log('clicked, after'); });

E('br').write();

var e3 = E('div').add('first line, added before');
e3.write();
e3.add(E('br'),'second line, added after', E('br'));

E('div').add('add style before').style({color: 'blue'}).write();

var e5 = E('div').add('add style after');
e5.write();
e5.style({color: 'blue'});

E('br').write();

E('div').add('add class before').addClass('important').write();

var e7 = E('div').add('add class after');
e7.write();
e7.addClass('important');

E('font').add(E('br'), 'set attr before').attrs({color: 'blue'}).write();

var e11 = E('font').add(E('br'), 'set attr after', E('br'));
e11.write();
e11.attrs({color: 'blue'});

 var e = E('div').add(
   E('span').add("hello "),
   E('span').add("!")).write();
 e.insertBefore(E('span').add('world'), e.children[1]);

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!"));
e.insertBefore(E('span').add('world'), e.children[1]);
e.write();

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!")).write();
e.insertAfter(E('span').add('world'), e.children[0]);

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!"));
e.insertAfter(E('span').add('world'), e.children[0]);
e.write();

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!"));
e.addBefore(e.children[1], E('span').add('there '), E('span').add('world'));
e.write();

var oldChild = E().add('First Child').style({color: 'red'});
var newChild = E().add('Second Child').style({color: 'green'});
var e = E('div').tag('br').add(oldChild).tag('br').write();
e.replaceChild(newChild, oldChild);

foam.CLASS({
  name: 'RedElement',
  extends: 'foam.u2.Element',
  methods: [ function init() { this.SUPER(); this.style({color: 'red'}); } ]
});

RedElement.create().add('red element').write();

var i = E('image').attrs({
  displayWidth: 200,
  displayHeight: 200,
  data: 'Dragon.png'
});

i.write();

foam.CLASS({
  name: 'Person',
  properties: [ 'firstName', 'lastName', 'age' ],
  actions: [
    function sayHello() {
      console.log('hello');
    },
    function sayGoodbye() {
      console.log('goodbye');
    }
  ]
});

var p = Person.create({firstName: 'John', lastName: 'Doe', age: 42});

var Y =  foam.__context__.createSubContext({data: p});
console.log('data: ', Y.data);

var s = p.firstName$;
var input = foam.u2.tag.Input.create({data: 'william'});
var input2 = foam.u2.tag.Input.create({data: 'john'});
input.data$ = input2.data$;
input.write();
input2.write();

Person.FIRST_NAME.toE(null, Y).write();
Person.FIRST_NAME.toE(null, Y).write();

var e = Y.E('div').add('simple: ').add(Person.FIRST_NAME, Person.LAST_NAME);
e.write();

var e2 = Y.E('div').add('simple2: ').add(Person.getAxiomsByClass(foam.core.Property));
e2.write();

foam.u2.DetailView.create({
  data: p
}).write();

var dv2 = foam.u2.DetailView.create({
  data: p,
  showActions: true
}).write();

foam.u2.DetailView.create({
  data: foam.util.Timer.create(),
  showActions: true
}).write();

foam.u2.DetailView.create({
  data: foam.util.Timer.create(),
  showActions: true,
  properties: [ foam.util.Timer.INTERVAL, foam.util.Timer.I ],
  actions: [ foam.util.Timer.STOP, foam.util.Timer.START ]
}).write();

foam.CLASS({
  name: 'CustomDetailView',
  extends: 'foam.u2.Element',

  exports: [ 'as data' ],

  axioms: [
    foam.u2.CSS.create({
      code: function() {/*
        important { color: red; }
      */}
    })
  ],

  properties: [
    { class: 'Int', name: 'i' },
    'field1',
    { name: 'field2', view: 'foam.u2.view.PasswordView' },
    {
      name: 'choices',
      view: {
//        class: 'foam.u2.view.ChoiceView',
        class: 'foam.u2.view.RadioView',
        choices: [
          'ABC',
          'DEF',
          'XYZ'
        ]
      }
    },
    'flip'
  ],

  actions: [
    function reset() { this.field1 = this.field2 = ''; },
    function sayHello() { console.log('hello'); }
  ],

  listeners: [
    {
      name: 'flop',
      isMerged: true,
      mergeDelay: 1000,
      code: function() {
        this.i++;
        this.flip = ! this.flip;
        this.flop();
      }
    }
  ],

  methods: [
    function initE() {
      this.flop();

      this.field1 = 'foo';
      this.field2 = 'bar';

      var o2 = this.cls_.create({field1: 'O2.f1', field2: 'O2.f2'});

      this.
        addClass(this.slot(function(flip) {
            return flip ? 'important' : '';
          })).
          tag('br').
          tag('hr').
          add(
              'start: ',
              this.field1$, ' ',
              this.field2$, ' ',
              this.field1$, ' ',
              this.field2$, ' O2: ',
              o2.field2$,
              this.E('br'),
              this.FIELD1, ' ',
              this.FIELD2, ' ',
              this.CHOICES, ' ',
              this.CHOICES, ' ',
              this.E('br'),
              this.SAY_HELLO, ' ',
              this.RESET,
              this.E('br'),
              'OnKey: '
          ).
          start('div').show(this.flip$).add('flip').end().
          start('div').hide(this.flip$).add('flop').end().
          start(this.FIELD1).attrs({onKey: true}).end().
          start(this.FIELD1).attrs({onKey: true}).end().
          start(this.FIELD2).attrs({onKey: true}).end().
          start(this.CHOICES).end().
          start(this.CHOICES).end().
          tag('br').

          start(this.FIELD1, {data$: o2.field1$}).end().
          start(this.FIELD2, {data$: o2.field2$}).end().

          tag('br').
          add('subContext: ').
          startContext({data: o2}).
            add(o2.FIELD1).
            add(o2.FIELD2).
            add(this.slot(function(flip) {
              return flip ? o2.FIELD1 : o2.FIELD2;
            })).
          endContext();

          /*
          start('notimage').
            attrs({
              data: 'dragon.png',
              displayWidth: this.slot(function(i) { return i * 10 % 100; })
            }).
          end().
          */

    }
  ]
});
foam.lookup('CustomDetailView').create().write();

// Converted from Angular2 demo:
// https://github.com/thelgevold/angular-2-samples/blob/master/components/bound-textbox/bound-textbox.js
foam.CLASS({
  name: 'BoundTextbox',
  extends: 'foam.u2.Controller',
  properties: [ ['text', 'hello' ] ],
  methods: [ function initE() {
    this.start('h1').add("Bound Textbox").end().start(this.TEXT, {onKey: true}).end().add(this.text$);
  }]
});
foam.lookup('BoundTextbox').create().write();

foam.CLASS({
  name: 'Test',
  properties: [
    {
      class: 'Int',
      name: 'foo',
      preSet: function(_, a) {
        if ( a > 20 ) return 20;
        return a;
      }
    }
  ]
});

var d = Test.create();
foam.u2.DetailView.create({ data: d }).write();
foam.u2.DetailView.create({ data: d }).write();
foam.u2.DetailView.create({ data: d }).write();

foam.CLASS({
  name: 'ActionDemo',
  properties: [ 'foo' ],
  actions: [
    function add() {
      console.log('add');
    },
    {
      name: 'add2',
      label: 'Add',
      icon: 'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png',
      code: function() { console.log('add'); }
    }
  ]
});

foam.CLASS({
  name: 'ActionDemoSubView',
  extends: 'foam.u2.Element',

  imports: [ 'viewAction' ],

  exports: [ 'as data' ],

  methods: [
    function initE() {
      this
        // Default Button
        .add('subView: ', this.SUB_VIEW_ACTION);
    }
  ],
  actions: [
    function subViewAction(X, obj) {
      this.viewAction();
    }
  ]
});

foam.CLASS({
  name: 'ActionDemoView',
  extends: 'foam.u2.View',

  exports: [ 'viewAction' ],

  methods: [
    function initE() {
      this
        // Default Button
        .add(ActionDemo.ADD)

        // Embed an Image manually
        .start(ActionDemo.ADD).start('img').attr('src','https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png').end().end()

        // Set the ActionView's icon
        .start(ActionDemo.ADD, {icon:'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png'}).end()

        // Set the ActionView's icon and hide the label
        .start(ActionDemo.ADD, {showLabel: false, icon:'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png'}).end()

        // Set the ActionView's icon and hide the label and make an anchor to avoid button decoration
        .start(ActionDemo.ADD, {showLabel: false, icon:'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png'}).setNodeName('a').end()

        // Show an Action that already has an icon defined
        .start(ActionDemo.ADD2).end()

        .add(this.VIEW_ACTION)

        .tag({class: 'ActionDemoSubView'});
    }
  ],
  actions: [
    function viewAction() {
      console.log('view action');
    }
  ]
});

ActionDemoView.create({data: ActionDemo.create()}).write();

foam.CLASS({
  name: 'ParentView',
  extends: 'foam.u2.Element',
  axioms: [
    foam.u2.CSS.create({code: `
      ^ { background: pink }
    `})
  ],
  methods: [ function initE() {
    this.addClass(this.myClass()).add('text');
  }]
});

foam.CLASS({
  name: 'Child1View',
  extends: 'ParentView',
  axioms: [ foam.u2.CSS.create({code: ParentView.getAxiomsByClass(foam.u2.CSS)[0].code}) ]
});

foam.CLASS({
  name: 'Child2View',
  extends: 'ParentView',
  axioms: [ foam.u2.CSS.create({code: ParentView.getAxiomsByClass(foam.u2.CSS)[0].code}) ]
})

Child1View.create().write();
Child2View.create().write();
<commit_msg>Added missing ;.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  name: 'Something',

  requires: [
    'foam.util.Timer'
  ],

  properties: [
    {
      name: 'value',
      value: 'aaaa'
    },
    {
      name: 'timer',
      factory: function() {
        return this.Timer.create();
      }
    }
  ],

  methods: [
    function init() {
      this.timer.start();
      this.timer.propertyChange.sub('second', this.onTimer);
    }
  ],

  listeners: [
    function onTimer() {
      this.value = this.value === 'aaaa' ? 'bbbb' : 'aaaa';
    }
  ]
});

var timer = foam.util.Timer.create();
timer.start();

var E = foam.__context__.E.bind(foam.__context__);

E('b').add(
  'bold',
  E('br'),
  'HTML Injection Attempt',
  E('br')
);

E().
  add('Entities: ').
  add('foo').
  nbsp().
  entity('amp').
  add(' bar ').
  entity('lt').
  entity('quot').
  entity("#039").
  tag('br').
  write();

E('b').add(
  'color: ',
  E('font').attrs({color: 'red'}).add('red', E('br'))).write();

var e = E('font').add('click me', E('br'));
console.log('id: ', e.id);
e.write();
e.attrs({color: 'orange'}).
  style({
    fontWeight: 'bold',
    fontSize:  '24pt'
  }).
  on('click', function() { console.log('clicked'); });

var e13 = E('div').add(
  'dynamic function: ',
  foam.core.ExpressionSlot.create({
    args: [ timer.second$ ],
    code: function(s) {
      return s % 2 ?
        E('span').add('PI', 'NG').style({color: 'aqua'}) :
        E('span').add('PONG').style({color: 'orange'});
    }
  }),
  ' dynamic function2: ',
  timer.second$.map(function(s) {
    return s % 2 ?
      E('span').add('PI', 'NG').style({color: 'aqua'}) :
      E('span').add('PONG').style({color: 'orange'});
  }),
  ' dynamic function3: ',
  timer.slot(function (second) {
    return second % 2 ?
      E('span').add('PI', 'NG').style({color: 'aqua'}) :
      E('span').add('PONG').style({color: 'orange'});
  }),
  ' dynamic function4: ',
  foam.core.ExpressionSlot.create({
    args: [ timer.second$ ],
    code: function(s) {
      return s % 2 ?
        E('span').add('PI', 'NG').style({color: 'aqua'}) :
        E('span').add('PONG').style({color: 'orange'});
    }
  }),
  E('br'),
  'dynamic value: ', timer.i$,
  E('br'));
e13.write();

var e2 = E('font').add('on click (before)', E('br')).on('click', function() { console.log('clicked, before'); });
e2.write();

var e2b = E('font').add('on click (after)');
e2b.write();
e2b.on('click', function() { console.log('clicked, after'); });

E('br').write();

var e3 = E('div').add('first line, added before');
e3.write();
e3.add(E('br'),'second line, added after', E('br'));

E('div').add('add style before').style({color: 'blue'}).write();

var e5 = E('div').add('add style after');
e5.write();
e5.style({color: 'blue'});

E('br').write();

E('div').add('add class before').addClass('important').write();

var e7 = E('div').add('add class after');
e7.write();
e7.addClass('important');

E('font').add(E('br'), 'set attr before').attrs({color: 'blue'}).write();

var e11 = E('font').add(E('br'), 'set attr after', E('br'));
e11.write();
e11.attrs({color: 'blue'});

 var e = E('div').add(
   E('span').add("hello "),
   E('span').add("!")).write();
 e.insertBefore(E('span').add('world'), e.children[1]);

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!"));
e.insertBefore(E('span').add('world'), e.children[1]);
e.write();

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!")).write();
e.insertAfter(E('span').add('world'), e.children[0]);

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!"));
e.insertAfter(E('span').add('world'), e.children[0]);
e.write();

var e = E('div').add(
  E('span').add("hello "),
  E('span').add("!"));
e.addBefore(e.children[1], E('span').add('there '), E('span').add('world'));
e.write();

var oldChild = E().add('First Child').style({color: 'red'});
var newChild = E().add('Second Child').style({color: 'green'});
var e = E('div').tag('br').add(oldChild).tag('br').write();
e.replaceChild(newChild, oldChild);

foam.CLASS({
  name: 'RedElement',
  extends: 'foam.u2.Element',
  methods: [ function init() { this.SUPER(); this.style({color: 'red'}); } ]
});

RedElement.create().add('red element').write();

var i = E('image').attrs({
  displayWidth: 200,
  displayHeight: 200,
  data: 'Dragon.png'
});

i.write();

foam.CLASS({
  name: 'Person',
  properties: [ 'firstName', 'lastName', 'age' ],
  actions: [
    function sayHello() {
      console.log('hello');
    },
    function sayGoodbye() {
      console.log('goodbye');
    }
  ]
});

var p = Person.create({firstName: 'John', lastName: 'Doe', age: 42});

var Y =  foam.__context__.createSubContext({data: p});
console.log('data: ', Y.data);

var s = p.firstName$;
var input = foam.u2.tag.Input.create({data: 'william'});
var input2 = foam.u2.tag.Input.create({data: 'john'});
input.data$ = input2.data$;
input.write();
input2.write();

Person.FIRST_NAME.toE(null, Y).write();
Person.FIRST_NAME.toE(null, Y).write();

var e = Y.E('div').add('simple: ').add(Person.FIRST_NAME, Person.LAST_NAME);
e.write();

var e2 = Y.E('div').add('simple2: ').add(Person.getAxiomsByClass(foam.core.Property));
e2.write();

foam.u2.DetailView.create({
  data: p
}).write();

var dv2 = foam.u2.DetailView.create({
  data: p,
  showActions: true
}).write();

foam.u2.DetailView.create({
  data: foam.util.Timer.create(),
  showActions: true
}).write();

foam.u2.DetailView.create({
  data: foam.util.Timer.create(),
  showActions: true,
  properties: [ foam.util.Timer.INTERVAL, foam.util.Timer.I ],
  actions: [ foam.util.Timer.STOP, foam.util.Timer.START ]
}).write();

foam.CLASS({
  name: 'CustomDetailView',
  extends: 'foam.u2.Element',

  exports: [ 'as data' ],

  axioms: [
    foam.u2.CSS.create({
      code: function() {/*
        important { color: red; }
      */}
    })
  ],

  properties: [
    { class: 'Int', name: 'i' },
    'field1',
    { name: 'field2', view: 'foam.u2.view.PasswordView' },
    {
      name: 'choices',
      view: {
//        class: 'foam.u2.view.ChoiceView',
        class: 'foam.u2.view.RadioView',
        choices: [
          'ABC',
          'DEF',
          'XYZ'
        ]
      }
    },
    'flip'
  ],

  actions: [
    function reset() { this.field1 = this.field2 = ''; },
    function sayHello() { console.log('hello'); }
  ],

  listeners: [
    {
      name: 'flop',
      isMerged: true,
      mergeDelay: 1000,
      code: function() {
        this.i++;
        this.flip = ! this.flip;
        this.flop();
      }
    }
  ],

  methods: [
    function initE() {
      this.flop();

      this.field1 = 'foo';
      this.field2 = 'bar';

      var o2 = this.cls_.create({field1: 'O2.f1', field2: 'O2.f2'});

      this.
        addClass(this.slot(function(flip) {
            return flip ? 'important' : '';
          })).
          tag('br').
          tag('hr').
          add(
              'start: ',
              this.field1$, ' ',
              this.field2$, ' ',
              this.field1$, ' ',
              this.field2$, ' O2: ',
              o2.field2$,
              this.E('br'),
              this.FIELD1, ' ',
              this.FIELD2, ' ',
              this.CHOICES, ' ',
              this.CHOICES, ' ',
              this.E('br'),
              this.SAY_HELLO, ' ',
              this.RESET,
              this.E('br'),
              'OnKey: '
          ).
          start('div').show(this.flip$).add('flip').end().
          start('div').hide(this.flip$).add('flop').end().
          start(this.FIELD1).attrs({onKey: true}).end().
          start(this.FIELD1).attrs({onKey: true}).end().
          start(this.FIELD2).attrs({onKey: true}).end().
          start(this.CHOICES).end().
          start(this.CHOICES).end().
          tag('br').

          start(this.FIELD1, {data$: o2.field1$}).end().
          start(this.FIELD2, {data$: o2.field2$}).end().

          tag('br').
          add('subContext: ').
          startContext({data: o2}).
            add(o2.FIELD1).
            add(o2.FIELD2).
            add(this.slot(function(flip) {
              return flip ? o2.FIELD1 : o2.FIELD2;
            })).
          endContext();

          /*
          start('notimage').
            attrs({
              data: 'dragon.png',
              displayWidth: this.slot(function(i) { return i * 10 % 100; })
            }).
          end().
          */

    }
  ]
});
foam.lookup('CustomDetailView').create().write();

// Converted from Angular2 demo:
// https://github.com/thelgevold/angular-2-samples/blob/master/components/bound-textbox/bound-textbox.js
foam.CLASS({
  name: 'BoundTextbox',
  extends: 'foam.u2.Controller',
  properties: [ ['text', 'hello' ] ],
  methods: [ function initE() {
    this.start('h1').add("Bound Textbox").end().start(this.TEXT, {onKey: true}).end().add(this.text$);
  }]
});
foam.lookup('BoundTextbox').create().write();

foam.CLASS({
  name: 'Test',
  properties: [
    {
      class: 'Int',
      name: 'foo',
      preSet: function(_, a) {
        if ( a > 20 ) return 20;
        return a;
      }
    }
  ]
});

var d = Test.create();
foam.u2.DetailView.create({ data: d }).write();
foam.u2.DetailView.create({ data: d }).write();
foam.u2.DetailView.create({ data: d }).write();

foam.CLASS({
  name: 'ActionDemo',
  properties: [ 'foo' ],
  actions: [
    function add() {
      console.log('add');
    },
    {
      name: 'add2',
      label: 'Add',
      icon: 'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png',
      code: function() { console.log('add'); }
    }
  ]
});

foam.CLASS({
  name: 'ActionDemoSubView',
  extends: 'foam.u2.Element',

  imports: [ 'viewAction' ],

  exports: [ 'as data' ],

  methods: [
    function initE() {
      this
        // Default Button
        .add('subView: ', this.SUB_VIEW_ACTION);
    }
  ],
  actions: [
    function subViewAction(X, obj) {
      this.viewAction();
    }
  ]
});

foam.CLASS({
  name: 'ActionDemoView',
  extends: 'foam.u2.View',

  exports: [ 'viewAction' ],

  methods: [
    function initE() {
      this
        // Default Button
        .add(ActionDemo.ADD)

        // Embed an Image manually
        .start(ActionDemo.ADD).start('img').attr('src','https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png').end().end()

        // Set the ActionView's icon
        .start(ActionDemo.ADD, {icon:'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png'}).end()

        // Set the ActionView's icon and hide the label
        .start(ActionDemo.ADD, {showLabel: false, icon:'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png'}).end()

        // Set the ActionView's icon and hide the label and make an anchor to avoid button decoration
        .start(ActionDemo.ADD, {showLabel: false, icon:'https://cdn4.iconfinder.com/data/icons/48x48-free-object-icons/48/Add.png'}).setNodeName('a').end()

        // Show an Action that already has an icon defined
        .start(ActionDemo.ADD2).end()

        .add(this.VIEW_ACTION)

        .tag({class: 'ActionDemoSubView'});
    }
  ],
  actions: [
    function viewAction() {
      console.log('view action');
    }
  ]
});

ActionDemoView.create({data: ActionDemo.create()}).write();

foam.CLASS({
  name: 'ParentView',
  extends: 'foam.u2.Element',
  axioms: [
    foam.u2.CSS.create({code: `
      ^ { background: pink }
    `})
  ],
  methods: [ function initE() {
    this.addClass(this.myClass()).add('text');
  }]
});

foam.CLASS({
  name: 'Child1View',
  extends: 'ParentView',
  axioms: [ foam.u2.CSS.create({code: ParentView.getAxiomsByClass(foam.u2.CSS)[0].code}) ]
});

foam.CLASS({
  name: 'Child2View',
  extends: 'ParentView',
  axioms: [ foam.u2.CSS.create({code: ParentView.getAxiomsByClass(foam.u2.CSS)[0].code}) ]
});

Child1View.create().write();
Child2View.create().write();
<|endoftext|>


<commit_before>const Command = require('../Command')

module.exports =
class AnnounceChannelCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'support',
      properName: 'Support',
      aliases: ['server'],
      userPermissions: [],
      description: 'Posts an invite link to the Official RoVer Discord where you can easily get help.'
    })
  }

  async fn (msg) {
    msg.author.send('Having trouble? You can join our official support discord here: discord.gg/7yfwrat, or you can check out the documentation here: https://rover.link/#readme').then(() => {
      msg.reply('Sent you a DM with information.')
    }).catch(() => {
      msg.reply('I can\'t seem to message you - please make sure your DMs are enabled!')
    });
  }
}
<commit_msg>peer code review has failed us<commit_after>const Command = require('../Command')

module.exports =
class SupportServerCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'support',
      properName: 'Support',
      aliases: ['server'],
      userPermissions: [],
      description: 'Posts an invite link to the Official RoVer Discord where you can easily get help.'
    })
  }

  async fn (msg) {
    msg.author.send('Having trouble? You can join our official support discord here: discord.gg/7yfwrat, or you can check out the documentation here: https://rover.link/#readme').then(() => {
      msg.reply('Sent you a DM with information.')
    }).catch(() => {
      msg.reply('I can\'t seem to message you - please make sure your DMs are enabled!')
    });
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import './index.css'
import Draggable, { DraggableCore } from 'react-draggable'
import CharacterEntry from '../CharacterList/CharacterEntry'
import { NotificationManager } from 'react-notifications'

class PlayerCanvas extends Component {
  constructor(props) {
    super(props)

    this.state = {
      tryCount: 0
    }
  }
  render() {
    const { renderable, onUpdateRenderablePosition, summary, onStart, onStop, onClick } = this.props
    const { tryCount } = this.state
    let imgLink = summary
    if (summary.indexOf('?') !== -1) imgLink = summary + `&tryCount=${tryCount}`
    else imgLink = summary + `?tryCount=${tryCount}`
    return (
      <DraggableCore
        onStart={onStart}
        onStop={onStop}
        onDrag={onUpdateRenderablePosition}
        position={renderable.position}
        >

          <img
            src={imgLink}
            alt=''
            className='renderable-instance'
            draggable={false}
            onClick={onClick}
            onError={this.showError.bind(this)}
            style={{
              position: 'relative',
              transform: `translate(${renderable.position.x}px, ${renderable.position.y}px)`,
              touchAction: 'none'
            }}
            />

        </DraggableCore>
    )
  }

  showError() {
    NotificationManager.warning(`There was an error rendering your ${this.props.renderable.type}`, '', 7000)
    setTimeout(function () {
      if (this.state.tryCount < 10) {
        NotificationManager.warning(`Retrying to render your ${this.props.renderable.type}`, '', 2000)
        setTimeout(function () {
          this.setState({ tryCount: this.state.tryCount + 1 })
        }.bind(this), 2500)
      }
    }.bind(this), 7500)
  }
}

export default PlayerCanvas
<commit_msg>Correcting not being able to move new characters if there are other characters<commit_after>import React, { Component } from 'react'
import './index.css'
import Draggable, { DraggableCore } from 'react-draggable'
import CharacterEntry from '../CharacterList/CharacterEntry'
import { NotificationManager } from 'react-notifications'

class PlayerCanvas extends Component {
  constructor(props) {
    super(props)

    this.state = {
      tryCount: 0
    }
  }
  render() {
    const { renderable, onUpdateRenderablePosition, summary, onStart, onStop, onClick } = this.props
    const { tryCount } = this.state
    let imgLink = summary
    if (summary.indexOf('?') !== -1) imgLink = summary + `&tryCount=${tryCount}`
    else imgLink = summary + `?tryCount=${tryCount}`
    return (
      <DraggableCore
        onStart={onStart}
        onStop={onStop}
        onDrag={onUpdateRenderablePosition}
        position={renderable.position}
        >
        <div style={{ transform: `translate(${renderable.position.x}px, ${renderable.position.y}px)` }}>

        </DraggableCore>
    )
  }

  showError() {
    NotificationManager.warning(`There was an error rendering your ${this.props.renderable.type}`, '', 7000)
    setTimeout(function () {
      if (this.state.tryCount < 10) {
        NotificationManager.warning(`Retrying to render your ${this.props.renderable.type}`, '', 2000)
        setTimeout(function () {
          this.setState({ tryCount: this.state.tryCount + 1 })
        }.bind(this), 2500)
      }
    }.bind(this), 7500)
  }
}

export default PlayerCanvas
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import LinearProgress from 'material-ui/lib/linear-progress';

require('styles//StopWatch.scss');

class StopWatchComponent extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      completed : 0,
      steps     : 0,
      timer     : undefined
    };
  }

  componentDidMount() {
    this.state.timer = setTimeout(() => this.progress(this.state.steps * 2), this.props.watchTimeout);
    this.state.steps = this.getPercentageSteps(this.props.slides.collection[this.props.slides.pointer].timeout);
  }

  componentWillUnmount() {
    clearTimeout(this.timer);
  }

  componentWillReceiveProps(nextProps) {
    if(nextProps.slides.isPlaying && this.props.slides.pointer != nextProps.slides.pointer) {
      this.reset();
      this.setState({
        steps : this.getPercentageSteps(nextProps.slides.collection[nextProps.slides.pointer].timeout),
        timer : setTimeout(() => this.progress(this.state.steps * 2), this.props.watchTimeout)
      });
    }
  }

  getPercentageSteps(totalDurationOfSlide) {
    return Math.round(100 / ( totalDurationOfSlide / this.props.watchTimeout) );
  }

  progress(completed) {
    if (completed > 100) {
      this.setState({completed: 100});
    } else {
      this.setState({completed});
      this.setState({timer : setTimeout(() => this.progress(completed + this.state.steps), this.props.watchTimeout)});
    }
  }

  reset() {
    clearTimeout(this.state.timer);
    this.setState({completed: 0});
  }

  render() {
    return (

        <LinearProgress mode="determinate" value={this.state.completed} />

    );
  }
}

StopWatchComponent.displayName = 'StopWatchComponent';

StopWatchComponent.propTypes = {
  slides : React.PropTypes.object.isRequired,
  watchTimeout : React.PropTypes.number.isRequired
};

StopWatchComponent.defaultProps = {
  watchTimeout: 250
};

export default StopWatchComponent;
<commit_msg>Implements the handling of stop and play<commit_after>'use strict';

import React from 'react';
import LinearProgress from 'material-ui/lib/linear-progress';

require('styles//StopWatch.scss');

class StopWatchComponent extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      completed : 0,
      steps     : 0,
      timer     : undefined
    };
  }

  componentDidMount() {
    this.state.timer = setTimeout(() => this.progress(this.state.steps * 2), this.props.watchTimeout);
    this.state.steps = this.getPercentageSteps(this.props.slides.collection[this.props.slides.pointer].timeout);
  }

  componentWillUnmount() {
    clearTimeout(this.timer);
  }

  componentWillReceiveProps(nextProps) {
    console.log(nextProps.slides.isPlaying, this.state.timer);
    if(!nextProps.slides.isPlaying) {
      this.reset();
      return;
    }

    if(nextProps.slides.isPlaying) {
      if (this.props.slides.pointer != nextProps.slides.pointer) {
        this.reset();
      }
      this.setState({
        steps : this.getPercentageSteps(nextProps.slides.collection[nextProps.slides.pointer].timeout),
        timer : setTimeout(() => this.progress(this.state.steps * 2), this.props.watchTimeout)
      });
    }
  }

  getPercentageSteps(totalDurationOfSlide) {
    return Math.round(100 / ( totalDurationOfSlide / this.props.watchTimeout) );
  }

  progress(completed) {
    if (completed > 100) {
      this.setState({completed: 100});
    } else {
      this.setState({completed});
      this.setState({timer : setTimeout(() => this.progress(completed + this.state.steps), this.props.watchTimeout)});
    }
  }

  reset() {
    clearTimeout(this.state.timer);
    this.setState({completed: 0});
  }

  render() {
    return (

        <LinearProgress mode="determinate" value={this.state.completed} />

    );
  }
}

StopWatchComponent.displayName = 'StopWatchComponent';

StopWatchComponent.propTypes = {
  slides       : React.PropTypes.object.isRequired,
  watchTimeout : React.PropTypes.number.isRequired
};

StopWatchComponent.defaultProps = {
  watchTimeout: 250
};

export default StopWatchComponent;
<|endoftext|>