<commit_before>/* data field is for preloading a set of object data, where object would look like this:
 * {
      img: {
        srcset: 'https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/7ef4dba2-6810-4b1c-ba5e-8d5089b0358e/320.jpg 320w, https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/50fde484-13e4-4e94-a44f-2765ef4a5d21/640.jpg 640w, https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/85961b53-60c9-4888-ab3f-6db107f9ede1/960.jpg 960w',
        src: 'https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/7ef4dba2-6810-4b1c-ba5e-8d5089b0358e/640.jpg',
        alt: 'poster'
      },
      title: 'Colour lithograph poster advertising the Nord Express train from Paris to Riga',
      href: 'https://collections.vam.ac.uk/item/O89673'
    }
*/

module.exports = {
  title: 'Object Shuffler',
  label: 'Object Shuffler',
  context: {
    objectShufflerData: [
      {
        title: 'Posters',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?profile=ymal&q=poster',
        data: [
        ]
      },
      {
        title: 'Netsuke',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?profile=ymal&q_object_type=netsuke',
        data: [
        ]
      },
      {
        title: 'Lego',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?profile=ymal&q=lego',
        data: [
        ]
      },
      {
        title: 'Bikes',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?profile=ymal&q=bicycle',
        data: [
        ]
      }
    ]
  }
};
<commit_msg>object-shuffler: API updates<commit_after>/* data field is for preloading a set of object data, where object would look like this:
 * {
      img: {
        srcset: 'https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/7ef4dba2-6810-4b1c-ba5e-8d5089b0358e/320.jpg 320w, https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/50fde484-13e4-4e94-a44f-2765ef4a5d21/640.jpg 640w, https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/85961b53-60c9-4888-ab3f-6db107f9ede1/960.jpg 960w',
        src: 'https://vanda-production-assets.s3.amazonaws.com/2019/01/10/14/46/48/7ef4dba2-6810-4b1c-ba5e-8d5089b0358e/640.jpg',
        alt: 'poster'
      },
      title: 'Colour lithograph poster advertising the Nord Express train from Paris to Riga',
      href: 'https://collections.vam.ac.uk/item/O89673'
    }
*/

module.exports = {
  title: 'Object Shuffler',
  label: 'Object Shuffler',
  context: {
    objectShufflerData: [
      {
        title: 'Posters',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?search_profile=ymal&q=poster',
        data: [
        ]
      },
      {
        title: 'Netsuke',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?search_profile=ymal&q_object_type=netsuke',
        data: [
        ]
      },
      {
        title: 'Lego',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?search_profile=ymal&q=lego',
        data: [
        ]
      },
      {
        title: 'Bikes',
        feed: 'http://vam-etc-test-api.azureedge.net/api/v2/objects/search?search_profile=ymal&q=bicycle',
        data: [
        ]
      }
    ]
  }
};
<|endoftext|>


<commit_before>"use strict";

meta.Math.AdvAABB = function(minX, minY, maxX, maxY)
{
	this.minX = minX;
	this.minY = minY;
	this.maxX = maxX;
	this.maxY = maxY;

	this.initWidth = maxX - minX;
	this.initHeight = maxY - minY;
	this.initHalfWidth = this.initWidth / 2;
	this.initHalfHeight = this.initHeight / 2;

	this.width = this.initWidth;
	this.height = this.initHeight;
	this.halfWidth = this.width / 2;
	this.halfHeight = this.height / 2;

	this.x = Math.floor(minX + this.halfWidth);
	this.y = Math.floor(minY + this.halfHeight);
	this.scaleX = 1.0;
	this.scaleY = 1.0;
};

meta.Math.AdvAABB.prototype =
{
	move: function(x, y)
	{
		this.x += x;
		this.y += y;
		this.minX += x;
		this.minY += y;
		this.maxX += x;
		this.maxY += y;
	},

	set: function(x, y)
	{
		this.x = Math.floor(x);
		this.y = Math.floor(y);

		this.minX = this.x - this.halfWidth;
		this.minY = this.y - this.halfHeight;
		this.maxX = this.x + this.halfWidth;
		this.maxY = this.y + this.halfHeight;
	},

	resize: function(width, height)
	{
		this.initWidth = width;
		this.initHeight = height;
		this.initHalfWidth = width / 2;
		this.initHalfHeight = height / 2;

		this.width = width * this.scaleX;
		this.height = height * this.scaleY;
		this.halfWidth = this.width / 2;
		this.halfHeight = this.height / 2;

		this.maxX = this.minX + this.width;
		this.maxY = this.minY + this.height;
	},

	moveToAndResize: function(x, y, width, height)
	{
		this.x = Math.floor(x);
		this.y = Math.floor(y);
		this.initWidth = width;
		this.initHeight = height;
		this.initHalfWidth = width / 2;
		this.initHalfHeight = height / 2;

		var prevHalfWidth = this.halfWidth;
		var prevHalfHeight = this.halfHeight;
		this.width = this.initWidth * this.scaleX;
		this.height = this.initHeight * this.scaleY;
		this.halfWidth = this.width / 2;
		this.halfHeight = this.height / 2;
		var diffHalfWidth = prevHalfWidth - this.halfWidth;
		var diffHalfHeight = prevHalfHeight - this.halfHeight;

		this.minX = this.x - diffHalfWidth;
		this.minY = this.y - diffHalfHeight;
		this.maxX = this.minX + this.width;
		this.maxY = this.minY + this.height;
	},

	scale: function(scaleX, scaleY)
	{
		this.scaleX = scaleX;
		this.scaleY = scaleY;

		this.width = this.initWidth * this.scaleX;
		this.height = this.initHeight * this.scaleY;
		this.halfWidth = this.width / 2;
		this.halfHeight = this.height / 2;

		this.minX = this.x - this.halfWidth;
		this.minY = this.y - this.halfHeight;
		this.maxX = this.minX + this.width;
		this.maxY = this.minY + this.height;
	},

	vsAABB: function(src)
	{
		if(this.minX > src.maxX || this.maxX < src.minX ||
			this.minY > src.maxY || this.maxY < src.minY)
		{
			return false;
		}

		return true;
	},

	vsBorderAABB: function(src)
	{
		if(this.minX >= src.maxX || this.maxX <= src.minX ||
			this.minY >= src.maxY || this.maxY <= src.minY)
		{
			return false;
		}

		return true;
	},

	vsAABBIntersection: function(src)
	{
		if(this.minX > src.maxX || this.maxX < src.minX ||
			this.minY > src.maxY || this.maxY < src.minY)
		{
			return 0;
		}

		if(this.minX > src.minX || this.minY > src.minY) { return 1; }
		if(this.maxX < src.maxX || this.maxY < src.maxY) { return 1; }

		return 2;
	},

	vsPoint: function(x, y)
	{
		if(this.minX > x || this.maxX < x) { return false; }
		if(this.minY > y || this.maxY < y) { return false; }

		return true;
	},

	vsBorderPoint: function(x, y)
	{
		if(this.minX >= x || this.maxX <= x) { return false; }
		if(this.minY >= y || this.maxY <= y) { return false; }

		return true;
	},

	getSqrDistance: function(x, y)
	{
		var tmp;
		var sqDist = 0;

		if(x < this.minX) {
			tmp = (this.minX - x);
			sqDist += tmp * tmp;
		}
		if(x > this.maxX) {
			tmp = (x - this.maxX);
			sqDist += tmp * tmp;
		}

		if(y < this.minY) {
			tmp = (this.minY - y);
			sqDist += tmp * tmp;
		}
		if(y > this.maxY) {
			tmp = (y - this.maxY);
			sqDist += tmp * tmp;
		}

		return sqDist;
	},

	getDistanceVsAABB: function(aabb)
	{
		var centerX = this.minX + ((this.maxX - this.minX) / 2);
		var centerY = this.minY + ((this.maxY - this.minY) / 2);
		var srcCenterX = aabb.minX + ((aabb.maxY - aabb.minY) / 2);
		var srcCenterY = aabb.minY + ((aabb.maxY - aabb.minY) / 2);

		var diffX = srcCenterX - centerX;
		var diffY = srcCenterY - centerY;

		return Math.sqrt((diffX * diffX) + (diffY * diffY));
	},

	isUndefined: function() {
		return (this.maxY === void(0));
	},

	draw: function(ctx)
	{
		var minX = Math.floor(this.minX);
		var minY = Math.floor(this.minY);
		var maxX = Math.floor(this.maxX);
		var maxY = Math.floor(this.maxY);

		ctx.beginPath();
		ctx.moveTo(minX, minY);
		ctx.lineTo(maxX, minY);
		ctx.lineTo(maxX, maxY);
		ctx.lineTo(minX, maxY);
		ctx.lineTo(minX, minY);
		ctx.stroke();
	},

	drawTranslated: function(ctx)
	{
		var camera = meta.camera;

		this.translate(camera._x, camera._y);
		this.draw(ctx);
		this.translate(-camera._x, -camera._y);
	},

	print: function(str)
	{
		if(str)
		{
			console.log("(AABB) " + str + " minX: " + this.minX + " minY: " + this.minY
				+ " maxX: " + this.maxX + " maxY: " + this.maxY);
		}
		else
		{
			console.log("(AABB) minX: " + this.minX + " minY: " + this.minY
				+ " maxX: " + this.maxX + " maxY: " + this.maxY);
		}
	}
};<commit_msg>Update: AABB scale.<commit_after>"use strict";

