<commit_before>// ==UserScript==
// @name       Minimalistic Twitter
// @namespace  http://bell345.github.io/
// @version    0.9.1
// @description  A minimalistic script for a minimalistic style.
// @match      https://twitter.com/*
// @copyright  CC-BY-SA Thomas Bell 2014
// ==/UserScript==
onload = function () {
    var newStyle = "";
    newStyle += "<style>";
    newStyle += "* { \n\tborder-width:0 !important; \n\tborder-radius:0 !important; \n}";
    newStyle += "\n.active a { \n\tborder-bottom-width:4px !important; \n}";
    newStyle += "\n.tweet-timestamp { \n\tfloat:right; \n}";
    newStyle += "\n.module { \n\tmargin-bottom:0 !important; \n}";
    newStyle += "\n.content-main, .profile-card.profile-header { \n\twidth: 520px; \n}";
    newStyle += "\n.wrapper, .profile-header-inner-overlay { \n\tbackground:none !important; \n}";
    newStyle += "\n.nav>li { \n\tcolor:#eee !important; \n}";
    newStyle += "\n.global-nav-inner {\n\tbackground:#333; \n}";
    newStyle += "\ndiv.stream-item-header img.avatar {\n\twidth:48px !important; \n\theight:48px !important; \n\tmargin-left:48px !important; \n}";
    newStyle += "\n.with-icn {\n\tfont-size:initial; \n}";
    newStyle += "\ndiv.content-main div.expansion-container div.tweet.descendant {\n\tpadding-left:0 !important; \n}";
    newStyle += "\nul.js-nav-links li.active a { \n\tborder-color:#0094ff; \n}";
    newStyle += "\n.modal-close { \n\tright:0 !important; \n\tbottom:0 !important; \n\ttop:0 !important; \n\theight:38px !important; \n\twidth:40px !important; \n}";
    newStyle += "\n.button-text.following-text, .button-text.unfollow-text { \n\tpadding-top:4px !important; \n}";
    newStyle += "\n.Footer.module { \n\tmargin-top:16px; \n}";
    newStyle += "\n.stream-container .tweet.gotham.avatar-small>div.content { \n\tmargin-left:58px !important; \n}";
    newStyle += "\n.stream-container .tweet .fullname { \n\tfont-size:17px !important; \n}";
    newStyle += "</style>";
    $("head").append(newStyle);
}<commit_msg>Updated min-twitter again<commit_after>// ==UserScript==
// @name       Minimalistic Twitter
// @namespace  http://bell345.github.io/
// @version    0.9.2
// @description  A minimalistic script for a minimalistic style.
// @match      https://twitter.com/*
// @copyright  CC-BY-SA Thomas Bell 2014
// ==/UserScript==
onload = function () {
    var newStyle = "";
    newStyle += "<style>";
    newStyle += "* { \n\tborder-width:0 !important; \n\tborder-radius:0 !important; \n}";
    newStyle += "\n.active a { \n\tborder-bottom-width:4px !important; \n}";
    newStyle += "\n.tweet-timestamp { \n\tfloat:right; \n}";
    newStyle += "\n.module { \n\tmargin-bottom:0 !important; \n}";
    newStyle += "\n.content-main, .profile-card.profile-header { \n\twidth: 520px; \n}";
    newStyle += "\n.wrapper, .profile-header-inner-overlay { \n\tbackground:none !important; \n}";
    newStyle += "\n.nav>li { \n\tcolor:#eee !important; \n}";
    newStyle += "\n.global-nav-inner {\n\tbackground:#333; \n}";
    newStyle += "\ndiv.stream-item-header img.avatar {\n\twidth:48px !important; \n\theight:48px !important; \n\tmargin-left:-58px !important; \n}";
    newStyle += "\n.with-icn {\n\tfont-size:initial; \n}";
    newStyle += "\ndiv.content-main div.expansion-container div.tweet.descendant {\n\tpadding-left:0 !important; \n}";
    newStyle += "\nul.js-nav-links li.active a { \n\tborder-color:#0094ff; \n}";
    newStyle += "\n.modal-close { \n\tright:0 !important; \n\tbottom:0 !important; \n\ttop:0 !important; \n\theight:38px !important; \n\twidth:40px !important; \n}";
    newStyle += "\n.button-text.following-text, .button-text.unfollow-text { \n\tpadding-top:4px !important; \n}";
    newStyle += "\n.Footer.module { \n\tmargin-top:16px; \n}";
    newStyle += "\n.stream-container .tweet.gotham.avatar-small>div.content { \n\tmargin-left:58px !important; \n}";
    newStyle += "\n.stream-container .tweet .fullname { \n\tfont-size:17px !important; \n}";
    newStyle += "</style>";
    $("head").append(newStyle);
}<|endoftext|>


<commit_before>mw.PlaylistHandlerMediaRss = function( playlist ){
	return this.init( playlist );
};

