User:Ricordisamoa/Echo.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/* <nowiki>
 * Echo.js
 * @author [[User:Ricordisamoa]]
 * -------------------experimental--------------------
 * mimics the [[mw:Extension:Echo]] (notifications) UI
 * -  only working with new messages at the moment   -
*/
/* global mw, $ */
$( function () {
	'use strict';

	// TEST: $( '#pt-notifications-message' ).remove();
	if ( $( '#pt-notifications-message' ).length > 0 ) {
		return; // the Echo extension is installed
	}
	var namespaces = mw.config.get( 'wgFormattedNamespaces' ),
	nsSpecial = namespaces[ '-1' ],
	userName = mw.config.get( 'wgUserName' ),
	userTalk = namespaces[ '3' ] + ':' + userName,
	newMsg = mw.config.get( 'wgUserNewMsgRevisionId' ),
	$messagesBadge = $( '<a>' )
	.attr( {
		href: mw.util.getUrl( nsSpecial + ':Notifications' ),
		'class': 'mw-echo-notifications-badge oo-ui-icon-speechBubbles oo-ui-iconElement oo-ui-image-invert'
	} )
	.text( ( 0 ).toLocaleString() );

	$messagesBadge
	.wrap(
		$( '<li>' )
		.attr( { id: 'pt-notifications-message' } )
		.addClass( 'mw-echo-ui-notificationBadgeButtonPopupWidget' )
	).parent()
	.insertAfter( '#pt-userpage' );

	var echoMessages = [
		'echo-new-messages',
		'echo-notification-message-text-only',
		'echo-overlay-link',
		'echo-specialpage',
		'mypreferences',
		'notification-edit-talk-page-bundle',
		'notification-edit-talk-page-with-section',
		'notification-edit-talk-page2',
		'notification-link-text-view-changes',
		'tooltip-pt-notifications-message'
	],
	localApi = new mw.Api(),
	enwikiApi = new mw.Api( {
		ajax: {
			url: '//en.wikipedia.org/w/api.php',
			dataType: 'jsonp',
			cache: true // default is false with dataType 'jsonp'
		}
	} );

	var msg = function () { // shorthand for mw.message(...).text()
		return mw.message.apply( this, arguments ).text();
	};

	function getNotificationBadgeButtonPopupWidgetPopup() {
		return $( '<div>' )
		.addClass( 'mw-echo-ui-notificationBadgeButtonPopupWidget-popup oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement oo-ui-popupWidget-anchored oo-ui-popupWidget' )
		.attr( 'aria-disabled', 'false' )
		.append(
			$( '<div>' )
			.addClass( 'oo-ui-popupWidget-popup' )
			.attr( 'style', 'width: 500px; height: auto; margin-left: -250px;' )
			.append(
				$( '<div>' )
				.addClass( 'oo-ui-popupWidget-head' )
				.append(
					$( '<span>' )
					.addClass( 'oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-iconWidget oo-ui-icon-speechBubbles oo-ui-iconElement' )
					.attr( 'aria-disabled', 'false' )
				)
				.append(
					$( '<span>' )
					.addClass( 'oo-ui-labelElement-label' )
					.text( msg( 'echo-notification-message-text-only' ) )
				)
			)
			.append(
				$( '<div>' )
				.addClass( 'oo-ui-clippableElement-clippable oo-ui-popupWidget-body' )
				.attr( 'style', 'overflow-y: scroll;' )
				.append(
					$( '<div>' )
					.addClass( 'oo-ui-widget oo-ui-widget-enabled oo-ui-selectWidget oo-ui-selectWidget-depressed mw-echo-ui-notificationsWidget mw-echo-ui-notificationsWidget-loadingOption' )
					.attr( 'aria-disabled', 'false' )
					.attr( 'role', 'listbox' )
				)
			)
			.append(
				$( '<div>' )
				.addClass( 'oo-ui-popupWidget-footer' )
				.append(
					$( '<div>' )
					.addClass( 'mw-echo-ui-notificationBadgeButtonPopupWidget-footer' )
					.append(
						$( '<div>' )
						.addClass( 'mw-echo-ui-notificationBadgeButtonPopupWidget-footer-buttons oo-ui-widget oo-ui-widget-enabled oo-ui-buttonGroupWidget' )
						.attr( 'aria-disabled', 'false' )
						.append(
							$( '<div>' )
							.addClass( 'mw-echo-ui-notificationBadgeButtonPopupWidget-footer-allnotifs oo-ui-widget oo-ui-widget-enabled oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-iconElement oo-ui-labelElement oo-ui-buttonWidget' )
							.attr( 'aria-disabled', 'false' )
							.append(
								$( '<a>' )
								.addClass( 'oo-ui-buttonElement-button' )
								.attr( 'role', 'button' )
								.attr( 'tabindex', '0' )
								.attr( 'aria-disabled', 'false' )
								.attr( 'href', mw.util.getUrl( nsSpecial + ':' + msg( 'echo-specialpage' ) ) )
								.attr( 'rel', 'nofollow' )
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-iconElement-icon oo-ui-icon-next' )
								)
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-labelElement-label' )
									.text( msg( 'echo-overlay-link' ) )
								)
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-indicatorElement-indicator' )
								)
							)
						)
						.append(
							$( '<div>' )
							.addClass( 'mw-echo-ui-notificationBadgeButtonPopupWidget-footer-preferences oo-ui-widget oo-ui-widget-enabled oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-iconElement oo-ui-labelElement oo-ui-buttonWidget' )
							.attr( 'aria-disabled', 'false' )
							.append(
								$( '<a>' )
								.addClass( 'oo-ui-buttonElement-button' )
								.attr( 'role', 'button' )
								.attr( 'tabindex', '0' )
								.attr( 'aria-disabled', 'false' )
								.attr( 'href', mw.util.getUrl( nsSpecial + ':Preferences' ) + '#mw-prefsection-echo' )
								.attr( 'rel', 'nofollow' )
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-iconElement-icon oo-ui-icon-advanced' )
								)
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-labelElement-label' )
									.text( msg( 'mypreferences' ) )
								)
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-indicatorElement-indicator' )
								)
							)
						)
					)
				)
			)
		)
		.append(
			$( '<div>' )
			.addClass( 'oo-ui-popupWidget-anchor' )
		);
	}

	function getNotificationItemWidget( userTalk, newMsg ) {
		return $( '<div>' )
		.addClass( 'oo-ui-widget oo-ui-widget-enabled oo-ui-optionWidget mw-echo-ui-notificationItemWidget mw-echo-ui-notificationItemWidget-message mw-echo-ui-notificationItemWidget-unread oo-ui-optionWidget-highlighted' )
		.attr( 'aria-disabled', 'false' )
		.attr( 'role', 'option' )
		.attr( 'aria-selected', 'false' )
		.append(
			$( '<a>' )
			.addClass( 'mw-echo-ui-notificationItemWidget-linkWrapper' )
			.attr( 'href', mw.util.getUrl( userTalk ) )
			.append(
				$( '<div>' )
				.addClass( 'mw-echo-ui-notificationItemWidget-icon' )
				.append(
					$( '<img>' )
					.attr( 'src', '//en.wikipedia.org/w/extensions/Echo/modules/icons/edit-user-talk.svg' )
				)
			)
			.append(
				$( '<div>' )
				.addClass( 'mw-echo-ui-notificationItemWidget-content' )
				.append(
					$( '<div>' )
					.addClass( 'mw-echo-ui-notificationItemWidget-content-message' )
					.append(
						$( '<div>' )
						.addClass( 'mw-echo-ui-notificationItemWidget-content-message-header' )
					)
					/*
					TODO: message body
					.append(
						$( '<div>' )
						.addClass( 'mw-echo-ui-notificationItemWidget-content-message-body' )
						.attr( 'dir', 'auto' )
						.append( 'TODO' )
					)
					*/
				)
				.append(
					$( '<div>' )
					.addClass( 'mw-echo-ui-notificationItemWidget-content-table' )
					.append(
						$( '<div>' )
						.addClass( 'mw-echo-ui-notificationItemWidget-content-actions' )
						.append(
							$( '<div>' )
							.addClass( 'mw-echo-ui-notificationItemWidget-content-actions-buttons oo-ui-widget oo-ui-widget-enabled oo-ui-selectWidget oo-ui-selectWidget-depressed oo-ui-buttonSelectWidget' )
							.attr( 'aria-disabled', 'false' )
							.attr( 'role', 'listbox' )
							.attr( 'tabindex', '0' )
							.append(
								$( '<div>' )
								.addClass( 'oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement oo-ui-optionWidget oo-ui-iconElement mw-echo-ui-menuItemWidget mw-echo-ui-menuItemWidget-prioritized oo-ui-element-hidden' )
								.attr( 'aria-disabled', 'false' )
								.attr( 'role', 'option' )
								.attr( 'aria-selected', 'false' )
								.append(
									// user link
									$( '<a>' )
									.addClass( 'mw-echo-ui-menuItemWidget-linkWrapper' )
									.attr( 'title', '' )
									.append(
										$( '<span>' )
										.addClass( 'oo-ui-iconElement-icon oo-ui-icon-userAvatar mw-echo-ui-menuItemWidget-icon' )
									)
									.append(
										$( '<div>' )
										.addClass( 'mw-echo-ui-menuItemWidget-content' )
										.append(
											// user name
											$( '<span>' )
											.addClass( 'oo-ui-labelElement-label mw-echo-ui-menuItemWidget-content-label' )
										)
										.append(
											$( '<span>' )
											.addClass( 'mw-echo-ui-menuItemWidget-content-description oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelWidget oo-ui-element-hidden' )
											.attr( 'aria-disabled', 'false' )
										)
									)
								)
							)
							.append(
								$( '<div>' )
								.addClass( 'oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement oo-ui-optionWidget oo-ui-iconElement mw-echo-ui-menuItemWidget mw-echo-ui-menuItemWidget-prioritized' )
								.attr( 'aria-disabled', 'false' )
								.attr( 'role', 'option' )
								.attr( 'aria-selected', 'false' )
								.append(
									$( '<a>' )
									.addClass( 'mw-echo-ui-menuItemWidget-linkWrapper' )
									.attr( 'href',
										mw.util.wikiScript() + '?' + $.param( {
											title: mw.util.wikiUrlencode( userTalk ),
											oldid: newMsg,
											diff: 'cur'
										} )
									)
									.append(
										$( '<span>' )
										.addClass( 'oo-ui-iconElement-icon oo-ui-icon-changes mw-echo-ui-menuItemWidget-icon' )
									)
									.append(
										$( '<div>' )
										.addClass( 'mw-echo-ui-menuItemWidget-content' )
										.append(
											$( '<span>' )
											.addClass( 'oo-ui-labelElement-label mw-echo-ui-menuItemWidget-content-label' )
											.text( msg( 'notification-link-text-view-changes' ) )
										)
										.append(
											$( '<span>' )
											.addClass( 'mw-echo-ui-menuItemWidget-content-description oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement-label oo-ui-labelWidget oo-ui-element-hidden' )
											.attr( 'aria-disabled', 'false' )
										)
									)
								)
							)
						)
						.append(
							$( '<div>' )
							.addClass( 'mw-echo-ui-notificationItemWidget-content-actions-menu oo-ui-widget oo-ui-widget-enabled oo-ui-buttonElement oo-ui-buttonElement-frameless oo-ui-iconElement oo-ui-buttonWidget mw-echo-ui-actionMenuPopupWidget' )
							.attr( 'aria-disabled', 'false' )
							.append(
								$( '<a>' )
								.addClass( 'oo-ui-buttonElement-button' )
								.attr( 'role', 'button' )
								.attr( 'tabindex', '0' )
								.attr( 'aria-disabled', 'false' )
								.attr( 'rel', 'nofollow' )
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-iconElement-icon oo-ui-icon-ellipsis' )
								)
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-labelElement-label' )
								)
								.append(
									$( '<span>' )
									.addClass( 'oo-ui-indicatorElement-indicator' )
								)
							)
						)
						/*
						TODO: date diff
						.append(
							$( '<span>' )
							.addClass( 'mw-echo-ui-notificationItemWidget-content-actions-timestamp oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement oo-ui-labelElement-label oo-ui-labelWidget' )
							.attr( 'aria-disabled', 'false' )
							.text( 'TODO' )
						)
						*/
					)
				)
			)
		);
	}

	function getNotifications() {
		if ( newMsg === null ) {
			return $.Deferred().resolve( [] );
		}

		return localApi.get( {
			formatversion: 2,
			prop: 'revisions',
			titles: userTalk,
			rvendid: newMsg,
			rvexcludeuser: userName,
			rvprop: 'user|comment|timestamp'
		} )
		.then( function ( rev ) {
			rev = rev.query.pages;
			if ( rev.length !== 1 ) {
				return $.Deferred().reject();
			}
			rev = rev[ 0 ].revisions;
			if ( rev === undefined || rev.length === 0 ) {
				return $.Deferred().reject();
			}
			var message, onlyUser;
			if ( rev.length === 1 ) {
				onlyUser = rev[ 0 ].user;
				var section = rev[ 0 ].comment.match( /^\/\* (\S+(.*\S)*) \*\// );
				if ( section === null ) {
					message = msg( 'notification-edit-talk-page2', rev[ 0 ].user, userName, '' );
				} else {
					message = msg( 'notification-edit-talk-page-with-section', rev[ 0 ].user, userName, section[ 1 ], section[ 1 ] );
				}
			} else {
				var users = $.map( rev, function ( elem ) {
					return elem.user;
				} );
				users = $.grep( users, function ( elem, pos ) {
					return users.indexOf( elem ) === pos;
				} );
				if ( users.length > 1 && users.indexOf( rev[ 0 ].user ) !== -1 ) {
					users.splice( users.indexOf( rev[ 0 ].user ), 1 );
				}
				if ( users.length === 1 ) {
					onlyUser = rev[ 0 ].user;
					message = msg( 'notification-edit-talk-page2', rev[ 0 ].user, userName, '' );
				} else {
					message = msg( 'notification-edit-talk-page-bundle', rev[ 0 ].user, userName, users.length, users.length );
					$messagesBadge.text( users.length.toLocaleString() );
				}
			}

			return localApi.get( {
				action: 'parse',
				formatversion: 2,
				text: message,
				contentmodel: 'wikitext',
				prop: 'text',
				disablepp: 1
			} )
			.then( function ( data ) {
				var $notif = getNotificationItemWidget( userTalk, newMsg );

				$notif
				.find( '.mw-echo-ui-notificationItemWidget-content-message-header' )
				.html( data.parse.text );

				if ( onlyUser !== undefined ) {
					$notif
					.find( '.mw-echo-ui-notificationItemWidget-content-actions-buttons div.mw-echo-ui-menuItemWidget' ).first()
					.removeClass( 'oo-ui-element-hidden' )
					.find( 'a.mw-echo-ui-menuItemWidget-linkWrapper' ).first()
					.attr( 'href', mw.util.getUrl( namespaces[ '3' ] + ':' + onlyUser ) )
					.find( '.oo-ui-labelElement-label' ).first()
					.text( onlyUser );
				}

				return [ $notif ];
			} );
		} );
	}

	enwikiApi.get( {
		formatversion: 2,
		meta: 'allmessages',
		amlang: mw.config.get( 'wgUserLanguage' ),
		ammessages: echoMessages.join( '|' )
	} )
	.done( function ( data ) {
		$.each( data.query.allmessages, function ( i, e ) {
			mw.messages.set( e.name, e.content );
		} );

		$messagesBadge
		.attr( 'title', msg( 'tooltip-pt-notifications-message' ) )
		.attr( 'href', mw.util.getUrl( nsSpecial + ':' + msg( 'echo-specialpage' ) ) )
		.one( 'click', function ( evt ) {
			evt.preventDefault();
			var $self = $( this );
			$self
			.click( function ( evt ) {
				evt.preventDefault();
				$( this ).parent().find( '.mw-echo-ui-notificationBadgeButtonPopupWidget-popup' ).toggle();
			} )
			.parent()
			.append( getNotificationBadgeButtonPopupWidgetPopup() );

			$( '.oo-ui-popupWidget-head' )
			.addClass( 'oo-ui-pendingElement-pending' );

			getNotifications()
			.done( function ( notifs ) {
				if ( notifs.length > 0 ) {
					$( '.mw-echo-ui-notificationsWidget' )
					.removeClass( 'mw-echo-ui-notificationsWidget-loadingOption' )
					.append( notifs );
				}

				$( '.oo-ui-popupWidget-head' )
				.removeClass( 'oo-ui-pendingElement-pending' );
			} )
			.fail( function () {
				$( '.oo-ui-popupWidget-head' )
				.removeClass( 'oo-ui-pendingElement-pending' );
			} );
		} );

		if ( newMsg !== null ) {
			$messagesBadge
			.text( ( 1 ).toLocaleString() )
			.addClass( 'oo-ui-flaggedElement-unseen' ); // blue badge

			$( '#pt-mytalk a' )
			.addClass( 'mw-echo-alert' )
			.text( msg( 'echo-new-messages' ) );
		}
	} );
} );