meta.Math.AdvAABB = function(minX, minY, maxX, maxY)
{
	this.minX = minX;
	this.minY = minY;
	this.maxX = maxX;
	this.maxY = maxY;

	this.initWidth = maxX - minX;
	this.initHeight = maxY - minY;
	this.initHalfWidth = this.initWidth / 2;
	this.initHalfHeight = this.initHeight / 2;

	this.width = this.initWidth;
	this.height = this.initHeight;
	this.halfWidth = this.width / 2;
	this.halfHeight = this.height / 2;

	this.x = Math.floor(minX + this.halfWidth);
	this.y = Math.floor(minY + this.halfHeight);
	this.scaleX = 1.0;
	this.scaleY = 1.0;
};

meta.Math.AdvAABB.prototype =
{
	move: function(x, y)
	{
		this.x += x;
		this.y += y;
		this.minX += x;
		this.minY += y;
		this.maxX += x;
		this.maxY += y;
	},

	set: function(x, y)
	{
		this.x = Math.floor(x);
		this.y = Math.floor(y);

		this.minX = this.x - this.halfWidth;
		this.minY = this.y - this.halfHeight;
		this.maxX = this.x + this.halfWidth;
		this.maxY = this.y + this.halfHeight;
	},

	resize: function(width, height)
	{
		this.initWidth = width;
		this.initHeight = height;
		this.initHalfWidth = width / 2;
		this.initHalfHeight = height / 2;

		this.width = width * this.scaleX;
		this.height = height * this.scaleY;
		this.halfWidth = this.width / 2;
		this.halfHeight = this.height / 2;

		this.maxX = this.minX + this.width;
		this.maxY = this.minY + this.height;
	},

	moveToAndResize: function(x, y, width, height)
	{
		this.x = Math.floor(x);
		this.y = Math.floor(y);
		this.initWidth = width;
		this.initHeight = height;
		this.initHalfWidth = width / 2;
		this.initHalfHeight = height / 2;

		this.width = this.initWidth * this.scaleX;
		this.height = this.initHeight * this.scaleY;
		this.halfWidth = this.width / 2;
		this.halfHeight = this.height / 2;

		this.minX = this.x - this.halfWidth;
		this.minY = this.y - this.halfHeight;
		this.maxX = this.minX + this.width;
		this.maxY = this.minY + this.height;
	},

	scale: function(scaleX, scaleY)
	{
		this.scaleX = scaleX;
		this.scaleY = scaleY;

		this.width = this.initWidth * this.scaleX;
		this.height = this.initHeight * this.scaleY;
		this.halfWidth = this.width / 2;
		this.halfHeight = this.height / 2;

		this.minX = this.x - this.halfWidth;
		this.minY = this.y - this.halfHeight;
		this.maxX = this.minX + this.width;
		this.maxY = this.minY + this.height;
	},

	vsAABB: function(src)
	{
		if(this.minX > src.maxX || this.maxX < src.minX ||
			this.minY > src.maxY || this.maxY < src.minY)
		{
			return false;
		}

		return true;
	},

	vsBorderAABB: function(src)
	{
		if(this.minX >= src.maxX || this.maxX <= src.minX ||
			this.minY >= src.maxY || this.maxY <= src.minY)
		{
			return false;
		}

		return true;
	},

	vsAABBIntersection: function(src)
	{
		if(this.minX > src.maxX || this.maxX < src.minX ||
			this.minY > src.maxY || this.maxY < src.minY)
		{
			return 0;
		}

		if(this.minX > src.minX || this.minY > src.minY) { return 1; }
		if(this.maxX < src.maxX || this.maxY < src.maxY) { return 1; }

		return 2;
	},

	vsPoint: function(x, y)
	{
		if(this.minX > x || this.maxX < x) { return false; }
		if(this.minY > y || this.maxY < y) { return false; }

		return true;
	},

	vsBorderPoint: function(x, y)
	{
		if(this.minX >= x || this.maxX <= x) { return false; }
		if(this.minY >= y || this.maxY <= y) { return false; }

		return true;
	},

	getSqrDistance: function(x, y)
	{
		var tmp;
		var sqDist = 0;

		if(x < this.minX) {
			tmp = (this.minX - x);
			sqDist += tmp * tmp;
		}
		if(x > this.maxX) {
			tmp = (x - this.maxX);
			sqDist += tmp * tmp;
		}

		if(y < this.minY) {
			tmp = (this.minY - y);
			sqDist += tmp * tmp;
		}
		if(y > this.maxY) {
			tmp = (y - this.maxY);
			sqDist += tmp * tmp;
		}

		return sqDist;
	},

	getDistanceVsAABB: function(aabb)
	{
		var centerX = this.minX + ((this.maxX - this.minX) / 2);
		var centerY = this.minY + ((this.maxY - this.minY) / 2);
		var srcCenterX = aabb.minX + ((aabb.maxY - aabb.minY) / 2);
		var srcCenterY = aabb.minY + ((aabb.maxY - aabb.minY) / 2);

		var diffX = srcCenterX - centerX;
		var diffY = srcCenterY - centerY;

		return Math.sqrt((diffX * diffX) + (diffY * diffY));
	},

	isUndefined: function() {
		return (this.maxY === void(0));
	},

	draw: function(ctx)
	{
		var minX = Math.floor(this.minX);
		var minY = Math.floor(this.minY);
		var maxX = Math.floor(this.maxX);
		var maxY = Math.floor(this.maxY);

		ctx.beginPath();
		ctx.moveTo(minX, minY);
		ctx.lineTo(maxX, minY);
		ctx.lineTo(maxX, maxY);
		ctx.lineTo(minX, maxY);
		ctx.lineTo(minX, minY);
		ctx.stroke();
	},

	drawTranslated: function(ctx)
	{
		var camera = meta.camera;

		this.translate(camera._x, camera._y);
		this.draw(ctx);
		this.translate(-camera._x, -camera._y);
	},

	print: function(str)
	{
		if(str)
		{
			console.log("(AABB) " + str + " minX: " + this.minX + " minY: " + this.minY
				+ " maxX: " + this.maxX + " maxY: " + this.maxY);
		}
		else
		{
			console.log("(AABB) minX: " + this.minX + " minY: " + this.minY
				+ " maxX: " + this.maxX + " maxY: " + this.maxY);
		}
	}
};<|endoftext|>