mw.PlaylistHandlerMediaRss.prototype = {
	// Set the media rss namespace
	mediaNS: 'http://search.yahoo.com/mrss/',

	// If playback should continue to the next clip on clip complete
	autoContinue: true,

	// if the playlist should automatically start:
	autoPlay: false,

	// If the playlist ui should be displayed
	includeInLayout: true,

	init: function ( playlist ){
		this.playlist = playlist;
	},

	/**
	 * Load the playlist source file with a callback
	 */
	loadPlaylist: function( callback ){
		var _this = this;
		// check if we already have the $rss loaded
		if( this.$rss ){
			callback( this.$rss );
			return ;
		}
		// Check if we have the source pre-loaded:
		if( this.getSrcPayLoad() ) {
			var xmlDoc =  $j.parseXML( this.getSrcPayLoad() );
			this.$rss = $j( xmlDoc );
			callback( _this.$rss );
			return ;
		}

		// Show an error if a cross domain request:
		if( mw.isLocalDomain( this.getSrc() ) ) {
			// Note this only works with local sources
			$j.get( mw.absoluteUrl( this.getSrc() ), function( data ){
				// jQuery already converts data into xmlDoc so the following is not needed:
				// var xmlDoc =  $j.parseXML( data );
				_this.$rss = $j( data );
				callback( _this.$rss );
			});
		} else {
			var proxyUrl = mw.getConfig( 'Mw.XmlProxyUrl' );
			if( !proxyUrl ){
				mw.log("Error: mw.KAds : missing kaltura proxy url ( can't load ad ) ");
				return ;
			}
			$j.getJSON( proxyUrl + '?url=' + encodeURIComponent( this.getSrc() ) + '&callback=?', function( result ){
				if( result['http_code'] == 'ERROR' || result['http_code'] == 0 ){
					mw.log("Error: loading " + _this.getSrc() );
					callback(false);
					return ;
				}
				// parse the MRSS:
				var xmlDoc =  $j.parseXML( result['contents'] );
				_this.$rss = $j( xmlDoc );
				callback( _this.$rss );
			});
		}
	},
	hasMultiplePlaylists: function(){
		return false;
	},
	hasPlaylistUi: function(){
		if( this.playlist.layout == 'noClipList' ){
			return false;
		}
		return this.includeInLayout;
	},
	getVideoListWidth: function(){
		return 'auto';
	},
	getSrcPayLoad: function(){
		return this.playlist.srcPayLoad;
	},
	getSrc: function(){
		return this.playlist.src;
	},
	// Get clip count
	getClipCount: function(){
		if( !this.$rss ){
			mw.log("Error no rss to count items" );
		}
		return this.$rss.find('item').length;
	},
	playClip: function( embedPlayer, clipIndex ){
		var _this = this;
		// Update the poster
		embedPlayer.updatePosterSrc( _this.getClipPoster( clipIndex, _this.playlist.getTargetPlayerSize() ) );
		// Empty existing sources
	    embedPlayer.emptySources();

	    var clipSources = this.getClipSources( clipIndex );
		if( !clipSources ){
			mw.log("Error: mw.Playlist no sources found for clipIndex:" + clipIndex);
			return ;
		}
		for( var i =0; i < clipSources.length; i++ ){
			var $source = $j('<source />')
			.attr( clipSources[i] );
			embedPlayer.mediaElement.tryAddSource( $source.get(0) ) ;
		}
		embedPlayer.changeMedia( function(){
			embedPlayer.play();
		});
	},

	drawEmbedPlayer: function( clipIndex, callback ){
		var _this = this;
		var playerSize = _this.playlist.getTargetPlayerSize();
		var $target = _this.playlist.getVideoPlayerTarget();
		var $video;
		// Check that the player is not already in the dom:
		if( $('#' + _this.playlist.getVideoPlayerId()).length ){
			mw.log( 'Error :: PlaylistHandler: drawEmbedPlayer player already in DOM? ');
		} else {

			// Build the video tag object:
			$video = $( '<video />' )
			.attr({
				'id' : _this.playlist.getVideoPlayerId(),
				'poster' : _this.getClipPoster( clipIndex, playerSize)
			})
			.css(
				playerSize
			);
			_this.updateVideoSources( clipIndex, $video );

			// Add the video to the target:
			$target.append( $video );

			// create the EmbedPlayer and issue the callback:
			$video.embedPlayer( callback );
		}
	},
	/**
	 * Adds the video sources for a given video tag
	 * @param clipIndex
	 * @param $video
	 * @return
	 */
	updateVideoSources: function( clipIndex, $video ){
		var clipSources = _this.getClipSources( clipIndex );
		if( clipSources ){
			// Update the sources from the playlist provider:
			for( var i =0; i < clipSources.length; i++ ){
				var $source = $j('<source />')
					.attr( clipSources[i] );
				$video.append( $source );
			}
		}
	},
	updatePlayerUi: function( clipIndex ){
		var playerSize = _this.playlist.getTargetPlayerSize();
		if( this.playlist.titleHeight != 0){
			// Build and output the title
			var $title = $('<div />' )
				.addClass( 'playlist-title ui-state-default ui-widget-header ui-corner-all')
				.css( {
					'top' : '0px',
					'height' : _this.titleHeight,
					'width' : playerSize.width
				} )
				.text(
					_this.getClipTitle( clipIndex )
				);
			$( _this.target + ' .media-rss-video-player-container' ).find('.playlist-title').remove();
			$( _this.target + ' .media-rss-video-player-container' ).prepend( $title );
		}
	},
	getClipSources: function( clipIndex ){
		var _this = this;
		var $item = $j( this.$rss.find('item')[ clipIndex ] );
		var clipSources = [];
		$j.each( $item.find( '*' ), function( inx, mediaContent){
			if( $j( mediaContent ).get(0).nodeName == 'media:content' ){
				clipSource = {};
				if( $j( mediaContent ).attr('url' ) ){
					clipSource.src = $j( mediaContent ).attr('url' );
				}
				if( $j( mediaContent ).attr('type' ) ){
					clipSource.type = $j( mediaContent ).attr('type' );
				}
				if( $j( mediaContent ).attr( 'duration' ) ) {
					clipSource.durationHint = $j( mediaContent ).attr('duration' );
				}
				clipSources.push( clipSource );
			}
		});
		return clipSources;
	},

	getCustomAttributes: function( clipIndex){
		// no custom metadata present in mrss playlist handler:
		return {};
	},
	addEmbedPlayerBindings: function( embedPlayer ){
		// no custom bindings in default mrss playlist handler
	},
	getClipList: function(){
		return this.$rss.find('item');
	},
	/**
	* Get an items poster image ( return missing thumb src if not found )
	*/
	getClipPoster: function ( clipIndex ){
		var $item = this.$rss.find('item').eq( clipIndex );
		var mediaThumb = $item.find( 'media\\:thumbnail, content\\:thumbnail, thumbnail' );
		mw.log( 'mw.PlaylistMediaRss::getClipPoster: ' + $j( mediaThumb ).attr('url' ) );
		if( mediaThumb && $j( mediaThumb ).attr('url' ) ){
			return $j( mediaThumb ).attr('url' );
		}

		// return missing thumb url
		return mw.getConfig( 'imagesPath' ) + 'vid_default_thumb.jpg';
	},
	/**
	* Get an item title from the $rss source
	*/
	getClipTitle: function( clipIndex ){
		var $item = this.$rss.find('item').eq( clipIndex ) ;
		var mediaTitle = $item.find( 'media\\:title, content\\:title, title' );
		if( mediaTitle ){
			return $j( mediaTitle ).text();
		}
		mw.log("Error could not find title for clip: " + clipIndex );
		return gM('mwe-mediarss-untitled');
	},

	/**
	 *  Get a clip description
	 */
	getClipDesc: function( clipIndex ){
		var $item = this.$rss.find('item').eq( clipIndex ) ;
		var mediaDesc = $item.find( 'media\\:description, content\\:description, description' );
		if( mediaDesc ){
			return $j( mediaDesc ).text();
		}
		mw.log("Error could not find description for clip: " + clipIndex );
		return gM('mwe-mediarss-untitled');
	},

	getClipDuration: function ( clipIndex ) {
		// return the first found media duration
		var $item = this.$rss.find('item').eq( clipIndex ) ;
		var itemDuration = 0;
		$j( $item.find('*')).each( function( inx, mediaContent ){
			if( $j( mediaContent ).attr( 'duration' ) ) {
				itemDuration = $j( mediaContent ).attr( 'duration' );
				// end for loop
				return false;
			}
		});
		return itemDuration;
	},
	getPlaylistItem: function( clipIndex ){
		var _this = this;
		var width = ( _this.playlist.itemThumbWidth )? _this.playlist.itemThumbWidth  : 70;
		var $item = $j('<div />')
			.css('width', '100%')
			.append(
				$j('<img />')
				.attr({
					'alt' : _this.getClipTitle( clipIndex ),
					'src' : _this.getClipPoster( clipIndex )
				})
				.css({
					'width': width + 'px'
				})
				,
				$j('<div />')
				.addClass('clipText')
				.append(
					$j('<span />')
					.addClass('clipTitle')
					.text(
						_this.playlist.formatTitle(
								_this.getClipTitle( clipIndex )
						)
					)
					,
					$j('<div />')
					.addClass('clipDuration')
					.text(
						mw.seconds2npt(
							_this.getClipDuration( clipIndex )
						)
					)
				)
				,
				$j('<div />').css('clear', 'right')
				,
				$j('<span />').addClass('clipDescription').text(
					_this.playlist.formatDescription(
						_this.getClipDesc( clipIndex )
					)
				)
			)
			// poor mans tool tip:
			.attr('title',_this.getClipDesc( clipIndex ) );
		return $item;
	}
};
<commit_msg>fixed missing _this = this ref<commit_after>mw.PlaylistHandlerMediaRss = function( playlist ){
	return this.init( playlist );
};