<commit_before>( function ( mw, $ ) {
	/**
	 * Notification view model
	 *
	 * @class
	 * @mixins OO.EventEmitter
	 *
	 * @constructor
	 * @param {Object} [config] Configuration object
	 * @cfg {string} [type='alert'] Notification type 'alert', 'message' or 'all'
	 * @cfg {number} [limit=25] Notification limit
	 * @cfg {string} [userLang] User language
	 */
	mw.echo.dm.NotificationsModel = function MwEchoDmNotificationsModel( config ) {
		config = config || {};

		// Mixin constructor
		OO.EventEmitter.call( this );

		// Mixin constructor
		mw.echo.dm.List.call( this );

		this.type = config.type || 'alert';
		this.limit = config.limit || 25;
		this.userLang = config.userLang || 'en';

		this.api = new mw.Api( { ajax: { cache: false } } );
		this.fetchNotificationsPromise = null;

		this.seenTime = mw.config.get( 'wgEchoSeenTime' );

		// Store references to unseen and unread notifications
		this.unseenNotifications = new mw.echo.dm.NotificationList();
		this.unreadNotifications = new mw.echo.dm.NotificationList();

		// Events
		this.aggregate( {
			seen: 'itemSeen',
			read: 'itemRead'
		} );

		this.connect( this, {
			itemSeen: 'onItemSeen',
			itemRead: 'onItemRead'
		} );
	};

	/* Initialization */

	OO.initClass( mw.echo.dm.NotificationsModel );
	OO.mixinClass( mw.echo.dm.NotificationsModel, OO.EventEmitter );
	OO.mixinClass( mw.echo.dm.NotificationsModel, mw.echo.dm.List );

	/* Events */

	/**
	 * @event updateSeenTime
	 * @param {number} seenTime Seen time
	 *
	 * Seen time has been updated
	 */

	/**
	 * @event unseenChange
	 * @param {number} count Number of unseen items
	 *
	 * Items' seen status has changed
	 */

	/**
	 * @event unreadChange
	 * @param {number} count Number of unread items
	 *
	 * Items' read status has changed
	 */

	/* Methods */

	/**
	 * Respond to item seen state change
	 *
	 * @param {mw.echo.dm.NotificationItem} item Notification item
	 * @param {boolean} isSeen Notification is seen
	 * @fires unseenChange
	 */
	mw.echo.dm.NotificationsModel.prototype.onItemSeen = function ( item, isSeen ) {
		var id = item && item.getId(),
			unseenItem = id && this.unseenNotifications.getItemById( id );

		if ( unseenItem ) {
			if ( isSeen ) {
				this.unseenNotifications.removeItems( [ unseenItem ] );
			} else {
				this.unseenNotifications.addItems( [ unseenItem ] );
			}
			this.emit( 'unseenChange', this.unseenNotifications.getItems() );
		}
	};

	/**
	 * Respond to item read state change
	 *
	 * @param {mw.echo.dm.NotificationItem} item Notification item
	 * @param {boolean} isRead Notification is read
	 * @fires unreadChange
	 */
	mw.echo.dm.NotificationsModel.prototype.onItemRead = function ( item, isRead ) {
		var id = item && item.getId(),
			unreadItem = id && this.unreadNotifications.getItemById( id );

		if ( unreadItem ) {
			if ( isRead ) {
				this.unseenNotifications.removeItems( [ unreadItem ] );
			} else {
				this.unseenNotifications.addItems( [ unreadItem ] );
			}
			this.emit( 'unreadChange', this.unreadNotifications.getItems() );
		}
	};

	/**
	 * Get the type of the notifications that this model deals with.
	 * Notifications type are given from the API: 'alert', 'message', 'all'
	 *
	 * @return {string} Notifications type
	 */
	mw.echo.dm.NotificationsModel.prototype.getType = function () {
		return this.type;
	};

	/**
	 * Get the counter of how many notifications are unseen
	 *
	 * @return {number} Number of unseen notifications
	 */
	mw.echo.dm.NotificationsModel.prototype.getUnseenCount = function () {
		return this.unseenNotifications.getItemCount();
	};

	/**
	 * Get the counter of how many notifications are unread
	 *
	 * @return {number} Number of unread notifications
	 */
	mw.echo.dm.NotificationsModel.prototype.getUnreadCount = function () {
		return this.unreadNotifications.getItemCount();
	};

	/**
	 * Set the system seen time - the last time we've marked notification as seen
	 *
	 * @param {string} Mediawiki seen timestamp in Mediawiki timestamp format
	 */
	mw.echo.dm.NotificationsModel.prototype.setSeenTime = function ( time ) {
		this.seenTime[this.type] = time;
	};

	/**
	 * Get the system seen time
	 *
	 * @return {string} Mediawiki seen timestamp in Mediawiki timestamp format
	 */
	mw.echo.dm.NotificationsModel.prototype.getSeenTime = function () {
		return this.seenTime[this.type];
	};

	/**
	 * Check whether the model is fetching notifications from the API
	 *
	 * @return {boolean} The model is in the process of fetching from the API
	 */
	mw.echo.dm.NotificationsModel.prototype.isFetchingNotifications = function () {
		return !!this.fetchNotificationsPromise;
	};

	/**
	 * Update the seen timestamp
	 *
	 * @return {jQuery.Promise} A promise that resolves with the seen timestamp
	 * @fires updateSeenTime
	 */
	mw.echo.dm.NotificationsModel.prototype.updateSeenTime = function () {
		var model = this;

		return this.api.postWithToken( 'edit', {
			action: 'echomarkseen',
			type: this.type
		} ).then( function ( data ) {
			var i, len,
				items = model.unseenNotifications.getItems(),
				time = data.query.echomarkseen.timestamp;

			// update wgEchoSeenTime value in JS (where it wouldn't
			// otherwise propagate until page reload)
			model.setSeenTime( time );

			// Update the notifications seen status
			for ( i = 0, len = items.length; i < len; i++ ) {
				items[i].toggleSeen( true );
			}
			model.unseenNotifications.clearItems();

			model.emit( 'updateSeenTime', model.getSeenTime() );
		} );
	};

	/**
	 * Mark all notifications as read
	 *
	 * @return {jQuery.Promise} A promise that resolves when all notifications
	 * were marked as read.
	 */
	mw.echo.dm.NotificationsModel.prototype.markAllRead = function () {
		var model = this,
			data = {
				action: 'echomarkread',
				uselang: this.userLang,
				sections: this.type
			};

		if ( !this.unreadNotifications.getItemCount() ) {
			return $.Deferred().resolve( 0 ).promise();
		}

		return this.api.postWithToken( 'edit', data )
			.then( function ( result ) {
				return result.query.echomarkread[model.type].rawcount || 0;
			} )
			.then( function () {
				var i, len,
					items = model.unreadNotifications.getItems();

				for ( i = 0, len = items.length; i < len; i++ ) {
					items[i].toggleRead( true );
					items[i].toggleSeen( true );
				}
				model.unreadNotifications.clearItems();
			} );
	};

	/**
	 * Fetch notifications from the API and update the notifications list.
	 *
	 * @return {jQuery.Promise} A promise that resolves with an array of notification
	 *  id's.
	 */
	mw.echo.dm.NotificationsModel.prototype.fetchNotifications = function () {
		var model = this,
			apiData = {
				action: 'query',
				meta: 'notifications',
				notsections: this.type,
				notmessageunreadfirst: 1,
				notformat: 'flyout',
				notlimit: this.limit,
				notprop: 'index|list|count',
				uselang: this.userLang
			};

		if ( !this.fetchNotificationsPromise ) {
			this.fetchNotificationsPromise = this.api.get( apiData )
				.then( function ( result ) {
					var notifData, i, len, $content, wasSeen, wasRead, notificationModel,
						optionItems = [],
						idArray = [],
						data = result.query.notifications;

					for ( i = 0, len = data.index.length; i < len; i++ ) {
						notifData = data.list[ data.index[i] ];
						// TODO: This should really be formatted better, and the OptionWidget
						// should be the one that displays whatever icon relates to this notification
						// according to its type.
						$content = $( $.parseHTML( notifData['*'] ) );

						wasRead = !!notifData.read;
						wasSeen = notifData.timestamp.mw <= model.getSeenTime();
						notificationModel = new mw.echo.dm.NotificationItem(
							notifData.id,
							{
								read: wasRead,
								seen: wasSeen,
								timestamp: notifData.timestamp.mw,
								category: notifData.category,
								content: $content,
								// Hack: Get the primary link from the $content
								primaryUrl: $content.find( '.mw-echo-notification-primary-link' ).attr( 'href' )
							}
						);

						idArray.push( notifData.id );
						optionItems.push( notificationModel );
					}
					model.addItems( optionItems );

					return idArray;
				} )
				.then( function ( idArray ) {
					model.fetchNotificationsPromise = null;

					return idArray;
				} );
		}

		return this.fetchNotificationsPromise;
	};

	/**
	 * Update the unread and unseen tracking lists when we add items
	 *
	 * @param {mw.echo.dm.NotificationItem[]} items Items to add
	 * @param {number} index Index to add items at
	 */
	mw.echo.dm.NotificationsModel.prototype.addItems = function ( items, index ) {
		var i, len;

		for ( i = 0, len = items.length; i < len; i++ ) {
			if ( !items[i].isRead() ) {
				this.unreadNotifications.addItems( [ items[i] ] );
			}
			if ( !items[i].isSeen() ) {
				this.unseenNotifications.addItems( [ items[i] ] );
			}
		}

		// Parent
		mw.echo.dm.List.prototype.addItems.call( this, items, index );
	};

	/**
	 * Update the unread and unseen tracking lists when we remove items
	 *
	 * @param {mw.echo.dm.NotificationItem[]} items Items to remove
	 * @param {number} index Index to add items at
	 */
	mw.echo.dm.NotificationsModel.prototype.removeItems = function ( items ) {
		var i, len;

		for ( i = 0, len = items.length; i < len; i++ ) {
			this.unreadNotifications.removeItems( [ items[i] ] );
			this.unseenNotifications.removeItems( [ items[i] ] );
		}

		// Parent
		mw.echo.dm.List.prototype.removeItems.call( this, items );
	};

	/**
	 * Update the unread and unseen tracking lists when we clear items
	 */
	mw.echo.dm.NotificationsModel.prototype.clearItems = function () {
		this.unreadNotifications.clearItems();
		this.unseenNotifications.clearItems();

		// Parent
		mw.echo.dm.List.prototype.clearItems.call( this );
	};

	/**
	 * Query the API for unread count of the notifications in this model
	 *
	 * @return {jQuery.Promise} jQuery promise that's resolved when the unread count is fetched
	 *  and the badge label is updated.
	 */
	mw.echo.dm.NotificationsModel.prototype.fetchUnreadCountFromApi = function () {
		var apiData = {
				action: 'query',
				meta: 'notifications',
				notsections: this.getType(),
				notmessageunreadfirst: 1,
				notlimit: this.limit,
				notprop: 'index|count',
				uselang: this.userLang
			};

		return this.api.get( apiData )
			.then( function ( result ) {
				return OO.getProp( result.query, 'notifications', 'rawcount' ) || 0;
			} );
	};
} )( mediaWiki, jQuery );
<commit_msg>Don't wait for the API to update seen status of notifications<commit_after>( function ( mw, $ ) {
	/**
	 * Notification view model
	 *
	 * @class
	 * @mixins OO.EventEmitter
	 *
	 * @constructor
	 * @param {Object} [config] Configuration object
	 * @cfg {string} [type='alert'] Notification type 'alert', 'message' or 'all'
	 * @cfg {number} [limit=25] Notification limit
	 * @cfg {string} [userLang] User language
	 */
	mw.echo.dm.NotificationsModel = function MwEchoDmNotificationsModel( config ) {
		config = config || {};

		// Mixin constructor
		OO.EventEmitter.call( this );

		// Mixin constructor
		mw.echo.dm.List.call( this );

		this.type = config.type || 'alert';
		this.limit = config.limit || 25;
		this.userLang = config.userLang || 'en';

		this.api = new mw.Api( { ajax: { cache: false } } );
		this.fetchNotificationsPromise = null;

		this.seenTime = mw.config.get( 'wgEchoSeenTime' );

		// Store references to unseen and unread notifications
		this.unseenNotifications = new mw.echo.dm.NotificationList();
		this.unreadNotifications = new mw.echo.dm.NotificationList();

		// Events
		this.aggregate( {
			seen: 'itemSeen',
			read: 'itemRead'
		} );

		this.connect( this, {
			itemSeen: 'onItemSeen',
			itemRead: 'onItemRead'
		} );
	};

	/* Initialization */

	OO.initClass( mw.echo.dm.NotificationsModel );
	OO.mixinClass( mw.echo.dm.NotificationsModel, OO.EventEmitter );
	OO.mixinClass( mw.echo.dm.NotificationsModel, mw.echo.dm.List );

	/* Events */

	/**
	 * @event updateSeenTime
	 *
	 * Seen time has been updated
	 */

	/**
	 * @event unseenChange
	 * @param {number} count Number of unseen items
	 *
	 * Items' seen status has changed
	 */

	/**
	 * @event unreadChange
	 * @param {number} count Number of unread items
	 *
	 * Items' read status has changed
	 */

	/* Methods */

	/**
	 * Respond to item seen state change
	 *
	 * @param {mw.echo.dm.NotificationItem} item Notification item
	 * @param {boolean} isSeen Notification is seen
	 * @fires unseenChange
	 */
	mw.echo.dm.NotificationsModel.prototype.onItemSeen = function ( item, isSeen ) {
		var id = item && item.getId(),
			unseenItem = id && this.unseenNotifications.getItemById( id );

		if ( unseenItem ) {
			if ( isSeen ) {
				this.unseenNotifications.removeItems( [ unseenItem ] );
			} else {
				this.unseenNotifications.addItems( [ unseenItem ] );
			}
			this.emit( 'unseenChange', this.unseenNotifications.getItems() );
		}
	};

	/**
	 * Respond to item read state change
	 *
	 * @param {mw.echo.dm.NotificationItem} item Notification item
	 * @param {boolean} isRead Notification is read
	 * @fires unreadChange
	 */
	mw.echo.dm.NotificationsModel.prototype.onItemRead = function ( item, isRead ) {
		var id = item && item.getId(),
			unreadItem = id && this.unreadNotifications.getItemById( id );

		if ( unreadItem ) {
			if ( isRead ) {
				this.unreadNotifications.removeItems( [ unreadItem ] );
			} else {
				this.unreadNotifications.addItems( [ unreadItem ] );
			}
			this.emit( 'unreadChange', this.unreadNotifications.getItems() );
		}
	};

	/**
	 * Get the type of the notifications that this model deals with.
	 * Notifications type are given from the API: 'alert', 'message', 'all'
	 *
	 * @return {string} Notifications type
	 */
	mw.echo.dm.NotificationsModel.prototype.getType = function () {
		return this.type;
	};

	/**
	 * Get the counter of how many notifications are unseen
	 *
	 * @return {number} Number of unseen notifications
	 */
	mw.echo.dm.NotificationsModel.prototype.getUnseenCount = function () {
		return this.unseenNotifications.getItemCount();
	};

	/**
	 * Get the counter of how many notifications are unread
	 *
	 * @return {number} Number of unread notifications
	 */
	mw.echo.dm.NotificationsModel.prototype.getUnreadCount = function () {
		return this.unreadNotifications.getItemCount();
	};

	/**
	 * Set the system seen time - the last time we've marked notification as seen
	 *
	 * @private
	 * @param {string} Mediawiki seen timestamp in Mediawiki timestamp format
	 */
	mw.echo.dm.NotificationsModel.prototype.setSeenTime = function ( time ) {
		this.seenTime[this.type] = time;
	};

	/**
	 * Get the system seen time
	 *
	 * @return {string} Mediawiki seen timestamp in Mediawiki timestamp format
	 */
	mw.echo.dm.NotificationsModel.prototype.getSeenTime = function () {
		return this.seenTime[this.type];
	};

	/**
	 * Check whether the model is fetching notifications from the API
	 *
	 * @return {boolean} The model is in the process of fetching from the API
	 */
	mw.echo.dm.NotificationsModel.prototype.isFetchingNotifications = function () {
		return !!this.fetchNotificationsPromise;
	};

	/**
	 * Update the seen timestamp
	 *
	 * @return {jQuery.Promise} A promise that resolves with the seen timestamp
	 * @fires updateSeenTime
	 */
	mw.echo.dm.NotificationsModel.prototype.updateSeenTime = function () {
		var i, len,
			model = this,
			items = this.unseenNotifications.getItems();

		// Update the notifications seen status
		for ( i = 0, len = items.length; i < len; i++ ) {
			items[i].toggleSeen( true );
		}
		this.emit( 'updateSeenTime' );

		return this.api.postWithToken( 'edit', {
			action: 'echomarkseen',
			type: this.type
		} )
			.then( function ( data ) {
				var time = data.query.echomarkseen.timestamp;

				// Update seen time from the server
				model.setSeenTime( time );
			} );
	};

	/**
	 * Mark all notifications as read
	 *
	 * @return {jQuery.Promise} A promise that resolves when all notifications
	 * were marked as read.
	 */
	mw.echo.dm.NotificationsModel.prototype.markAllRead = function () {
		var model = this,
			data = {
				action: 'echomarkread',
				uselang: this.userLang,
				sections: this.type
			};

		if ( !this.unreadNotifications.getItemCount() ) {
			return $.Deferred().resolve( 0 ).promise();
		}

		return this.api.postWithToken( 'edit', data )
			.then( function ( result ) {
				return result.query.echomarkread[model.type].rawcount || 0;
			} )
			.then( function () {
				var i, len,
					items = model.unreadNotifications.getItems();

				for ( i = 0, len = items.length; i < len; i++ ) {
					items[i].toggleRead( true );
					items[i].toggleSeen( true );
				}
				model.unreadNotifications.clearItems();
			} );
	};

	/**
	 * Fetch notifications from the API and update the notifications list.
	 *
	 * @return {jQuery.Promise} A promise that resolves with an array of notification
	 *  id's.
	 */
	mw.echo.dm.NotificationsModel.prototype.fetchNotifications = function () {
		var model = this,
			apiData = {
				action: 'query',
				meta: 'notifications',
				notsections: this.type,
				notmessageunreadfirst: 1,
				notformat: 'flyout',
				notlimit: this.limit,
				notprop: 'index|list|count',
				uselang: this.userLang
			};

		if ( !this.fetchNotificationsPromise ) {
			this.fetchNotificationsPromise = this.api.get( apiData )
				.then( function ( result ) {
					var notifData, i, len, $content, wasSeen, wasRead, notificationModel,
						optionItems = [],
						idArray = [],
						data = result.query.notifications;

					for ( i = 0, len = data.index.length; i < len; i++ ) {
						notifData = data.list[ data.index[i] ];
						// TODO: This should really be formatted better, and the OptionWidget
						// should be the one that displays whatever icon relates to this notification
						// according to its type.
						$content = $( $.parseHTML( notifData['*'] ) );

						wasRead = !!notifData.read;
						wasSeen = notifData.timestamp.mw <= model.getSeenTime();
						notificationModel = new mw.echo.dm.NotificationItem(
							notifData.id,
							{
								read: wasRead,
								seen: wasSeen,
								timestamp: notifData.timestamp.mw,
								category: notifData.category,
								content: $content,
								// Hack: Get the primary link from the $content
								primaryUrl: $content.find( '.mw-echo-notification-primary-link' ).attr( 'href' )
							}
						);

						idArray.push( notifData.id );
						optionItems.push( notificationModel );
					}
					model.addItems( optionItems );

					return idArray;
				} )
				.then( function ( idArray ) {
					model.fetchNotificationsPromise = null;

					return idArray;
				} );
		}

		return this.fetchNotificationsPromise;
	};

	/**
	 * Update the unread and unseen tracking lists when we add items
	 *
	 * @param {mw.echo.dm.NotificationItem[]} items Items to add
	 * @param {number} index Index to add items at
	 */
	mw.echo.dm.NotificationsModel.prototype.addItems = function ( items, index ) {
		var i, len;

		for ( i = 0, len = items.length; i < len; i++ ) {
			if ( !items[i].isRead() ) {
				this.unreadNotifications.addItems( [ items[i] ] );
			}
			if ( !items[i].isSeen() ) {
				this.unseenNotifications.addItems( [ items[i] ] );
			}
		}

		// Parent
		mw.echo.dm.List.prototype.addItems.call( this, items, index );
	};

	/**
	 * Update the unread and unseen tracking lists when we remove items
	 *
	 * @param {mw.echo.dm.NotificationItem[]} items Items to remove
	 * @param {number} index Index to add items at
	 */
	mw.echo.dm.NotificationsModel.prototype.removeItems = function ( items ) {
		var i, len;

		for ( i = 0, len = items.length; i < len; i++ ) {
			this.unreadNotifications.removeItems( [ items[i] ] );
			this.unseenNotifications.removeItems( [ items[i] ] );
		}

		// Parent
		mw.echo.dm.List.prototype.removeItems.call( this, items );
	};

	/**
	 * Update the unread and unseen tracking lists when we clear items
	 */
	mw.echo.dm.NotificationsModel.prototype.clearItems = function () {
		this.unreadNotifications.clearItems();
		this.unseenNotifications.clearItems();

		// Parent
		mw.echo.dm.List.prototype.clearItems.call( this );
	};

	/**
	 * Query the API for unread count of the notifications in this model
	 *
	 * @return {jQuery.Promise} jQuery promise that's resolved when the unread count is fetched
	 *  and the badge label is updated.
	 */
	mw.echo.dm.NotificationsModel.prototype.fetchUnreadCountFromApi = function () {
		var apiData = {
				action: 'query',
				meta: 'notifications',
				notsections: this.getType(),
				notmessageunreadfirst: 1,
				notlimit: this.limit,
				notprop: 'index|count',
				uselang: this.userLang
			};

		return this.api.get( apiData )
			.then( function ( result ) {
				return OO.getProp( result.query, 'notifications', 'rawcount' ) || 0;
			} );
	};
} )( mediaWiki, jQuery );
<|endoftext|>


<commit_before>var App = App || {}; //sigh jslint

Ext.define('CustomApp', {
    extend: 'Rally.app.App',
    componentCls: 'app',

    _bucketByDay:{

    },

    _displayData:function() {
        console.log(this.results);
        Ext.each(this.results, function(question) {
            var date = Ext.Date.format((Ext.Date.parse(question.creation_date, "U")),"c").split('T')[0];
            console.log(date);
            if (this._bucketByDay[date]) {
                this._bucketByDay[date]++;
            }
            else {
                this._bucketByDay[date] = 1;
            }
        }, this);
        var sorted = Ext.Object.getKeys(this._bucketByDay).sort();
        Ext.each(sorted,function(key){
            this.add({
                html:key+" "+this._bucketByDay[key]
            });
        },this);

    },
    launch: function() {
        var me = this;
        App.StackOverflowDataGrabber.getData(function(results) {
            me.results = results;
            me._displayData();
        });
    }
});
<commit_msg>bucketz<commit_after>var App = App || {}; //sigh jslint

Ext.define('CustomApp', {
    extend: 'Rally.app.App',
    componentCls: 'app',

    _bucketByDay:{

    },
    _bucketByMonth:{

    },

    _displayData:function() {
        console.log(this.results);
        Ext.each(this.results, function(question) {
            var date = Ext.Date.format((Ext.Date.parse(question.creation_date, "U")),"c").split('T')[0];
            if (this._bucketByDay[date]) {
                this._bucketByDay[date]++;
            }
            else {
                this._bucketByDay[date] = 1;
            }

            var monthString = date.substring(0,7);
            console.log(monthString);
            if (this._bucketByMonth[monthString]) {
                this._bucketByMonth[monthString]++;
            }
            else {
                this._bucketByMonth[monthString] = 1;
            }

        }, this);
        var sorted = Ext.Object.getKeys(this._bucketByMonth).sort();
        Ext.each(sorted,function(key){
            this.add({
                html:key+" "+this._bucketByMonth[key]
            });
        },this);

    },
    launch: function() {
        var me = this;
        App.StackOverflowDataGrabber.getData(function(results) {
            me.results = results;
            me._displayData();
        });
    }
});
<|endoftext|>