mw.PlaylistHandlerMediaRss.prototype = {
	// Set the media rss namespace
	mediaNS: 'http://search.yahoo.com/mrss/',

	// If playback should continue to the next clip on clip complete
	autoContinue: true,

	// if the playlist should automatically start:
	autoPlay: false,

	// If the playlist ui should be displayed
	includeInLayout: true,

	init: function ( playlist ){
		this.playlist = playlist;
	},

	/**
	 * Load the playlist source file with a callback
	 */
	loadPlaylist: function( callback ){
		var _this = this;
		// check if we already have the $rss loaded
		if( this.$rss ){
			callback( this.$rss );
			return ;
		}
		// Check if we have the source pre-loaded:
		if( this.getSrcPayLoad() ) {
			var xmlDoc =  $j.parseXML( this.getSrcPayLoad() );
			this.$rss = $j( xmlDoc );
			callback( _this.$rss );
			return ;
		}

		// Show an error if a cross domain request:
		if( mw.isLocalDomain( this.getSrc() ) ) {
			// Note this only works with local sources
			$j.get( mw.absoluteUrl( this.getSrc() ), function( data ){
				// jQuery already converts data into xmlDoc so the following is not needed:
				// var xmlDoc =  $j.parseXML( data );
				_this.$rss = $j( data );
				callback( _this.$rss );
			});
		} else {
			var proxyUrl = mw.getConfig( 'Mw.XmlProxyUrl' );
			if( !proxyUrl ){
				mw.log("Error: mw.KAds : missing kaltura proxy url ( can't load ad ) ");
				return ;
			}
			$j.getJSON( proxyUrl + '?url=' + encodeURIComponent( this.getSrc() ) + '&callback=?', function( result ){
				if( result['http_code'] == 'ERROR' || result['http_code'] == 0 ){
					mw.log("Error: loading " + _this.getSrc() );
					callback(false);
					return ;
				}
				// parse the MRSS:
				var xmlDoc =  $j.parseXML( result['contents'] );
				_this.$rss = $j( xmlDoc );
				callback( _this.$rss );
			});
		}
	},
	hasMultiplePlaylists: function(){
		return false;
	},
	hasPlaylistUi: function(){
		if( this.playlist.layout == 'noClipList' ){
			return false;
		}
		return this.includeInLayout;
	},
	getVideoListWidth: function(){
		return 'auto';
	},
	getSrcPayLoad: function(){
		return this.playlist.srcPayLoad;
	},
	getSrc: function(){
		return this.playlist.src;
	},
	// Get clip count
	getClipCount: function(){
		if( !this.$rss ){
			mw.log("Error no rss to count items" );
		}
		return this.$rss.find('item').length;
	},
	playClip: function( embedPlayer, clipIndex ){
		var _this = this;
		// Update the poster
		embedPlayer.updatePosterSrc( _this.getClipPoster( clipIndex, _this.playlist.getTargetPlayerSize() ) );
		// Empty existing sources
	    embedPlayer.emptySources();

	    var clipSources = this.getClipSources( clipIndex );
		if( !clipSources ){
			mw.log("Error: mw.Playlist no sources found for clipIndex:" + clipIndex);
			return ;
		}
		for( var i =0; i < clipSources.length; i++ ){
			var $source = $j('<source />')
			.attr( clipSources[i] );
			embedPlayer.mediaElement.tryAddSource( $source.get(0) ) ;
		}
		embedPlayer.changeMedia( function(){
			embedPlayer.play();
		});
	},

	drawEmbedPlayer: function( clipIndex, callback ){
		var _this = this;
		var playerSize = _this.playlist.getTargetPlayerSize();
		var $target = _this.playlist.getVideoPlayerTarget();
		var $video;
		// Check that the player is not already in the dom:
		if( $('#' + _this.playlist.getVideoPlayerId()).length ){
			mw.log( 'Error :: PlaylistHandler: drawEmbedPlayer player already in DOM? ');
		} else {

			// Build the video tag object:
			$video = $( '<video />' )
			.attr({
				'id' : _this.playlist.getVideoPlayerId(),
				'poster' : _this.getClipPoster( clipIndex, playerSize)
			})
			.css(
				playerSize
			);
			_this.updateVideoSources( clipIndex, $video );

			// Add the video to the target:
			$target.append( $video );

			// create the EmbedPlayer and issue the callback:
			$video.embedPlayer( callback );
		}
	},
	/**
	 * Adds the video sources for a given video tag
	 * @param clipIndex
	 * @param $video
	 * @return
	 */
	updateVideoSources: function( clipIndex, $video ){
		var clipSources = _this.getClipSources( clipIndex );
		if( clipSources ){
			// Update the sources from the playlist provider:
			for( var i =0; i < clipSources.length; i++ ){
				var $source = $j('<source />')
					.attr( clipSources[i] );
				$video.append( $source );
			}
		}
	},
	updatePlayerUi: function( clipIndex ){
		var _this = this;
		var playerSize = _this.playlist.getTargetPlayerSize();
		if( this.playlist.titleHeight != 0){
			// Build and output the title
			var $title = $('<div />' )
				.addClass( 'playlist-title ui-state-default ui-widget-header ui-corner-all')
				.css( {
					'top' : '0px',
					'height' : _this.titleHeight,
					'width' : playerSize.width
				} )
				.text(
					_this.getClipTitle( clipIndex )
				);
			$( _this.target + ' .media-rss-video-player-container' ).find('.playlist-title').remove();
			$( _this.target + ' .media-rss-video-player-container' ).prepend( $title );
		}
	},
	getClipSources: function( clipIndex ){
		var _this = this;
		var $item = $j( this.$rss.find('item')[ clipIndex ] );
		var clipSources = [];
		$j.each( $item.find( '*' ), function( inx, mediaContent){
			if( $j( mediaContent ).get(0).nodeName == 'media:content' ){
				clipSource = {};
				if( $j( mediaContent ).attr('url' ) ){
					clipSource.src = $j( mediaContent ).attr('url' );
				}
				if( $j( mediaContent ).attr('type' ) ){
					clipSource.type = $j( mediaContent ).attr('type' );
				}
				if( $j( mediaContent ).attr( 'duration' ) ) {
					clipSource.durationHint = $j( mediaContent ).attr('duration' );
				}
				clipSources.push( clipSource );
			}
		});
		return clipSources;
	},

	getCustomAttributes: function( clipIndex){
		// no custom metadata present in mrss playlist handler:
		return {};
	},
	addEmbedPlayerBindings: function( embedPlayer ){
		// no custom bindings in default mrss playlist handler
	},
	getClipList: function(){
		return this.$rss.find('item');
	},
	/**
	* Get an items poster image ( return missing thumb src if not found )
	*/
	getClipPoster: function ( clipIndex ){
		var $item = this.$rss.find('item').eq( clipIndex );
		var mediaThumb = $item.find( 'media\\:thumbnail, content\\:thumbnail, thumbnail' );
		mw.log( 'mw.PlaylistMediaRss::getClipPoster: ' + $j( mediaThumb ).attr('url' ) );
		if( mediaThumb && $j( mediaThumb ).attr('url' ) ){
			return $j( mediaThumb ).attr('url' );
		}

		// return missing thumb url
		return mw.getConfig( 'imagesPath' ) + 'vid_default_thumb.jpg';
	},
	/**
	* Get an item title from the $rss source
	*/
	getClipTitle: function( clipIndex ){
		var $item = this.$rss.find('item').eq( clipIndex ) ;
		var mediaTitle = $item.find( 'media\\:title, content\\:title, title' );
		if( mediaTitle ){
			return $j( mediaTitle ).text();
		}
		mw.log("Error could not find title for clip: " + clipIndex );
		return gM('mwe-mediarss-untitled');
	},

	/**
	 *  Get a clip description
	 */
	getClipDesc: function( clipIndex ){
		var $item = this.$rss.find('item').eq( clipIndex ) ;
		var mediaDesc = $item.find( 'media\\:description, content\\:description, description' );
		if( mediaDesc ){
			return $j( mediaDesc ).text();
		}
		mw.log("Error could not find description for clip: " + clipIndex );
		return gM('mwe-mediarss-untitled');
	},

	getClipDuration: function ( clipIndex ) {
		// return the first found media duration
		var $item = this.$rss.find('item').eq( clipIndex ) ;
		var itemDuration = 0;
		$j( $item.find('*')).each( function( inx, mediaContent ){
			if( $j( mediaContent ).attr( 'duration' ) ) {
				itemDuration = $j( mediaContent ).attr( 'duration' );
				// end for loop
				return false;
			}
		});
		return itemDuration;
	},
	getPlaylistItem: function( clipIndex ){
		var _this = this;
		var width = ( _this.playlist.itemThumbWidth )? _this.playlist.itemThumbWidth  : 70;
		var $item = $j('<div />')
			.css('width', '100%')
			.append(
				$j('<img />')
				.attr({
					'alt' : _this.getClipTitle( clipIndex ),
					'src' : _this.getClipPoster( clipIndex )
				})
				.css({
					'width': width + 'px'
				})
				,
				$j('<div />')
				.addClass('clipText')
				.append(
					$j('<span />')
					.addClass('clipTitle')
					.text(
						_this.playlist.formatTitle(
								_this.getClipTitle( clipIndex )
						)
					)
					,
					$j('<div />')
					.addClass('clipDuration')
					.text(
						mw.seconds2npt(
							_this.getClipDuration( clipIndex )
						)
					)
				)
				,
				$j('<div />').css('clear', 'right')
				,
				$j('<span />').addClass('clipDescription').text(
					_this.playlist.formatDescription(
						_this.getClipDesc( clipIndex )
					)
				)
			)
			// poor mans tool tip:
			.attr('title',_this.getClipDesc( clipIndex ) );
		return $item;
	}
};
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var toRomanNumeral = function(str) {

};

var RomanLetter = function(value,symbol) {
  return {
    value: function() { return value; },
    symbol: function() { return symbol; }
  };
};

var RomanAlphabet = function() {
  var letters = [
    new RomanLetter(1,'I'),
    new RomanLetter(2,'II'),
    new RomanLetter(3,'III'),
    new RomanLetter(4,'IV'),
    new RomanLetter(5,'V'),
    new RomanLetter(6,'VI'),
    new RomanLetter(7, 'VII'),
    new RomanLetter(8, 'VIII'),
    new RomanLetter(9, 'IX'),
    new RomanLetter(10,'X'),
    new RomanLetter(20,'XX'),
    new RomanLetter(30,'XXX'),
    new RomanLetter(40,'XL'),
    new RomanLetter(50,'L'),
    new RomanLetter(60,'LX'),
    new RomanLetter(70, 'LXX'),
    new RomanLetter(80, 'LXXX'),
    new RomanLetter(90, 'XC'),
    new RomanLetter(100,'C'),
    new RomanLetter(200,'CC'),
    new RomanLetter(300,'CCC'),
    new RomanLetter(400,'CD'),
    new RomanLetter(500,'D'),
    new RomanLetter(600,'DC'),
    new RomanLetter(700, 'DCC'),
    new RomanLetter(800, 'DCCC'),
    new RomanLetter(900, 'CM'),
    new RomanLetter(1000, 'M'),
    new RomanLetter(2000, 'MM'),
    new RomanLetter(3000, 'MMM'),
    new RomanLetter(4000, 'MMMM'),
  ];

  return {

    largestMatchingSymbol: function(num) {
      var n = num | 0;
      for (var i=letters.length-1;i>=0;i--) {
        if (letters[i].value() <= n) {
          return letters[i].symbol();
        }
      }
    },

    symbolFor: function(num) {
      var n = num | 0;
      for (var i=0;i<letters.length;++i) {
        if (letters[i].value() === n) {
          return letters[i].symbol();
        }
      }
    }
  }
};

describe('Roman Numerals', function() {
  describe('basics', function() {

    it('roman letters have a symbol and value', function() {
      var romanLetter = new RomanLetter(1,'I');
      assert.equal(1, romanLetter.value());
      assert.equal('I', romanLetter.symbol());
    });

    it('roman alphabet consists of letters', function() {
      var romanAlphabet = new RomanAlphabet();

      assert.equal("I", romanAlphabet.symbolFor(1));
      assert.equal("V", romanAlphabet.symbolFor(5));
      assert.equal("M", romanAlphabet.symbolFor(1000));
    });

    it('roman alphabet can find largest defined symbol', function() {
      var romanAlphabet = new RomanAlphabet();
      assert.equal('XX', romanAlphabet.largestMatchingSymbol(21));
      assert.equal('XX', romanAlphabet.largestMatchingSymbol(20));
    });

    describe('Acceptance Test', function() {
      it('meets acceptance criteria', function() {
        assert.equal("MCMXC", toRomanNumeral(1990));
        assert.equal("MMVIII", toRomanNumeral(2008));
        assert.equal("XCIX", toRomanNumeral(99));
        assert.equal("XLVII", toRomanNumeral(47));
      });
    });
  });
});
<commit_msg>refactor terrible test code<commit_after>"use strict";

var assert = require('assert');

var toRomanNumeral = function(str) {

};

var RomanLetter = function(value,symbol) {
  return {
    value: function() { return value; },
    symbol: function() { return symbol; }
  };
};

var RomanAlphabet = function() {
  var letters = [
    new RomanLetter(1,'I'),
    new RomanLetter(2,'II'),
    new RomanLetter(3,'III'),
    new RomanLetter(4,'IV'),
    new RomanLetter(5,'V'),
    new RomanLetter(6,'VI'),
    new RomanLetter(7, 'VII'),
    new RomanLetter(8, 'VIII'),
    new RomanLetter(9, 'IX'),
    new RomanLetter(10,'X'),
    new RomanLetter(20,'XX'),
    new RomanLetter(30,'XXX'),
    new RomanLetter(40,'XL'),
    new RomanLetter(50,'L'),
    new RomanLetter(60,'LX'),
    new RomanLetter(70, 'LXX'),
    new RomanLetter(80, 'LXXX'),
    new RomanLetter(90, 'XC'),
    new RomanLetter(100,'C'),
    new RomanLetter(200,'CC'),
    new RomanLetter(300,'CCC'),
    new RomanLetter(400,'CD'),
    new RomanLetter(500,'D'),
    new RomanLetter(600,'DC'),
    new RomanLetter(700, 'DCC'),
    new RomanLetter(800, 'DCCC'),
    new RomanLetter(900, 'CM'),
    new RomanLetter(1000, 'M'),
    new RomanLetter(2000, 'MM'),
    new RomanLetter(3000, 'MMM'),
    new RomanLetter(4000, 'MMMM'),
  ];

  return {

    largestMatchingSymbol: function(num) {
      var n = num | 0;
      for (var i=letters.length-1;i>=0;i--) {
        if (letters[i].value() <= n) {
          return letters[i].symbol();
        }
      }
    },

    symbolFor: function(num) {
      var n = num | 0;
      for (var i=0;i<letters.length;++i) {
        if (letters[i].value() === n) {
          return letters[i].symbol();
        }
      }
    }
  }
};

describe('Roman Numerals', function() {
  describe('basics', function() {

    var romanAlphabet = new RomanAlphabet();

    it('roman letters have a symbol and value', function() {
      var romanLetter = new RomanLetter(1,'I');
      assert.equal(1, romanLetter.value());
      assert.equal('I', romanLetter.symbol());
    });

    it('roman alphabet consists of letters', function() {
      assert.equal("I", romanAlphabet.symbolFor(1));
      assert.equal("V", romanAlphabet.symbolFor(5));
      assert.equal("M", romanAlphabet.symbolFor(1000));
    });

    it('roman alphabet can find largest defined symbol', function() {
      assert.equal('XX', romanAlphabet.largestMatchingSymbol(21));
      assert.equal('XX', romanAlphabet.largestMatchingSymbol(20));
    });

    describe('Acceptance Test', function() {
      it('meets acceptance criteria', function() {
        assert.equal("MCMXC", toRomanNumeral(1990));
        assert.equal("MMVIII", toRomanNumeral(2008));
        assert.equal("XCIX", toRomanNumeral(99));
        assert.equal("XLVII", toRomanNumeral(47));
      });
    });
  });
});
<|endoftext|>


<commit_before>
var _Deserializer = (function () {

    function _Deserializer(data, result, editor) {
        this._editor = (typeof editor !== 'undefined') ? editor : true;

        this._idList = [];
        this._idObjList = [];
        this._idPropList = [];
        this.result = result || new Fire._DeserializeInfo();

        var jsonObj = null;
        if (typeof data === 'string') {
            jsonObj = JSON.parse(data);
        }
        else {
            jsonObj = data;
        }

        if (Array.isArray(jsonObj)) {
            var jsonList = jsonObj;
            var refCount = jsonList.length;
            this.deserializedList = new Array(refCount);
            // deserialize
            for (var i = 0; i < refCount; i++) {
                if (jsonList[i]) {
                    this.deserializedList[i] = _deserializeObject(this, jsonList[i]);
                }
            }
            this.deserializedData = refCount > 0 ? this.deserializedList[0] : [];

            //// callback
            //for (var j = 0; j < refCount; j++) {
            //    if (referencedList[j].onAfterDeserialize) {
            //        referencedList[j].onAfterDeserialize();
            //    }
            //}
        }
        else {
            this.deserializedList = new Array(1);
            this.deserializedData = jsonObj ? _deserializeObject(this, jsonObj) : null;
            this.deserializedList[0] = this.deserializedData;

            //// callback
            //if (deserializedData.onAfterDeserialize) {
            //    deserializedData.onAfterDeserialize();
            //}
        }

        // dereference
        _dereference(this);
    }

    var _dereference = function (self) {
        // 这里不采用遍历反序列化结果的方式，因为反序列化的结果如果引用到复杂的外部库，很容易堆栈溢出。
        var deserializedList = self.deserializedList;
        for (var i = 0, len = self._idList.length; i < len; i++) {
            var propName = self._idPropList[i];
            var id = self._idList[i];
            self._idObjList[i][propName] = deserializedList[id];
        }
    };

    function _deserializeObjField (self, obj, jsonObj, propName) {
        var id = jsonObj.__id__;
        if (typeof id === 'undefined') {
            var uuid = jsonObj.__uuid__;
            if (uuid) {
                self.result.uuidList.push(uuid);
                self.result.uuidObjList.push(obj);
                self.result.uuidPropList.push(propName);
            }
            else {
                obj[propName] = _deserializeObject(self, jsonObj);
            }
        }
        else {
            var dObj = self.deserializedList[id];
            if (dObj) {
                obj[propName] = dObj;
            }
            else {
                self._idList.push(id);
                self._idObjList.push(obj);
                self._idPropList.push(propName);
            }
        }
    }

    /**
     * @param {Object} serialized - The obj to deserialize, must be non-nil
     */
    var _deserializeObject = function (self, serialized) {
        var propName, prop;
        var obj = null;
        var klass = null;
        if (serialized.__type__) {
            klass = Fire.getClassByName(serialized.__type__);
            if (!klass) {
                Fire.error('[Fire.deserialize] unknown type: ' + serialized.__type__);
                return null;
            }
            // instantiate a new object
            obj = new klass();
        }
        else if (!Array.isArray(serialized)) {
            // embedded primitive javascript object
            obj = serialized;
            //// jshint -W010
            //obj = new Object();
            //// jshint +W010
        }
        else {
            // array
            obj = serialized;
            for (var i = 0; i < serialized.length; i++) {
                prop = serialized[i];
                if (typeof prop === 'object' && prop) {
                    if (!prop.__uuid__ && typeof prop.__id__ === 'undefined') {
                        obj[i] = _deserializeObject(self, prop);
                    }
                    else {
                        _deserializeObjField(self, obj, prop, '' + i);
                    }
                }
            }
            return serialized;
        }

        // parse property
        if (klass && Fire._isFireClass(klass)) {
            if (!klass.__props__) {
                return obj;
            }
            for (var p = 0; p < klass.__props__.length; p++) {
                propName = klass.__props__[p];
                var attrs = Fire.attr(klass, propName);
                // assume all prop in __props__ must have attr
                var hostType = attrs.hostType;
                if (!hostType) {
                    // skip nonSerialized
                    if (attrs.serializable === false) {
                        continue;
                    }

                    // skip editor only if not editor
                    if (!self._editor && attrs.editorOnly) {
                        continue;
                    }

                    prop = serialized[propName];
                    if (typeof prop !== 'undefined') {
                        if (typeof prop !== 'object') {
                            obj[propName] = prop;
                        }
                        else {
                            if (prop) {
                                if (!prop.__uuid__ && typeof prop.__id__ === 'undefined') {
                                    obj[propName] = _deserializeObject(self, prop);
                                }
                                else {
                                    _deserializeObjField(self, obj, prop, propName);
                                }
                            }
                            else {
                                obj[propName] = null;
                            }
                        }
                    }
                }
                else {
                    // always load host objects even if property not serialized
                    if (self.result.hostProp) {
                        Fire.error('not support multi host object in a file');
                        // 这里假定每个asset都有uuid，每个json只能包含一个asset，只能包含一个hostProp
                    }
                    self.result.hostProp = propName;
                }
            }
        }
        else /*javascript object instance*/ {
            for (propName in obj) {
                prop = serialized[propName];
                if (typeof prop !== 'undefined' && serialized.hasOwnProperty(propName)) {
                    if (typeof prop !== 'object') {
                        obj[propName] = prop;
                    }
                    else {
                        if (prop) {
                            if (!prop.__uuid__ && typeof prop.__id__ === 'undefined') {
                                obj[propName] = _deserializeObject(self, prop);
                            }
                            else {
                                _deserializeObjField(self, obj, prop, propName);
                            }
                        }
                        else {
                            obj[propName] = null;
                        }
                    }
                }
            }
        }
        return obj;
    };

    return _Deserializer;
})();

/**
 * Deserialize json to Fire.Asset
 * @param {(string|object)} data - the serialized Fire.Asset json string or json object
 * @param {Fire._DeserializeInfo} [result] - additional loading result
 * @param {boolean} [editor=true] - if false, property with Fire.EditorOnly will be discarded
 * @returns {object} the main data(asset)
 */
Fire.deserialize = function (data, result, editor) {
    Fire._isCloning = true;
    var deserializer = new _Deserializer(data, result, editor);
    Fire._isCloning = false;
    return deserializer.deserializedData;
};

/**
 * 包含反序列化时的一些信息
 * @class Fire._DeserializeInfo
 */
Fire._DeserializeInfo = function () {

    //this.urlList = [];
    //this.callbackList = [];

    // uuids(assets) need to load

    /**
     * @property {string[]} uuidList - list of the depends assets' uuid
     */
    this.uuidList = [];
    /**
     * @property {object[]} uuidObjList - the obj list whose field needs to load asset by uuid
     */
    this.uuidObjList = [];
    /**
     * @property {string[]} uuidPropList - the corresponding field name which referenced to the asset
     */
    this.uuidPropList = [];

    // host objects need to load
    // (不用存hostList因为它的uuid可以从asset上获得)

    /**
     * @property {string} hostProp - the corresponding field name which referenced to the host object
     */
    this.hostProp = '';
    ///**
    // * @property {Fire.Asset[]} hostObjList - the obj list whose corresponding host object needs to load
    // */
    //this.hostObjList = [];
    ///**
    // * @property {string[]} hostPropList - the corresponding field name which referenced to the host object
    // */
    //this.hostPropList = [];
};