<commit_before>const { environment } = require('@rails/webpacker')
const ConcatPlugin = require('webpack-concat-plugin')

environment.plugins.append('ConcatPlugin', new ConcatPlugin({
  uglify: true,
  sourceMap: true,
  outputPath: 'js',
  fileName: 'application.bundle.js',
  filesToConcat: [
    'jquery',
    'jquery-migrate',
    'jquery-ujs',
    'jquery.iframe-transport',
    'dropzone',
    'govuk_frontend_toolkit/javascripts/govuk/stick-at-top-when-scrolling.js',
    'govuk_frontend_toolkit/javascripts/govuk/stop-scrolling-at-footer.js',
    'jquery-accessible-accordion-aria',
    'jsrender',
    'jquery-highlight',
    'jquery-throttle-debounce',
    'accessible-autocomplete',
    './app/webpack/javascripts/vendor/**/*',
    'datatables.net/js/jquery.dataTables.min.js',
    'datatables.net-buttons/js/dataTables.buttons.min.js',
    'datatables.net-fixedheader/js/dataTables.fixedHeader.min.js',
    'datatables.net-select/js/dataTables.select.min.js',
    'jquery-datatables-checkboxes/js/dataTables.checkboxes.min.js',
    './app/webpack/javascripts/modules/**/*',
    './app/webpack/javascripts/plugins/**/*',
    './app/webpack/javascripts/application.js'
  ]
}))

environment.config.merge({
  output: {
    filename: 'js/[name]-[hash].js'
  }
})

module.exports = environment
<commit_msg>Muting jquery Migrate console warnings on Production<commit_after>const { environment } = require('@rails/webpacker')
const ConcatPlugin = require('webpack-concat-plugin')

environment.plugins.append('ConcatPlugin', new ConcatPlugin({
  uglify: true,
  sourceMap: true,
  outputPath: 'js',
  fileName: 'application.bundle.js',
  filesToConcat: [
    'jquery',
    process.env.RAILS_ENV === 'development' ? 'jquery-migrate' : 'jquery-migrate/dist/jquery-migrate.min.js',
    'jquery-ujs',
    'jquery.iframe-transport',
    'dropzone',
    'govuk_frontend_toolkit/javascripts/govuk/stick-at-top-when-scrolling.js',
    'govuk_frontend_toolkit/javascripts/govuk/stop-scrolling-at-footer.js',
    'jquery-accessible-accordion-aria',
    'jsrender',
    'jquery-highlight',
    'jquery-throttle-debounce',
    'accessible-autocomplete',
    './app/webpack/javascripts/vendor/**/*',
    'datatables.net/js/jquery.dataTables.min.js',
    'datatables.net-buttons/js/dataTables.buttons.min.js',
    'datatables.net-fixedheader/js/dataTables.fixedHeader.min.js',
    'datatables.net-select/js/dataTables.select.min.js',
    'jquery-datatables-checkboxes/js/dataTables.checkboxes.min.js',
    './app/webpack/javascripts/modules/**/*',
    './app/webpack/javascripts/plugins/**/*',
    './app/webpack/javascripts/application.js'
  ]
}))

environment.config.merge({
  output: {
    filename: 'js/[name]-[hash].js'
  }
})

module.exports = environment
<|endoftext|>


<commit_before>function CommentsModule() {

    if (window.username) {
        $('body').on('submit', 'form.comment_add', this.addComment);
        $('body').on('submit', 'form.comment_add_reply', this.addReply);
    }

}