Fire._DeserializeInfo.prototype.reset = function () {
    this.uuidList.length = 0;
    this.uuidObjList.length = 0;
    this.uuidPropList.length = 0;
    this.hostProp = '';
    //this.hostObjList.length = 0;
    //this.hostPropList.length = 0;
};

Fire._DeserializeInfo.prototype.getUuidOf = function (obj, propName) {
    for (var i = 0; i < this.uuidObjList.length; i++) {
        if (this.uuidObjList[i] === obj && this.uuidPropList[i] === propName) {
            return this.uuidList[i];
        }
    }
    return "";
};
<commit_msg>support custom class finder for deserialization<commit_after>
var _Deserializer = (function () {

    function _Deserializer(data, result, isEditor, classFinder) {
        this._editor = isEditor;
        this._classFinder = classFinder;

        this._idList = [];
        this._idObjList = [];
        this._idPropList = [];
        this.result = result || new Fire._DeserializeInfo();

        var jsonObj = null;
        if (typeof data === 'string') {
            jsonObj = JSON.parse(data);
        }
        else {
            jsonObj = data;
        }

        if (Array.isArray(jsonObj)) {
            var jsonList = jsonObj;
            var refCount = jsonList.length;
            this.deserializedList = new Array(refCount);
            // deserialize
            for (var i = 0; i < refCount; i++) {
                if (jsonList[i]) {
                    this.deserializedList[i] = _deserializeObject(this, jsonList[i]);
                }
            }
            this.deserializedData = refCount > 0 ? this.deserializedList[0] : [];

            //// callback
            //for (var j = 0; j < refCount; j++) {
            //    if (referencedList[j].onAfterDeserialize) {
            //        referencedList[j].onAfterDeserialize();
            //    }
            //}
        }
        else {
            this.deserializedList = new Array(1);
            this.deserializedData = jsonObj ? _deserializeObject(this, jsonObj) : null;
            this.deserializedList[0] = this.deserializedData;

            //// callback
            //if (deserializedData.onAfterDeserialize) {
            //    deserializedData.onAfterDeserialize();
            //}
        }

        // dereference
        _dereference(this);
    }

    var _dereference = function (self) {
        // 这里不采用遍历反序列化结果的方式，因为反序列化的结果如果引用到复杂的外部库，很容易堆栈溢出。
        var deserializedList = self.deserializedList;
        for (var i = 0, len = self._idList.length; i < len; i++) {
            var propName = self._idPropList[i];
            var id = self._idList[i];
            self._idObjList[i][propName] = deserializedList[id];
        }
    };

    function _deserializeObjField (self, obj, jsonObj, propName) {
        var id = jsonObj.__id__;
        if (typeof id === 'undefined') {
            var uuid = jsonObj.__uuid__;
            if (uuid) {
                self.result.uuidList.push(uuid);
                self.result.uuidObjList.push(obj);
                self.result.uuidPropList.push(propName);
            }
            else {
                obj[propName] = _deserializeObject(self, jsonObj);
            }
        }
        else {
            var dObj = self.deserializedList[id];
            if (dObj) {
                obj[propName] = dObj;
            }
            else {
                self._idList.push(id);
                self._idObjList.push(obj);
                self._idPropList.push(propName);
            }
        }
    }

    function _deserializePrimitiveObject (self, instance, serialized) {
        for (var propName in instance) {    // 遍历 instance，如果具有类型，才不会把 __type__ 也读进来
            var prop = serialized[propName];
            if (typeof prop !== 'undefined' && serialized.hasOwnProperty(propName)) {
                if (typeof prop !== 'object') {
                    instance[propName] = prop;
                }
                else {
                    if (prop) {
                        if ( !prop.__uuid__ && typeof prop.__id__ === 'undefined' ) {
                            instance[propName] = _deserializeObject(self, prop);
                        }
                        else {
                            _deserializeObjField(self, instance, prop, propName);
                        }
                    }
                    else {
                        instance[propName] = null;
                    }
                }
            }
        }
    }

    /**
     * @param {Object} serialized - The obj to deserialize, must be non-nil
     */
    var _deserializeObject = function (self, serialized) {
        var propName, prop;
        var obj = null;
        var klass = null;
        if (serialized.__type__) {
            klass = self._classFinder(serialized.__type__);
            if (!klass) {
                Fire.error('[Fire.deserialize] unknown type: ' + serialized.__type__);
                return null;
            }
            // instantiate a new object
            obj = new klass();
        }
        else if (!Array.isArray(serialized)) {
            // embedded primitive javascript object
            obj = serialized;
            //// jshint -W010
            //obj = new Object();
            //// jshint +W010
        }
        else {
            // array
            obj = serialized;
            for (var i = 0; i < serialized.length; i++) {
                prop = serialized[i];
                if (typeof prop === 'object' && prop) {
                    if (!prop.__uuid__ && typeof prop.__id__ === 'undefined') {
                        obj[i] = _deserializeObject(self, prop);
                    }
                    else {
                        _deserializeObjField(self, obj, prop, '' + i);
                    }
                }
            }
            return serialized;
        }

        // parse property
        if (klass && Fire._isFireClass(klass)) {
            var props = klass.__props__;
            if (!props) {
                return obj;
            }
            for (var p = 0; p < props.length; p++) {
                propName = props[p];
                var attrs = Fire.attr(klass, propName);
                // assume all prop in __props__ must have attr
                var hostType = attrs.hostType;
                if (!hostType) {
                    if (attrs.serializable === false) {
                        continue;   // skip nonSerialized
                    }
                    if (!self._editor && attrs.editorOnly) {
                        continue;   // skip editor only if not editor
                    }
                    prop = serialized[propName];
                    if (typeof prop !== 'undefined') {
                        if (typeof prop !== 'object') {
                            obj[propName] = prop;
                        }
                        else {
                            if (prop) {
                                if ( !prop.__uuid__ && typeof prop.__id__ === 'undefined' ) {
                                    obj[propName] = _deserializeObject(self, prop);
                                }
                                else {
                                    _deserializeObjField(self, obj, prop, propName);
                                }
                            }
                            else {
                                obj[propName] = null;
                            }
                        }
                    }
                }
                else {
                    // always load host objects even if property not serialized
                    if (self.result.hostProp) {
                        Fire.error('not support multi host object in a file');
                        // 这里假定每个asset都有uuid，每个json只能包含一个asset，只能包含一个hostProp
                    }
                    self.result.hostProp = propName;
                }
            }
            if (props[props.length - 1] === '_$erialized') {
                // save original serialized data
                obj._$erialized = serialized;
                // parse the serialized data as primitive javascript object, so its __id__ will be dereferenced
                _deserializePrimitiveObject(self, obj._$erialized, serialized);
            }
        }
        else {
            _deserializePrimitiveObject(self, obj, serialized);
        }
        return obj;
    };

    return _Deserializer;
})();

/**
 * Deserialize json to Fire.Asset
 * @param {(string|object)} data - the serialized Fire.Asset json string or json object
 * @param {Fire._DeserializeInfo} [result] - additional loading result
 * @param {boolean} [isEditor=true] - if false, property with Fire.EditorOnly will be discarded
 * @param {object} [options=null]
 * @returns {object} the main data(asset)
 */
Fire.deserialize = function (data, result, isEditor, options) {
    isEditor = (typeof isEditor !== 'undefined') ? isEditor : true;
    var classFinder = (options && options.classFinder) || Fire.getClassByName;

    Fire._isCloning = true;
    var deserializer = new _Deserializer(data, result, isEditor, classFinder);
    Fire._isCloning = false;
    return deserializer.deserializedData;
};

/**
 * 包含反序列化时的一些信息
 * @class Fire._DeserializeInfo
 */
Fire._DeserializeInfo = function () {

    //this.urlList = [];
    //this.callbackList = [];

    // uuids(assets) need to load

    /**
     * @property {string[]} uuidList - list of the depends assets' uuid
     */
    this.uuidList = [];
    /**
     * @property {object[]} uuidObjList - the obj list whose field needs to load asset by uuid
     */
    this.uuidObjList = [];
    /**
     * @property {string[]} uuidPropList - the corresponding field name which referenced to the asset
     */
    this.uuidPropList = [];

    // host objects need to load
    // (不用存hostList因为它的uuid可以从asset上获得)

    /**
     * @property {string} hostProp - the corresponding field name which referenced to the host object
     */
    this.hostProp = '';
    ///**
    // * @property {Fire.Asset[]} hostObjList - the obj list whose corresponding host object needs to load
    // */
    //this.hostObjList = [];
    ///**
    // * @property {string[]} hostPropList - the corresponding field name which referenced to the host object
    // */
    //this.hostPropList = [];
};

Fire._DeserializeInfo.prototype.reset = function () {
    this.uuidList.length = 0;
    this.uuidObjList.length = 0;
    this.uuidPropList.length = 0;
    this.hostProp = '';
    //this.hostObjList.length = 0;
    //this.hostPropList.length = 0;
};

Fire._DeserializeInfo.prototype.getUuidOf = function (obj, propName) {
    for (var i = 0; i < this.uuidObjList.length; i++) {
        if (this.uuidObjList[i] === obj && this.uuidPropList[i] === propName) {
            return this.uuidList[i];
        }
    }
    return "";
};
<|endoftext|>


<commit_before>(function(){"use strict";function t(t,n){return _.isFunction(t)?t(n):t}function n(t,n){return function(e){return t(e,n)}}var e=window||this,r={},o={},u=_.curry;r.getAttr=function(t,n){return(n.getAttribute(t)||"").trim()},r.setAttr=function(n,e,r){return r.setAttribute(n,t(e,r)),r},r.removeAttr=function(t,n){return n.removeAttribute(t),n},_.forEach(["add","remove","toggle"],function(t){r[t+"Class"]=function(n,e){return _.forEach(n,function(n){e.classList[t](n)}),e}}),r.hasClass=function(t,n){return _.all(t,function(t){return n.classList.contains(t)})},r.getClass=function(t){return _.toArray(t.classList)},r.getStyle=function(t,n){return getComputedStyle(n)[t]},r.setStyle=function(n,e,r){return r.style[n]=t(e,r),r},r.getData=function(t,n){return r.getAttr("data-"+t,n)},r.setData=function(n,e,o){return r.setAttr("data-"+n,t(e,o),o)},r.removeData=function(t,n){return r.removeAttr("data-"+t,n)},r.watch=function(t,e,r){return e=n(e,r),r.addEventListener(t,e),function(){return r.removeEventListener(t,e)}},r.trigger=function(t,n){var e=document.createEvent("HTMLEvents");return e.initEvent(eventName,!0,!1),n.dispatchEvent(e),n},r.getInnerHTML=function(t){return t.innerHTML.trim()},r.setInnerHTML=function(n,e){return e.innerHTML=t(n,e),e},r.getOuterHTML=function(t){return t.outerHTML},r.setOuterHTML=function(n,e){return e.outerHTML=t(n,e),e},r.offset=function(t){var n=t.getBoundingClientRect();return{top:n.top+document.body.scrollTop,left:n.left+document.body.scrollLeft}},r.position=function(t){return{top:t.offsetTop,left:t.offsetLeft}},r.outerHeight=function(t){return t.offsetHeight},r.outerWidth=function(t){return t.offsetWidth},r.remove=function(t){return t.parentNode.removeChild(t),t},r.clone=function(t){return t.cloneNode(!0)},r.node=function(t,n){var e=document.createElement(t);return e.appendChild(_.isString(n)?document.createTextNode(n):n),e},r.fragment=function(){return document.createDocumentFragment()},r.insertAfter=function(t,n){return n.parentNode.insertBefore(t,n.nextSibling),n},r.insertBefore=function(t,n){return n.parentNode.insertBefore(t,n),n},r.append=function(n,e){return e.appendChild(t(n,e)),e},r.prepend=function(t,n){return n.childNodes[1]?n.insertBefore(t,n.childNodes[1]):r.append(t,n),n},r.equal=function(t,n){return t===n},r.attrEqual=function(n,e,o){return r.getAttr(n,o)===t(e,o)},r.attrMatch=function(t,n,e){return n.test(r.getAttr(t,e).toString())},r.propEqual=function(n,e,o){return r.getProp(n,o)===t(e,o)},r.propMatch=function(t,n,e){return n.test(r.getProp(t,e).toString())},r.dataEqual=function(n,e,o){return r.getData(n,o)===t(e,o)},r.dataMatch=function(t,n,e){return n.test(r.getData(t,e).toString())},r.textEqual=function(n,e){return r.getText(e)===t(n,e)},r.textMatch=function(t,n){return t.test(r.getText(n))},r.tagMatch=function(n,e){return t(n,e)===e.tagName},r.q=function(t){return t instanceof Element||t instanceof Text?t:document.querySelector(t)},r.getProp=function(t,n){return(n[t]||"").trim()},r.setProp=function(n,e,r){return r[n]=t(e,r),r},r.removeProp=function(t,n){return delete n[t],n},r.getText=function(t){return(t.textContent||"").trim()},r.setText=function(n,e){return e.textContent=t(n,e),e},r.siblings=function(t){for(var n=[],e=t.parentNode.firstChild;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},r.children=function(t){return r.siblings(t.firstChild)},r.parent=function(t){var n=t.parentNode;return n&&11!==n.nodeType?n:null},o.q=function(t,n){return _.isArray(t)?t:_.toArray(t instanceof HTMLCollection||t instanceof NodeList?t:(n||document).querySelectorAll(t))},_.forEach(r,function(t,n){"q"!==n&&(o[n]=u(function(){var n=_.initial(arguments),e=_.last(arguments);return _.reduce(e,function(e,r){return e.concat(t.apply(null,n.concat(r)))},[])},t.length),r[n]=u(t,t.length))}),"function"==typeof define&&"object"==typeof define.amd&&define.amd?(e.n=r,e.c=o,define(function(){return o}),define(function(){return r})):(e.n=r,e.c=o)}).call(this);<commit_msg>Added minified build.<commit_after>(function(){"use strict";function t(t,n){return _.isFunction(t)?t(n):t}function n(t,n){return function(e){return t(e,n)}}var e=window||this,r={},o={},u=_.curry;r.getAttr=function(t,n){return(n.getAttribute(t)||"").trim()},r.setAttr=function(n,e,r){return r.setAttribute(n,t(e,r)),r},r.removeAttr=function(t,n){return n.removeAttribute(t),n},_.forEach(["add","remove","toggle"],function(t){r[t+"Class"]=function(n,e){return _.forEach(n,function(n){e.classList[t](n)}),e}}),r.hasClass=function(t,n){return _.all(t,function(t){return n.classList.contains(t)})},r.getClass=function(t){return _.toArray(t.classList)},r.getStyle=function(t,n){return getComputedStyle(n)[t]},r.setStyle=function(n,e,r){return r.style[n]=t(e,r),r},r.getData=function(t,n){return r.getAttr("data-"+t,n)},r.setData=function(n,e,o){return r.setAttr("data-"+n,t(e,o),o)},r.removeData=function(t,n){return r.removeAttr("data-"+t,n)},r.watch=function(t,e,r){return e=n(e,r),r.addEventListener(t,e),function(){return r.removeEventListener(t,e)}},r.trigger=function(t,n){var e=document.createEvent("HTMLEvents");return e.initEvent(t,!0,!1),n.dispatchEvent(e),n},r.getInnerHTML=function(t){return t.innerHTML.trim()},r.setInnerHTML=function(n,e){return e.innerHTML=t(n,e),e},r.getOuterHTML=function(t){return t.outerHTML},r.setOuterHTML=function(n,e){return e.outerHTML=t(n,e),e},r.offset=function(t){var n=t.getBoundingClientRect();return{top:n.top+document.body.scrollTop,left:n.left+document.body.scrollLeft}},r.position=function(t){return{top:t.offsetTop,left:t.offsetLeft}},r.outerHeight=function(t){return t.offsetHeight},r.outerWidth=function(t){return t.offsetWidth},r.remove=function(t){return t.parentNode.removeChild(t),t},r.clone=function(t){return t.cloneNode(!0)},r.node=function(t,n){var e=document.createElement(t);return e.appendChild(_.isString(n)?document.createTextNode(n):n),e},r.fragment=function(){return document.createDocumentFragment()},r.insertAfter=function(t,n){return n.parentNode.insertBefore(t,n.nextSibling),n},r.insertBefore=function(t,n){return n.parentNode.insertBefore(t,n),n},r.append=function(n,e){return e.appendChild(t(n,e)),e},r.prepend=function(t,n){return n.childNodes[1]?n.insertBefore(t,n.childNodes[1]):r.append(t,n),n},r.equal=function(t,n){return t===n},r.attrEqual=function(n,e,o){return r.getAttr(n,o)===t(e,o)},r.attrMatch=function(t,n,e){return n.test(r.getAttr(t,e).toString())},r.propEqual=function(n,e,o){return r.getProp(n,o)===t(e,o)},r.propMatch=function(t,n,e){return n.test(r.getProp(t,e).toString())},r.dataEqual=function(n,e,o){return r.getData(n,o)===t(e,o)},r.dataMatch=function(t,n,e){return n.test(r.getData(t,e).toString())},r.textEqual=function(n,e){return r.getText(e)===t(n,e)},r.textMatch=function(t,n){return t.test(r.getText(n))},r.tagMatch=function(n,e){return t(n,e)===e.tagName},r.q=function(t){return t instanceof Element||t instanceof Text?t:document.querySelector(t)},r.getProp=function(t,n){return(n[t]||"").trim()},r.setProp=function(n,e,r){return r[n]=t(e,r),r},r.removeProp=function(t,n){return delete n[t],n},r.getText=function(t){return(t.textContent||"").trim()},r.setText=function(n,e){return e.textContent=t(n,e),e},r.siblings=function(t){for(var n=[],e=t.parentNode.firstChild;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},r.children=function(t){return r.siblings(t.firstChild)},r.parent=function(t){var n=t.parentNode;return n&&11!==n.nodeType?n:null},o.q=function(t,n){return _.isArray(t)?t:_.toArray(t instanceof HTMLCollection||t instanceof NodeList?t:(n||document).querySelectorAll(t))},_.forEach(r,function(t,n){"q"!==n&&(o[n]=u(function(){var n=_.initial(arguments),e=_.last(arguments);return _.reduce(e,function(e,r){return e.concat(t.apply(null,n.concat(r)))},[])},t.length),r[n]=u(t,t.length))}),"function"==typeof define&&"object"==typeof define.amd&&define.amd?(e.n=r,e.c=o,define(function(){return o}),define(function(){return r})):(e.n=r,e.c=o)}).call(this);<|endoftext|>


<commit_before>
/**
 * Manages the creating or editing of a single polygon on the video.
 * Consists of a toolbar, an overlay, and the polygon inside the overlay.
 *
 * Click to place or remove a draggable point. Points should be
 * put down in clockwise order.
 */
class PolygonEditor {
    constructor(annotator){
        this.annotator = annotator;
        this.baseZ = 2147483649 + 5;
        this.$breadcrumbs = [];

        // Create the video overlay
        this.$clickSurface = $("

").appendTo(this.annotator.player.$container);
        this.$clickSurface.css("z-index", this.baseZ);
        this.$clickSurface.click((event) => {
            this.OnSurfaceClick(event);
        });

        // Create the poly object
        this.$poly = $("

").appendTo(this.annotator.player.$container);
        this.$poly.css("z-index", this.$clickSurface.css("z-index") + 1);

        this.ResizeOverlay();
        this.annotator.player.$container.on("OnFullscreenChange", (event, setFullscreen) => this.ResizeOverlay());

        // Create the toolbar up top
        this.$bar = $("

").appendTo(this.annotator.player.$container);
        this.$postToolbar = $("

").appendTo(this.$bar);
        // Invisible expanding divider
        this.$postToolbar.append($("

Edit Polygon

").css("flex-grow", 1).css("order", 0));

        // Create undo button
        this.$undoButton = $("<button>Remove Last Point</button>").button({
            icon: "fa fa-undo",
            showLabel: false
        });
        this.$undoButton.css("margin-right", "15px");
        this.$undoButton.attr('title', "Remove last point");
        this.$undoButton.click(() => {
            this.RemoveLastBreadcrumb();
        });
        this.RegisterElement(this.$undoButton, this.$postToolbar, 1, 'flex-end');

        // Create the confirm button
        this.$confirmButton = $("<button>Finish polygon</button>").button({
            icon: "fa fa-check",
            showLabel: false
        });
        this.$confirmButton.attr('title', "Finish polygon");
        this.$confirmButton.addClass("annotator-confirm-button");
        this.$confirmButton.click(() => {
            this.originalJSON = this.GetJSON();
            this.Done();
            this.annotator.$container.trigger("OnPolygonEditingEnded");
        });
        this.RegisterElement(this.$confirmButton, this.$postToolbar, 3, 'flex-end');

        // Create the cancel button
        this.$cancelButton = $("<button>Cancel polygon editing</button>").button({
            icon: "fa fa-remove",
            showLabel: false
        });
        this.$cancelButton.addClass("annotator-cancel-button");
        this.$cancelButton.attr('title', "Cancel polygon editing");
        this.$cancelButton.click(() => {
            //Restore the original state
            this.Restore();
            this.Done();
            this.annotator.$container.trigger("OnPolygonEditingEnded");
        });
        this.RegisterElement(this.$cancelButton, this.$postToolbar, 2, 'flex-end');

        this.Done();
    }

    OnSurfaceClick(event){
        // Add a breadcrumb on click
        let target = $(event.currentTarget);
        let x = event.pageX - target.offset().left;
        let y = event.pageY - target.offset().top;

        let xPercent = (x / target.width()) * 100;
        let yPercent = (y / target.height()) * 100;

        this.AddBreadcrumb(xPercent, yPercent);

        //this.newPolyPoints.push([xPercent.toFixed(3), yPercent.toFixed(3)]);
        this.UpdatePolyClipping();
    }

    /**
     * Creates a new breadcrumb at the given (x, y) point on the
     * clickSurface, where x and y are percentages from 0 to 100.
     */
    AddBreadcrumb(xPercent, yPercent){
        let $breadcrumb = $("

");
        $breadcrumb.appendTo(this.$clickSurface);
        $breadcrumb.css("position", "absolute");

        // Percentage representations of breadcrumb width and height
        let offPercentX = ($breadcrumb.outerWidth() / this.$clickSurface.width()) * 100;
        let offPercentY = ($breadcrumb.outerHeight() / this.$clickSurface.height()) * 100;

        $breadcrumb.css("left", (xPercent - (offPercentX / 2)).toString() + "%");
        $breadcrumb.css("top", (yPercent - (offPercentY / 2)).toString() + "%");
        $breadcrumb.css("z-index", this.baseZ + 5);

        $breadcrumb.draggable({
            //containment: "parent",
            drag: () => {
                // Recalculate percentages (mangled by jQuery UI draggable code)
                // See http://stackoverflow.com/a/23673462
                var l = ( 100 * parseFloat($breadcrumb.css("left")) / parseFloat($breadcrumb.parent().css("width")) )+ "%" ;
                var t = ( 100 * parseFloat($breadcrumb.css("top")) / parseFloat($breadcrumb.parent().css("height")) )+ "%" ;
                $breadcrumb.css("left" , l);
                $breadcrumb.css("top" , t);
                this.UpdatePolyClipping();
            }
        });
        $breadcrumb.click((event) => {
            // Remove the breadcrumb on click
            event.stopPropagation();
            $breadcrumb.remove();
            this.$breadcrumbs.splice(this.$breadcrumbs.indexOf($breadcrumb), 1);
            this.UpdatePolyClipping();
            this.UpdateBreadcrumbColoring();
        });

        this.$breadcrumbs.push($breadcrumb);

        //this.UpdatePolyClipping();
        this.UpdateBreadcrumbColoring();
    }

    /**
     * Removes the last-placed breadcrumb from the list
     * and updates the view.
     */
    RemoveLastBreadcrumb(){
        let $removed = this.$breadcrumbs.pop();
        $removed.remove();
        this.UpdatePolyClipping();
        this.UpdateBreadcrumbColoring();
    }

    /**
     * Gets the center of the breadcrumb as an (x, y) pair
     * representing the percentage distance from the top and left
     * of the click surface (0% - 100%).
     */
    GetCenterPercentage($breadcrumb){

        let topPercent = ($breadcrumb.position().top / $breadcrumb.parent().height()) * 100;
        let leftPercent = ($breadcrumb.position().left / $breadcrumb.parent().width()) * 100;

        // Percentage values for the dimensions of the breadcrumb relative to the click surface
        let offPercentX = ($breadcrumb.outerWidth() / $breadcrumb.parent().width()) * 100;
        let offPercentY = ($breadcrumb.outerHeight() / $breadcrumb.parent().height()) * 100;

        return {
            x: leftPercent + (offPercentX / 2.0),
            y: topPercent + (offPercentY / 2.0)
        }
    }

    Reset(){

        // Remove all breadcrumbs
        for(let $breadcrumb of this.$breadcrumbs){
            $breadcrumb.remove();
        }
        this.$breadcrumbs = [];

        // Remove the poly if it already exists
        // if(this.$poly != null){
        //     this.$poly.remove();
        // }
    }

    Restore(){
        this.InitPoly(this.originalJSON);
    }

    InitPoly(pointsJSON = null){
        this.Reset();

        // If JSON was specified, generate breadcrumbs from it.
        if(pointsJSON != null){
            // Parse the points array from the annotation
            let pointsData = JSON.parse(pointsJSON);

            // Put down the breadcrumbs
            for(let point of pointsData){
                this.AddBreadcrumb(point[0], point[1]);
            }
        }

        this.UpdatePolyClipping();

        this.originalJSON = pointsJSON;
    }

    UpdatePolyClipping(){
        if(this.$breadcrumbs.length < 3){
            this.$poly.clipPath([], {
                svgDefId: 'annotatorPolyEditorSvg'
            });
            return;
        }

        let points = this.$breadcrumbs.map(($crumb) => {
            let pos = this.GetCenterPercentage($crumb);
            return [pos.x, pos.y];
        });

        this.$poly.clipPath(points, {
            isPercentage: true,
            svgDefId: 'annotatorPolyEditorSvg'
        });

    }

    UpdateBreadcrumbColoring(){
        for(let i = 0; i < this.$breadcrumbs.length; i++){
            let $crumb = this.$breadcrumbs[i];
            // Recolor each breadcrumb
            let color = "#000000";

            if (i == this.$breadcrumbs.length - 1) {
                color = "#FF0000";
            }
            else if (i == 0){
                color = "#00FF00";
            }
            this.$breadcrumbs[i].css("border-color", color);
        }
    }

    /**
     * Gets an array of percentages representing the x and y percentages of each
     * point in the polygon.
     */
    GetJSON(){
        // Extract the coordinates from the crumbs and put them in the array
        let points = [];
        for(let crumb of this.$breadcrumbs){
            let point = this.GetCenterPercentage(crumb);
            points.push([point.x.toString(), point.y.toString()]);
        }

        return JSON.stringify(points);
    }

    BeginEditing(){
        this.$clickSurface.show();
        this.$poly.show();
        this.$bar.show();
        this.UpdatePolyClipping();
    }

    Done(){
        this.$clickSurface.hide();
        this.$poly.hide();
        this.$bar.hide();
    }

    ResizeOverlay(){
        // Resize video overlay to fit actual video dimensions
        let videoDims = this.annotator.player.GetVideoDimensions();
        this.$clickSurface.css('width', videoDims.width);
        this.$clickSurface.css('height', videoDims.height);
        let heightDiff = (this.annotator.player.$video.height() - videoDims.height) / 2;
        this.$clickSurface.css('top', heightDiff);

        this.$poly.width(videoDims.width);
        this.$poly.height(videoDims.height);
        this.$poly.css("top", heightDiff);
    }

    RegisterElement($element, $container, order, justification = 'flex-start'){
        $element.css('order', order);
        $element.css('align-self', justification);
        // Sets grow [shrink] [basis]
        //$element.css('flex', '0 0 auto');
        $container.append($element);
    }

    ShowJustPolygon(){
        this.$poly.show();
        //this.UpdatePolyClipping();
    }

}

export { PolygonEditor };<commit_msg>Disable persistent poly editor for now<commit_after>
/**
 * Manages the creating or editing of a single polygon on the video.
 * Consists of a toolbar, an overlay, and the polygon inside the overlay.
 *
 * Click to place or remove a draggable point. Points should be
 * put down in clockwise order.
 */
class PolygonEditor {
    constructor(annotator){
        this.annotator = annotator;
        this.baseZ = 2147483649;
        this.$breadcrumbs = [];

        // Create the video overlay
        this.$clickSurface = $("

").appendTo(this.annotator.player.$container);
        this.$clickSurface.css("z-index", this.baseZ);
        this.$clickSurface.click((event) => {
            this.OnSurfaceClick(event);
        });

        // Create the poly object
        this.$poly = $("

").appendTo(this.annotator.player.$container);
        this.$poly.css("z-index", this.baseZ + 1);

        this.ResizeOverlay();
        this.annotator.player.$container.on("OnFullscreenChange", (event, setFullscreen) => this.ResizeOverlay());

        // Create the toolbar up top
        this.$bar = $("

").appendTo(this.annotator.player.$container);
        this.$postToolbar = $("

").appendTo(this.$bar);
        // Invisible expanding divider
        this.$postToolbar.append($("

Edit Polygon

").css("flex-grow", 1).css("order", 0));

        // Create undo button
        this.$undoButton = $("<button>Remove Last Point</button>").button({
            icon: "fa fa-undo",
            showLabel: false
        });
        this.$undoButton.css("margin-right", "15px");
        this.$undoButton.attr('title', "Remove last point");
        this.$undoButton.click(() => {
            this.RemoveLastBreadcrumb();
        });
        this.RegisterElement(this.$undoButton, this.$postToolbar, 1, 'flex-end');

        // Create the confirm button
        this.$confirmButton = $("<button>Finish polygon</button>").button({
            icon: "fa fa-check",
            showLabel: false
        });
        this.$confirmButton.attr('title', "Finish polygon");
        this.$confirmButton.addClass("annotator-confirm-button");
        this.$confirmButton.click(() => {
            this.originalJSON = this.GetJSON();
            this.Done();
            this.annotator.$container.trigger("OnPolygonEditingEnded");
        });
        this.RegisterElement(this.$confirmButton, this.$postToolbar, 3, 'flex-end');

        // Create the cancel button
        this.$cancelButton = $("<button>Cancel polygon editing</button>").button({
            icon: "fa fa-remove",
            showLabel: false
        });
        this.$cancelButton.addClass("annotator-cancel-button");
        this.$cancelButton.attr('title', "Cancel polygon editing");
        this.$cancelButton.click(() => {
            //Restore the original state
            this.Restore();
            this.Done();
            this.annotator.$container.trigger("OnPolygonEditingEnded");
        });
        this.RegisterElement(this.$cancelButton, this.$postToolbar, 2, 'flex-end');

        this.Done();
    }

    OnSurfaceClick(event){
        // Add a breadcrumb on click
        let target = $(event.currentTarget);
        let x = event.pageX - target.offset().left;
        let y = event.pageY - target.offset().top;

        let xPercent = (x / target.width()) * 100;
        let yPercent = (y / target.height()) * 100;

        this.AddBreadcrumb(xPercent, yPercent);

        //this.newPolyPoints.push([xPercent.toFixed(3), yPercent.toFixed(3)]);
        this.UpdatePolyClipping();
    }

    /**
     * Creates a new breadcrumb at the given (x, y) point on the
     * clickSurface, where x and y are percentages from 0 to 100.
     */
    AddBreadcrumb(xPercent, yPercent){
        let $breadcrumb = $("

");
        $breadcrumb.appendTo(this.$clickSurface);
        $breadcrumb.css("position", "absolute");

        // Percentage representations of breadcrumb width and height
        let offPercentX = ($breadcrumb.outerWidth() / this.$clickSurface.width()) * 100;
        let offPercentY = ($breadcrumb.outerHeight() / this.$clickSurface.height()) * 100;

        $breadcrumb.css("left", (xPercent - (offPercentX / 2)).toString() + "%");
        $breadcrumb.css("top", (yPercent - (offPercentY / 2)).toString() + "%");
        $breadcrumb.css("z-index", this.baseZ - 50);

        $breadcrumb.draggable({
            //containment: "parent",
            drag: () => {
                // Recalculate percentages (mangled by jQuery UI draggable code)
                // See http://stackoverflow.com/a/23673462
                var l = ( 100 * parseFloat($breadcrumb.css("left")) / parseFloat($breadcrumb.parent().css("width")) )+ "%" ;
                var t = ( 100 * parseFloat($breadcrumb.css("top")) / parseFloat($breadcrumb.parent().css("height")) )+ "%" ;
                $breadcrumb.css("left" , l);
                $breadcrumb.css("top" , t);
                this.UpdatePolyClipping();
            }
        });
        $breadcrumb.click((event) => {
            // Remove the breadcrumb on click
            event.stopPropagation();
            $breadcrumb.remove();
            this.$breadcrumbs.splice(this.$breadcrumbs.indexOf($breadcrumb), 1);
            this.UpdatePolyClipping();
            this.UpdateBreadcrumbColoring();
        });

        this.$breadcrumbs.push($breadcrumb);

        //this.UpdatePolyClipping();
        this.UpdateBreadcrumbColoring();
    }

    /**
     * Removes the last-placed breadcrumb from the list
     * and updates the view.
     */
    RemoveLastBreadcrumb(){
        let $removed = this.$breadcrumbs.pop();
        $removed.remove();
        this.UpdatePolyClipping();
        this.UpdateBreadcrumbColoring();
    }

    /**
     * Gets the center of the breadcrumb as an (x, y) pair
     * representing the percentage distance from the top and left
     * of the click surface (0% - 100%).
     */
    GetCenterPercentage($breadcrumb){

        let topPercent = ($breadcrumb.position().top / $breadcrumb.parent().height()) * 100;
        let leftPercent = ($breadcrumb.position().left / $breadcrumb.parent().width()) * 100;

        // Percentage values for the dimensions of the breadcrumb relative to the click surface
        let offPercentX = ($breadcrumb.outerWidth() / $breadcrumb.parent().width()) * 100;
        let offPercentY = ($breadcrumb.outerHeight() / $breadcrumb.parent().height()) * 100;

        return {
            x: leftPercent + (offPercentX / 2.0),
            y: topPercent + (offPercentY / 2.0)
        }
    }

    Reset(){

        // Remove all breadcrumbs
        for(let $breadcrumb of this.$breadcrumbs){
            $breadcrumb.remove();
        }
        this.$breadcrumbs = [];

        // Remove the poly if it already exists
        // if(this.$poly != null){
        //     this.$poly.remove();
        // }
    }

    Restore(){
        this.InitPoly(this.originalJSON);
    }

    InitPoly(pointsJSON = null){
        this.Reset();

        // If JSON was specified, generate breadcrumbs from it.
        if(pointsJSON != null){
            // Parse the points array from the annotation
            let pointsData = JSON.parse(pointsJSON);

            // Put down the breadcrumbs
            for(let point of pointsData){
                this.AddBreadcrumb(point[0], point[1]);
            }
        }

        this.UpdatePolyClipping();

        this.originalJSON = pointsJSON;
    }

    UpdatePolyClipping(){
        if(this.$breadcrumbs.length < 3){
            this.$poly.clipPath([], {
                svgDefId: 'annotatorPolyEditorSvg'
            });
            return;
        }

        let points = this.$breadcrumbs.map(($crumb) => {
            let pos = this.GetCenterPercentage($crumb);
            return [pos.x, pos.y];
        });

        this.$poly.clipPath(points, {
            isPercentage: true,
            svgDefId: 'annotatorPolyEditorSvg'
        });

    }

    UpdateBreadcrumbColoring(){
        for(let i = 0; i < this.$breadcrumbs.length; i++){
            let $crumb = this.$breadcrumbs[i];
            // Recolor each breadcrumb
            let color = "#000000";

            if (i == this.$breadcrumbs.length - 1) {
                color = "#FF0000";
            }
            else if (i == 0){
                color = "#00FF00";
            }
            this.$breadcrumbs[i].css("border-color", color);
        }
    }

    /**
     * Gets an array of percentages representing the x and y percentages of each
     * point in the polygon.
     */
    GetJSON(){
        // Extract the coordinates from the crumbs and put them in the array
        let points = [];
        for(let crumb of this.$breadcrumbs){
            let point = this.GetCenterPercentage(crumb);
            points.push([point.x.toString(), point.y.toString()]);
        }

        return JSON.stringify(points);
    }

    BeginEditing(){
        this.$clickSurface.show();
        this.$poly.show();
        this.$bar.show();
        this.UpdatePolyClipping();
    }

    Done(){
        this.$clickSurface.hide();
        this.$poly.hide();
        this.$bar.hide();
    }

    ResizeOverlay(){
        // Resize video overlay to fit actual video dimensions
        let videoDims = this.annotator.player.GetVideoDimensions();
        this.$clickSurface.css('width', videoDims.width);
        this.$clickSurface.css('height', videoDims.height);
        let heightDiff = (this.annotator.player.$video.height() - videoDims.height) / 2;
        this.$clickSurface.css('top', heightDiff);

        this.$poly.width(videoDims.width);
        this.$poly.height(videoDims.height);
        this.$poly.css("top", heightDiff);
    }

    RegisterElement($element, $container, order, justification = 'flex-start'){
        $element.css('order', order);
        $element.css('align-self', justification);
        // Sets grow [shrink] [basis]
        //$element.css('flex', '0 0 auto');
        $container.append($element);
    }

    ShowJustPolygon(){
        //this.$poly.show();
        //this.UpdatePolyClipping();
    }

}

export { PolygonEditor };<|endoftext|>