CommentsModule.prototype.addComment = function(e) {
    var form = this;

    $(form).find('.form-group').removeClass('has-error');
    $(form).find('.help-block').remove();

    var url = laroute.action('CommentController@addComment', { content: data.content.hashid });

    $.post(url, $(form).serialize(), function(data){
        if (data.status == 'ok') {
            $(form).trigger('reset');

            $('.comments').append(data.comment);
            $('.comments p.no_comments').remove();
        } else {
            $(form).find('.form-group').last().addClass('has-error')
                .append('
'+ data.error +'
');
        }
    });

    e.preventDefault();
};

CommentsModule.prototype.addReply = function(e) {
    var form = this;
    var parent = $(form).parent('.comment').prevAll('.comment:not(.comment_reply)').first();

    $(form).find('.form-group').removeClass('has-error');
    $(form).find('.help-block').remove();

    $.post($(form).attr('action'), $(form).serialize(), function(data){
        if (data.status == 'ok') {
            $(form).parent().remove();

            $(parent).nextUntil('.comment:not(.comment_reply)').remove();
            $(parent).after(data.replies);

            $('.md a[href*="youtube.com"]').each(function()
            {
                var url = $(this).attr('href');
                var regex = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:[^#]+)?(?:#)?(?:t=(\d+))?(?:\S+)?$/i;

                var found = url.match(regex);

                if (found)
                    $(this).addClass('yt-video').data('yt-id', found[1]).data('yt-time', found[2]);
            });
        } else {
            $(form).find('.form-group').last().addClass('has-error')
                .append('
'+ data.error +'
');
        }
    });

    e.preventDefault();
};
<commit_msg>Fix comments<commit_after>function CommentsModule() {

    if (window.username) {
        $('body').on('submit', 'form.comment_add', this.addComment);
        $('body').on('submit', 'form.comment_add_reply', this.addReply);
    }

}

CommentsModule.prototype.addComment = function(e) {
    var form = this;
    var id = $(form).find('input[name=id]').val();

    $(form).find('.form-group').removeClass('has-error');
    $(form).find('.help-block').remove();

    var url = laroute.action('CommentController@addComment', { content: id });

    $.post(url, $(form).serialize(), function(data){
        if (data.status == 'ok') {
            $(form).trigger('reset');

            $('.comments').append(data.comment);
            $('.comments p.no_comments').remove();
        } else {
            $(form).find('.form-group').last().addClass('has-error')
                .append('
'+ data.error +'
');
        }
    });

    e.preventDefault();
};

CommentsModule.prototype.addReply = function(e) {
    var form = this;
    var parent = $(form).parent('.comment').prevAll('.comment:not(.comment_reply)').first();

    $(form).find('.form-group').removeClass('has-error');
    $(form).find('.help-block').remove();

    $.post($(form).attr('action'), $(form).serialize(), function(data){
        if (data.status == 'ok') {
            $(form).parent().remove();

            $(parent).nextUntil('.comment:not(.comment_reply)').remove();
            $(parent).after(data.replies);

            $('.md a[href*="youtube.com"]').each(function()
            {
                var url = $(this).attr('href');
                var regex = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:[^#]+)?(?:#)?(?:t=(\d+))?(?:\S+)?$/i;

                var found = url.match(regex);

                if (found)
                    $(this).addClass('yt-video').data('yt-id', found[1]).data('yt-time', found[2]);
            });
        } else {
            $(form).find('.form-group').last().addClass('has-error')
                .append('
'+ data.error +'
');
        }
    });

    e.preventDefault();
};
<|endoftext|>


<commit_before>/**
 * Created by Mihail on 9/18/2016.
 */

'use strict';

import MarketCards from '../../../src/market-place/market-cards';
import { expect } from 'chai';

describe('Market Cards module', () => {
	test('Should have container - html element', () => {
		const container = document.createElement('div');
		const marketCards = new MarketCards(container);
		expect(marketCards.container).not.to.be.undefined;
		expect(marketCards.container.tagName).to.be.equal('DIV');
	});

	test('Should define the count of the purchased cards', () => {
		const arrRadioButtons = [
			{
				type: 'radio',
				checked: true,
				value: 4
			},
			{
				type: 'radio',
				checked: false,
				value: 5
			}
		];

		const countCards = MarketCards.getPurchasedCardsCount(arrRadioButtons);
		expect(countCards).to.be.equal(4);
	});

	test(
        'Should set the correct price depending on how many cards are offered',
        () => {
            let cards = document.createElement('div');
            cards.innerHTML = '1 <input type="radio" id="one" name="marketCards" value="1" checked="checked">' +
                '' +
                '

';

            MarketCards.setCardPrices(5, [cards]);
            expect(cards.querySelector('.price').innerHTML).to.be.equal('5');
        }
    );
});
<commit_msg>:heavy_check_mark: Migrating front end unit tests from Mocha to Jest - updated market-cards.test.js<commit_after>import MarketCards from '../../../src/market-place/market-cards';

describe('Market Cards module', () => {
	test('Should have container - html element', () => {
		const container = document.createElement('div');
		const marketCards = new MarketCards(container);
		expect(marketCards.container).toBeDefined();
		expect(marketCards.container.tagName).toEqual('DIV');
	});

	test('Should define the count of the purchased cards', () => {
		const arrRadioButtons = [
			{
				type: 'radio',
				checked: true,
				value: 4
			},
			{
				type: 'radio',
				checked: false,
				value: 5
			}
		];

		const countCards = MarketCards.getPurchasedCardsCount(arrRadioButtons);
		expect(countCards).toEqual(4);
	});

	test(
        'Should set the correct price depending on how many cards are offered',
        () => {
            let cards = document.createElement('div');
            cards.innerHTML = '1 <input type="radio" id="one" name="marketCards" value="1" checked="checked">' +
                '' +
                '

';

            MarketCards.setCardPrices(5, [cards]);
            expect(cards.querySelector('.price').innerHTML).toEqual('5');
        }
    );
});
<|endoftext|>


<commit_before>import Vector from '../vector';
import Label from './label';
import OBB from '../utils/obb';
import Geo from '../geo';

const PLACEMENT = {
    MID_POINT: 0,
    CORNER: 1
};

export default class LabelLine extends Label {

    constructor (size, lines, options) {
        super(size, options);

        this.lines = lines;
        this.offset = this.options.offset;

        this.segment_size = options.segment_size;
        this.segment_texture_size = options.segment_texture_size;

        this.placement = (options.placement === undefined) ? PLACEMENT.MID_POINT : options.placement;

        this.position = null;
        this.pre_offset = [[0,0], [0,0]];
        this.collapsed_size = [];
        this.kink_index = 0;

        // optionally limit the line segments that the label may be placed in, by specifying a segment index range
        // used as a coarse subdivide for placing multiple labels per line geometry
        this.segment_index = options.segment_index || 0;
        this.segment_max = options.segment_end || this.lines.length;

        this.throw_away = false;

        // get first good segment
        var segment = this.getNextFittingSegment(this.getCurrentSegment());

        if (!segment) this.throw_away = true;
    }

    nextLabelLine() {
        // increment segment
        var hasNext = this.getNextSegment();
        if (!hasNext) return false;

        // clone options
        var options = Object.create(this.options);
        options.segment_index = this.segment_index;
        options.placement = this.placement;

        // create new label
        var label = new LabelLine(this.size, this.lines, options);

        return (label.throw_away) ? false : label;
    }

    getNextSegment() {
        switch (this.placement) {
            case PLACEMENT.CORNER:
                this.placement = PLACEMENT.MID_POINT;
                break;
            case PLACEMENT.MID_POINT:
                if (this.segment_index >= this.lines.length - 2) return false;
                if (this.segment_size.length > 1) {
                    this.placement = PLACEMENT.CORNER;
                    this.kink_index = 0;
                }
                this.segment_index++;
                break;
        }

        return this.getCurrentSegment();
    }

    getCurrentSegment() {
        let p1, p2, segment;
        switch (this.placement) {
            case PLACEMENT.CORNER:
                p1 = this.lines[this.segment_index - 1];
                p2 = this.lines[this.segment_index];
                let p3 = this.lines[this.segment_index + 1];
                segment = [ p1, p2, p3 ];
                break;
            case PLACEMENT.MID_POINT:
                p1 = this.lines[this.segment_index];
                p2 = this.lines[this.segment_index + 1];
                segment = [ p1, p2 ];
                break;
        }

        return segment;
    }

    getNextFittingSegment(segment) {
        segment = segment || this.getNextSegment();
        if (!segment) return false;

        if (this.doesSegmentFit(segment)) {
            this.update();
            if (this.inTileBounds())
                return segment;
        }

        return this.getNextFittingSegment();
    }

    doesSegmentFit(segment) {
        let does_fit = false;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                does_fit = this.fitKinkedSegment(segment);
                break;
            case PLACEMENT.MID_POINT:
                let excess = 1 + this.options.line_exceed / 100;
                let p0p1 = Vector.sub(segment[0], segment[1]);
                let line_length = Vector.length(p0p1);

                let label_length = this.size[0] * this.options.units_per_pixel;
                does_fit = (label_length < excess * line_length)
                break;
        }

        return does_fit;
    }

    fitKinkedSegment(segment) {
        let excess = 1 + this.options.line_exceed / 100;
        let opp = this.options.units_per_pixel;

        let does_fit = false;

        let p0p1 = Vector.sub(segment[0], segment[1]);
        let p1p2 = Vector.sub(segment[1], segment[2]);

        let line_length1 = Vector.length(p0p1);
        let line_length2 = Vector.length(p1p2);

        // break up multiple segments into two chunks (N-1 options)
        let label_length1 = this.size[0];
        let label_length2 = 0;
        let width;

        this.kink_index = this.segment_size.length - 1;

        while (!does_fit && this.kink_index > 0) {
            width = this.segment_size[this.kink_index];

            label_length1 -= width;
            label_length2 += width;

            does_fit = (opp * label_length1 < excess * line_length1 && opp * label_length2 < excess * line_length2);
            if (!does_fit) this.kink_index--;
        }

        if (does_fit && this.kink_index > 0) {
            var width1 = 0;
            var width2 = this.size[0] + 16;
            for (var i = 0; i < this.kink_index; i++) {
                var segment_width = this.segment_size[i];
                width1 += segment_width;
                width2 -= segment_width;
            }
            this.collapsed_size[0] = width1;
            this.collapsed_size[1] = width2;

            this.pre_offset[0][0] = -0.5 * this.collapsed_size[0];
            this.pre_offset[1][0] = 0.5 * this.collapsed_size[1];

            return true;
        }
        else return false;
    }

    update() {
        this.angle = this.getCurrentAngle();
        this.position = this.getCurrentPosition();
        this.updateBBoxes();
    }

    getCurrentAngle() {
        var segment = this.getCurrentSegment();
        var angle;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                var theta1 = getAngleFromSegment(segment[0], segment[1]);
                var theta2 = getAngleFromSegment(segment[1], segment[2]);

                var orientation1 = getOrientationFromSegment(segment[0], segment[1]);
                var orientation2 = getOrientationFromSegment(segment[1], segment[2]);

                if (orientation1 !== orientation2) {
                    theta2 -= Math.PI;
                }

                angle = (orientation1) ? [theta2, theta1] : [theta1, theta2];
                break;
            case PLACEMENT.MID_POINT:
                var theta = getAngleFromSegment(segment[0], segment[1]);
                angle = [theta];
                break;
        }

        return angle;
    }

    getCurrentPosition() {
        let segment = this.getCurrentSegment();
        let position;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                position = segment[1].slice();
                break;
            case PLACEMENT.MID_POINT:
                position = [
                    (segment[0][0] + segment[1][0]) / 2,
                    (segment[0][1] + segment[1][1]) / 2
                ];
                break;
        }

        return position;
    }

    updateBBoxes() {
        let upp = this.options.units_per_pixel;
        let height = (this.size[1] + this.options.buffer[1] * 2) * upp * Label.epsilon;

        this.obbs = [];
        this.aabbs = [];

        switch (this.placement) {
            case PLACEMENT.CORNER:
                for (var i = 0; i < this.collapsed_size.length; i++){
                    var size = this.collapsed_size[i];
                    var angle = this.angle[i];

                    let width = (size) * upp * Label.epsilon;

                    var direction = (i == 0) ? -1 : 1;
                    var offset = Vector.rot([direction * 0.5 * width, 0], -angle);
                    var position = Vector.add(this.position, offset);

                    var obb = getOBB(position, width, height, angle, this.offset, upp);
                    var aabb = obb.getExtent();

                    this.obbs.push(obb);
                    this.aabbs.push(aabb);
                }
                break;
            case PLACEMENT.MID_POINT:
                let width = (this.size[0] + this.options.buffer[0] * 2) * upp * Label.epsilon;

                var angle = this.angle[0];
                var obb = getOBB(this.position, width, height, angle, this.offset, upp);
                var aabb = obb.getExtent();

                this.obbs.push(obb);
                this.aabbs.push(aabb);
                break;
        }
    }

    inTileBounds() {
        for (var i = 0; i < this.aabbs.length; i++) {
            var aabb = this.aabbs[i];
            var obj = { aabb };
            var in_bounds = super.inTileBounds.call(obj);
            if (!in_bounds) return false;
        }
        return true;
    }

    add(bboxes) {
        for (var i = 0; i < this.aabbs.length; i++){
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };
            super.add.call(obj, bboxes);
        }
    }

    discard(bboxes) {
        for (var i = 0; i < this.obbs.length; i++){
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };
            var shouldDiscard = super.occluded.call(obj, bboxes);
            if (shouldDiscard) return true;
        }
        return false;
    }
}

function getOBB(position, width, height, angle, offset, upp) {
    // apply offset, x positive, y pointing down
    offset = Vector.rot(offset, angle);
    let p0 = position[0] + (offset[0] * upp);
    let p1 = position[1] - (offset[1] * upp);

    // the angle of the obb is negative since it's the tile system y axis is pointing down
    return new OBB(p0, p1, -angle, width, height);
}

function getAngleFromSegment(pt1, pt2) {
    let PI = Math.PI;
    let PI_2 = PI / 2;
    let p1p2 = Vector.sub(pt1, pt2);
    let theta = Math.atan2(p1p2[0], p1p2[1]) + PI_2;

    if (theta >= PI_2) {
        // If in 2nd quadrant, move to 4th quadrant
        theta += PI;
    }
    else if (theta < 0) {
        // If in 4th quadrant, make a positive angle
        theta += 2 * PI
    }

    return theta;
}

function getOrientationFromSegment(pt1, pt2) {
    return pt1[0] >= pt2[0];
}

function nudge(){
    var height = this.size[1];
    var nudge0 = height / 2 * Math.cos(Math.PI/2 - this.angle[0]);
    var nudge1 = height / 2 * Math.cos(Math.PI/2 - this.angle[1]);

    this.pre_offset[0][0] += nudge0*this.options.units_per_pixel;
    this.pre_offset[1][0] -= nudge1*this.options.units_per_pixel;
}<commit_msg>Nudge labels based on angle<commit_after>import Vector from '../vector';
import Label from './label';
import OBB from '../utils/obb';
import Geo from '../geo';

const PLACEMENT = {
    MID_POINT: 0,
    CORNER: 1
};

export default class LabelLine extends Label {

    constructor (size, lines, options) {
        super(size, options);

        this.lines = lines;
        this.offset = this.options.offset;

        this.segment_size = options.segment_size;
        this.segment_texture_size = options.segment_texture_size;

        this.placement = (options.placement === undefined) ? PLACEMENT.MID_POINT : options.placement;

        this.position = null;
        this.pre_offset = [[0,0], [0,0]];
        this.collapsed_size = [];
        this.kink_index = 0;

        // optionally limit the line segments that the label may be placed in, by specifying a segment index range
        // used as a coarse subdivide for placing multiple labels per line geometry
        this.segment_index = options.segment_index || 0;
        this.segment_max = options.segment_end || this.lines.length;

        this.throw_away = false;

        // get first good segment
        var segment = this.getNextFittingSegment(this.getCurrentSegment());

        if (!segment) this.throw_away = true;
    }

    nextLabelLine() {
        // increment segment
        var hasNext = this.getNextSegment();
        if (!hasNext) return false;

        // clone options
        var options = Object.create(this.options);
        options.segment_index = this.segment_index;
        options.placement = this.placement;

        // create new label
        var label = new LabelLine(this.size, this.lines, options);

        return (label.throw_away) ? false : label;
    }

    getNextSegment() {
        switch (this.placement) {
            case PLACEMENT.CORNER:
                this.placement = PLACEMENT.MID_POINT;
                break;
            case PLACEMENT.MID_POINT:
                if (this.segment_index >= this.lines.length - 2) return false;
                if (this.segment_size.length > 1) {
                    this.placement = PLACEMENT.CORNER;
                    this.kink_index = 0;
                }
                this.segment_index++;
                break;
        }

        return this.getCurrentSegment();
    }

    getCurrentSegment() {
        let p1, p2, segment;
        switch (this.placement) {
            case PLACEMENT.CORNER:
                p1 = this.lines[this.segment_index - 1];
                p2 = this.lines[this.segment_index];
                let p3 = this.lines[this.segment_index + 1];
                segment = [ p1, p2, p3 ];
                break;
            case PLACEMENT.MID_POINT:
                p1 = this.lines[this.segment_index];
                p2 = this.lines[this.segment_index + 1];
                segment = [ p1, p2 ];
                break;
        }

        return segment;
    }

    getNextFittingSegment(segment) {
        segment = segment || this.getNextSegment();
        if (!segment) return false;

        if (this.doesSegmentFit(segment)) {
            this.update();
            if (this.inTileBounds())
                return segment;
        }

        return this.getNextFittingSegment();
    }

    doesSegmentFit(segment) {
        let does_fit = false;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                does_fit = this.fitKinkedSegment(segment);
                break;
            case PLACEMENT.MID_POINT:
                let excess = 1 + this.options.line_exceed / 100;
                let p0p1 = Vector.sub(segment[0], segment[1]);
                let line_length = Vector.length(p0p1);

                let label_length = this.size[0] * this.options.units_per_pixel;
                does_fit = (label_length < excess * line_length)
                break;
        }

        return does_fit;
    }

    fitKinkedSegment(segment) {
        let excess = 1 + this.options.line_exceed / 100;
        let opp = this.options.units_per_pixel;

        let does_fit = false;

        let p0p1 = Vector.sub(segment[0], segment[1]);
        let p1p2 = Vector.sub(segment[1], segment[2]);

        let line_length1 = Vector.length(p0p1);
        let line_length2 = Vector.length(p1p2);

        // break up multiple segments into two chunks (N-1 options)
        let label_length1 = this.size[0];
        let label_length2 = 0;
        let width;

        this.kink_index = this.segment_size.length - 1;

        while (!does_fit && this.kink_index > 0) {
            width = this.segment_size[this.kink_index];

            label_length1 -= width;
            label_length2 += width;

            does_fit = (opp * label_length1 < excess * line_length1 && opp * label_length2 < excess * line_length2);
            if (!does_fit) this.kink_index--;
        }

        if (does_fit && this.kink_index > 0) {
            var width1 = 0;
            var width2 = this.size[0] + 16;
            for (var i = 0; i < this.kink_index; i++) {
                var segment_width = this.segment_size[i];
                width1 += segment_width;
                width2 -= segment_width;
            }
            this.collapsed_size[0] = width1;
            this.collapsed_size[1] = width2;

            this.pre_offset[0][0] = -0.5 * this.collapsed_size[0];
            this.pre_offset[1][0] = 0.5 * this.collapsed_size[1];

            return true;
        }
        else return false;
    }

    update() {
        this.angle = this.getCurrentAngle();
        this.position = this.getCurrentPosition();
        this.updateBBoxes();
    }

    getCurrentAngle() {
        var segment = this.getCurrentSegment();
        var angle;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                var theta1 = getAngleFromSegment(segment[0], segment[1]);
                var theta2 = getAngleFromSegment(segment[1], segment[2]);

                var orientation1 = getOrientationFromSegment(segment[0], segment[1]);
                var orientation2 = getOrientationFromSegment(segment[1], segment[2]);

                if (orientation1 !== orientation2) {
                    theta2 -= Math.PI;
                }

                angle = (orientation1) ? [theta2, theta1] : [theta1, theta2];
                break;
            case PLACEMENT.MID_POINT:
                var theta = getAngleFromSegment(segment[0], segment[1]);
                angle = [theta];
                break;
        }

        return angle;
    }

    getCurrentPosition() {
        let segment = this.getCurrentSegment();
        let position;

        switch (this.placement) {
            case PLACEMENT.CORNER:
                position = segment[1].slice();
                break;
            case PLACEMENT.MID_POINT:
                position = [
                    (segment[0][0] + segment[1][0]) / 2,
                    (segment[0][1] + segment[1][1]) / 2
                ];
                break;
        }

        return position;
    }

    updateBBoxes() {
        let upp = this.options.units_per_pixel;
        let height = (this.size[1] + this.options.buffer[1] * 2) * upp * Label.epsilon;

        this.obbs = [];
        this.aabbs = [];

        switch (this.placement) {
            case PLACEMENT.CORNER:
                for (var i = 0; i < this.collapsed_size.length; i++){
                    var size = this.collapsed_size[i];
                    var angle = this.angle[i];

                    let width = (size) * upp * Label.epsilon;

                    var direction = (i == 0) ? -1 : 1;
                    var offset = Vector.rot([direction * 0.5 * width, 0], -angle);
                    var position = Vector.add(this.position, offset);

                    var obb = getOBB(position, width, height, angle, this.offset, upp);
                    var aabb = obb.getExtent();

                    this.obbs.push(obb);
                    this.aabbs.push(aabb);
                }
                break;
            case PLACEMENT.MID_POINT:
                let width = (this.size[0] + this.options.buffer[0] * 2) * upp * Label.epsilon;

                var angle = this.angle[0];
                var obb = getOBB(this.position, width, height, angle, this.offset, upp);
                var aabb = obb.getExtent();

                this.obbs.push(obb);
                this.aabbs.push(aabb);
                break;
        }
    }

    inTileBounds() {
        for (var i = 0; i < this.aabbs.length; i++) {
            var aabb = this.aabbs[i];
            var obj = { aabb };
            var in_bounds = super.inTileBounds.call(obj);
            if (!in_bounds) return false;
        }
        return true;
    }

    add(bboxes) {
        for (var i = 0; i < this.aabbs.length; i++){
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };
            super.add.call(obj, bboxes);
        }
    }

    discard(bboxes) {
        for (var i = 0; i < this.obbs.length; i++){
            var aabb = this.aabbs[i];
            var obb = this.obbs[i];
            var obj = { aabb, obb };
            var shouldDiscard = super.occluded.call(obj, bboxes);
            if (shouldDiscard) return true;
        }
        return false;
    }
}

function getOBB(position, width, height, angle, offset, upp) {
    // apply offset, x positive, y pointing down
    offset = Vector.rot(offset, angle);
    let p0 = position[0] + (offset[0] * upp);
    let p1 = position[1] - (offset[1] * upp);

    // the angle of the obb is negative since it's the tile system y axis is pointing down
    return new OBB(p0, p1, -angle, width, height);
}

function getAngleFromSegment(pt1, pt2) {
    let PI = Math.PI;
    let PI_2 = PI / 2;
    let p1p2 = Vector.sub(pt1, pt2);
    let theta = Math.atan2(p1p2[0], p1p2[1]) + PI_2;

    if (theta >= PI_2) {
        // If in 2nd quadrant, move to 4th quadrant
        theta += PI;
    }
    else if (theta < 0) {
        // If in 4th quadrant, make a positive angle
        theta += 2 * PI
    }

    return theta;
}

function getOrientationFromSegment(pt1, pt2) {
    return pt1[0] >= pt2[0];
}

function nudge() {
    if (this.angle[1] === undefined) return;

    var height = this.size[1] / 2;
    var angle_delta = Math.abs(this.angle[1] - this.angle[0]) % 2*Math.PI;
    var dx = height * Math.sin(0.5 * angle_delta);

    if (this.pre_offset[0][0] < 0)
        this.pre_offset[0][0] -= dx;
    else
        this.pre_offset[0][0] += dx;

    if (this.pre_offset[1][0] < 0)
        this.pre_offset[1][0] -= dx;
    else
        this.pre_offset[1][0] += dx;
}<|endoftext|>


<commit_before>import React from 'react';
import {
  StyleSheet,
  Text,
  View,
  ListView,
  Dimensions,
  ActivityIndicator,
} from 'react-native';

const api = 'https://f1portal.herokuapp.com';

export default class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      driverJson: [],
      isLoading: true,
    };
  }

  componentDidMount() {

    return fetch(api + '/get_drivers')
      .then((response) => response.json())
      .then((responseJson) => {
        console.log(responseJson),
        this.setState({
          isLoading: false,
          driverJson: responseJson,
          dataSource: this.state.dataSource.cloneWithRows(
            responseJson.MRData.StandingsTable
            .StandingsLists[0].DriverStandings),
        });
      })
      .catch((error) => {
        console.error(error);
      });

  }

  renderRow(standingCell, something, rowID) {
      return (
        <View style={styles.listElem}>

          <View style={styles.driverOrder}>
            <Text style={{color: 'grey'}}>
              {parseInt(rowID, 10) + 1}
            </Text>
          </View>

          <View style={styles.driverNameBox}>

            <Text>{standingCell.Driver.givenName + " "
              + standingCell.Driver.familyName}
            </Text>

            <Text style={{color: 'grey'}}>
              {standingCell.Constructors[0].name}</Text>

          </View>

          <View style={styles.driverPointsBox}>
            <Text style={{color: 'red'}}>
              {standingCell.points}</Text>
          </View>

        </View>
      )
  }

  render() {

    if (this.state.isLoading) {
      return (
        <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
          <ActivityIndicator />
        </View>
      );
    }

    return (
      <View style={styles.container}>

        <View style={styles.header}>
          <Text style={styles.headerText}>
            F1 Portal
          </Text>
        </View>

        <View style={styles.listHeader}>
          <Text style={styles.listHeaderText}>{
            this.state.driverJson.MRData.StandingsTable.season + " Drivers Championship"}</Text>
        </View>

        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'}}>
          <ListView
            flex-start dataSource={this.state.dataSource}
            renderRow={this.renderRow.bind(this)}
            enableEmptySections={true}
          />
        </View>

      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  header:{
    backgroundColor: '#F44336',
    paddingTop: 20,
    paddingBottom: 10,
    flexDirection: 'row'
  },
  headerText:{
    color: '#fff',
    textAlign: 'center',
    fontWeight: 'normal',
    flex: 1,
    fontSize: 20,
  },
  listHeader:{
    backgroundColor: '#F5F5F5',
    paddingTop: 20,
    paddingBottom: 10,
    flexDirection: 'row',
    alignContent: 'center',
    borderBottomWidth: 1.5,
    borderBottomColor: '#EEEEEE',
  },
  listHeaderText:{
    color: '#F44336',
    textAlign: 'center',
    fontWeight: 'normal',
    flex: 1,
    fontSize: 20,
  },
  listElem:{
    width: Dimensions.get('window').width,
    paddingTop: 20,
    paddingBottom: 20,
    paddingLeft: 30,
    paddingRight: 30,
    flexDirection: 'row',
    borderBottomWidth: 1,
    borderBottomColor: '#F5F5F5',
    alignItems: 'flex-start'
  },
  driverPointsBox:{
    flex: 1,
    alignItems: 'flex-end'
  },
  driverNameBox:{
    flex: 1,
    alignItems: 'flex-start',
    flexDirection: 'column'
  },
  driverOrder:{
    alignItems: 'flex-start',
    minWidth: 25,
  }
});

<commit_msg>Removing the printing of drivers json from the console log<commit_after>import React from 'react';
import {
  StyleSheet,
  Text,
  View,
  ListView,
  Dimensions,
  ActivityIndicator,
} from 'react-native';

const api = 'https://f1portal.herokuapp.com';

export default class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      driverJson: [],
      isLoading: true,
    };
  }

  componentDidMount() {

    return fetch(api + '/get_drivers')
      .then((response) => response.json())
      .then((responseJson) => {
        //console.log(responseJson),
        this.setState({
          isLoading: false,
          driverJson: responseJson,
          dataSource: this.state.dataSource.cloneWithRows(
            responseJson.MRData.StandingsTable
            .StandingsLists[0].DriverStandings),
        });
      })
      .catch((error) => {
        console.error(error);
      });

  }

  renderRow(standingCell, something, rowID) {
      return (
        <View style={styles.listElem}>

          <View style={styles.driverOrder}>
            <Text style={{color: 'grey'}}>
              {parseInt(rowID, 10) + 1}
            </Text>
          </View>

          <View style={styles.driverNameBox}>

            <Text>{standingCell.Driver.givenName + " "
              + standingCell.Driver.familyName}
            </Text>

            <Text style={{color: 'grey'}}>
              {standingCell.Constructors[0].name}</Text>

          </View>

          <View style={styles.driverPointsBox}>
            <Text style={{color: 'red'}}>
              {standingCell.points}</Text>
          </View>

        </View>
      )
  }

  render() {

    if (this.state.isLoading) {
      return (
        <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
          <ActivityIndicator />
        </View>
      );
    }

    return (
      <View style={styles.container}>

        <View style={styles.header}>
          <Text style={styles.headerText}>
            F1 Portal
          </Text>
        </View>

        <View style={styles.listHeader}>
          <Text style={styles.listHeaderText}>{
            this.state.driverJson.MRData.StandingsTable.season + " Drivers Championship"}</Text>
        </View>

        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'}}>
          <ListView
            flex-start dataSource={this.state.dataSource}
            renderRow={this.renderRow.bind(this)}
            enableEmptySections={true}
          />
        </View>

      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  header:{
    backgroundColor: '#F44336',
    paddingTop: 20,
    paddingBottom: 10,
    flexDirection: 'row'
  },
  headerText:{
    color: '#fff',
    textAlign: 'center',
    fontWeight: 'normal',
    flex: 1,
    fontSize: 20,
  },
  listHeader:{
    backgroundColor: '#F5F5F5',
    paddingTop: 20,
    paddingBottom: 10,
    flexDirection: 'row',
    alignContent: 'center',
    borderBottomWidth: 1.5,
    borderBottomColor: '#EEEEEE',
  },
  listHeaderText:{
    color: '#F44336',
    textAlign: 'center',
    fontWeight: 'normal',
    flex: 1,
    fontSize: 20,
  },
  listElem:{
    width: Dimensions.get('window').width,
    paddingTop: 20,
    paddingBottom: 20,
    paddingLeft: 30,
    paddingRight: 30,
    flexDirection: 'row',
    borderBottomWidth: 1,
    borderBottomColor: '#F5F5F5',
    alignItems: 'flex-start'
  },
  driverPointsBox:{
    flex: 1,
    alignItems: 'flex-end'
  },
  driverNameBox:{
    flex: 1,
    alignItems: 'flex-start',
    flexDirection: 'column'
  },
  driverOrder:{
    alignItems: 'flex-start',
    minWidth: 25,
  }
});

<|endoftext|>