Увага: Після публікування слід очистити кеш браузера, щоб побачити зміни.

  • Firefox / Safari: тримайте Shift, коли натискаєте Оновити, або натисніть Ctrl-F5 чи Ctrl-Shift-R (⌘-R на Apple Mac)
  • Google Chrome: натисніть Ctrl-Shift-R (⌘-Shift-R на Apple Mac)
  • Internet Explorer / Edge: тримайте Ctrl, коли натискаєте Оновити, або натисніть Ctrl-F5
  • Opera: натисніть Ctrl-F5
/** Wikidata Freeze gadget */
( function() {

	var PREFIX = "Module:WikidataCache/";
	var MODULE_DOC = "{{wikidata cache module}}";

	var i18n = {
		menuButton: 'Кеш Вікіданих',

		buttonCancel: 'Скасувати',
		buttonCompare: 'Перевірити зміни',
		buttonCreateCache: 'Створити локальний кеш',
		buttonRemove: 'Видалити локальний кеш',
		buttonUpdateCache: 'Оновити локальний кеш',

		descriptionCached: 'Ця сторінка має локальний кеш елемента Вікіданих. '
				+ 'Ця копія була створена для запобігання потрапляння змін у Вікіданих одразу в україномовну Вікіпедію. '
				+ 'Цей елемент співпадає з теперішньою версією Вікіданих. '
				+ 'Якщо ви є адміністратором, ви можете видалити локальний кеш, якщо додатковий захист більше не потрібен.',
		descriptionNotCached: 'Ця сторінка не має локального кешу елементу Вікіданих. '
				+ 'Ви можете створити локальну копію, і тим самим захистити сторінку від змін, що відбудуться на Вікіданих. '
				+ 'Це може бути корисно для захищених сторінок, стабілізованих сторінок та сторінок, що часто піддають вандалізму на Вікіданих. '
				+ 'Для створення або оновлення кешу потрібні права адміністратора.',
		descriptionOutdated: 'Ця сторінка має локальний кеш елемента Вікіданих. '
				+ 'Ця копія була створена для запобігання потрапляння змін у Вікіданих одразу в україномовну Вікіпедію. '
				+ 'На даний момент у Вікіданих є більш свіжа інформація (можливо, що містить неконсенсусні або вандальні зміни). '
				+ 'Якщо Ви є адміністратром, Ви можете оновити локальний кеш або видалити локальну версію, якщо додатковий захист більше не потрібен.',

		errorLoadingWikidata: 'Помилка завантаження елемента з Вікіданих',
		errorUpdatingCache: 'Помилка збереження кеша',

		statusLoadingWikidata: 'Отримання інформації з Вікіданих...',
		statusDeleteDonePurge: 'Видалення кешу завершено, оновлення сторінки...',
		statusGenerateLua: 'Генерация тексту LUA-модуля...',
		statusRemovingCache: 'Видалення локального кешу...',
		statusUpdateDonePurge: 'Оновлення кешу завершено, оновлення сторінки...',
		statusUpdatingCache: 'Оновлення локального кешу...',

		summary: 'Оновлення локального кешу',

		title: 'Керування локальним кешом Вікіданих',
	};

	/* Some utils functions */

	function getFirstObjectValue( obj ) {
		return obj[Object.keys( obj )[0]];
	}

	function getWikidataApiPrefix() {
		return '//www.wikidata.org/w/api.php' + '?origin=' + encodeURIComponent( location.protocol + mw.config.get( 'wgServer' ) ) + '&format=json';
	}

	function isWikidata() {
		return mw.config.get( 'wgSiteName' ) === 'Wikidata';
	}

	function purge() {
		window.location.replace( mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/index.php?action=purge&title='
				+ encodeURIComponent( mw.config.get( 'wgPageName' ) ) );
	}

	/* Different dialog windows depending on status of local cache */

	function dialogForNotCached() {
		"use strict";

		var dialog = $( document.createElement( "div" ) );
		dialog.append( $( document.createElement( "p" ) ).text( i18n.descriptionNotCached ) );
		dialog.append( document.createElement( "br" ) );
		dialog.append( $( document.createElement( "div" ) ).text( i18n.buttonCreateCache ).button().click( function() {
			dialog.dialog( 'close' );
			updateCache();
		} ) );
		dialog.dialog( {
			title: i18n.title,
			autoOpen: true,
			width: 500,
			buttons: [ {
				text: i18n.buttonCancel,
				click: function() {
					dialog.dialog( 'close' );
				},
			} ]
		} );
	}

	function dialogForCurrent() {
		"use strict";

		var dialog = $( document.createElement( "div" ) );
		$( document.createElement( "p" ) ).text( i18n.descriptionCached ).appendTo( dialog );
		$( document.createElement( "br" ) ).appendTo( dialog );
		$( document.createElement( "div" ) ).text( i18n.buttonRemove ).button().click( function() {
			dialog.dialog( 'close' );
			removeCache();
		} ).appendTo( dialog );
		dialog.dialog( {
			title: i18n.title,
			autoOpen: true,
			width: 500,
			buttons: [ {
				text: i18n.buttonCancel,
				click: function() {
					dialog.dialog( 'close' );
				},
			} ]
		} );
	}

	function dialogForOutdated( oldRevisionId ) {
		"use strict";

		var dialog = $( document.createElement( "div" ) );
		$( document.createElement( "p" ) ).text( i18n.descriptionOutdated ).appendTo( dialog );
		$( document.createElement( "br" ) ).appendTo( dialog );

		$( document.createElement( "a" ) ) //
		/**/.text( i18n.buttonCompare )
		/**/.button()
		/**/.attr( 'href', '//www.wikidata.org/w/index.php?diff=cur&oldid=' + oldRevisionId )
		/**/.attr( 'target', '_blank' )
		/**/.appendTo( dialog );
		$( document.createElement( "br" ) ).appendTo( dialog );

		$( document.createElement( "div" ) )
		/**/.text( i18n.buttonUpdateCache )
		/**/.button()
		/**/.click( function() {
			dialog.dialog( 'close' );
			updateCache();
		} )
		/**/.appendTo( dialog );
		$( document.createElement( "br" ) ).appendTo( dialog );

		$( document.createElement( "div" ) ).text( i18n.buttonRemove ).button().click( function() {
			dialog.dialog( 'close' );
			removeCache();
		} ).appendTo( dialog );
		dialog.dialog( {
			title: i18n.title,
			autoOpen: true,
			width: 500,
			buttons: [ {
				text: i18n.buttonCancel,
				click: function() {
					dialog.dialog( 'close' );
				},
			} ]
		} );
	}

	/** Update cache function */
	function updateCache() {
		"use strict";
		jsMsg( i18n.statusLoadingWikidata );

		var entityId = mw.config.get( 'wgWikibaseItemId' ).toUpperCase();
		$.ajax( {
			type: 'GET',
			url: getWikidataApiPrefix() + '&action=wbgetentities&ids=' + entityId,
			dataType: 'json',
			success: function( result ) {
				var entity = result.entities[entityId];
				var api = ( new mw.Api() );

				jsMsg( i18n.statusGenerateLua );
				var lua = generateLua( entity );

				jsMsg( i18n.statusUpdatingCache );
				$.when(

				api.postWithEditToken( {
					action: 'edit',
					format: 'json',
					title: PREFIX + entityId.toUpperCase(),
					summary: i18n.summary,
					text: lua,
				}, function() {
					// ok
				}, function( text, data ) {
					// error
					alert( i18n.errorUpdatingCache + ': ' + text );
				} ),

				api.postWithEditToken( {
					action: 'edit',
					format: 'json',
					title: PREFIX + entityId + '/doc',
					summary: i18n.summary,
					createonly: 1,
					text: MODULE_DOC,
				} )

				).done( function() {
					jsMsg( i18n.statusUpdateDonePurge );
					purge();
				} );

			},
			fail: function() {
				alert( i18n.errorLoadingWikidata );
			},
		} );
	}

	function generateLua( json ) {
		"use strict";
		return "return " + generateLuaImpl( json, 1 ) + ";";
	}

	function repeat( str, count ) {
		var result = "";
		for ( var i = 0; i < count; i++ ) {
			result = result + str;
		}
		return result;
	}

	function escapeForLua( str ) {
		return str.replace( "'", "\\'" );
	}

	function generateLuaImpl( value, level ) {
		"use strict";

		if ( typeof value === 'string' ) {
			return "'" + escapeForLua( value ) + "'";
		} else if ( typeof value === 'number' ) {
			return '' + value;
		} else if ( $.isArray( value ) ) {
			var result = '{';
			$.each( value, function( i, item ) {
				var subvalue = generateLuaImpl( item, level + 1 );
				if ( subvalue ) {
					result = result + subvalue + ', ';
				}
			} );
			return result + '}';
		} else if ( typeof value === 'object' ) {
			var result = '{';
			for ( var key in value ) {
				var subvalue = value[key];
				var subResult = generateLuaImpl( subvalue, level + 1 );
				if ( subResult ) {
					result = result + '\n' + repeat( '\t', level ) + generateLuaLabel( key ) + " = " + subResult + ',';
				}
			}
			return result + '\n' + repeat( '\t', level - 1 ) + '}';
		}
		return;
	}

	function generateLuaLabel( key ) {
		if ( /^[a-zA-Z][a-zA-Z0-9\_]*$/.exec( key ) ) {
			return key;
		}
		return "['" + escapeForLua( key ) + "']";
	}

	/** Remove cache function */
	function removeCache() {
		jsMsg( i18n.statusRemovingCache );

		var entityId = mw.config.get( 'wgWikibaseItemId' ).toUpperCase();
		var api = ( new mw.Api() );

		$.when(

		api.postWithToken( 'delete', {
			action: 'delete',
			title: PREFIX + entityId,
		} ),

		api.postWithToken( 'delete', {
			action: 'delete',
			title: PREFIX + entityId + '/doc',
		} )

		).done( function() {
			jsMsg( i18n.statusDeleteDonePurge );
			purge();
		} );
	}

	mw.loader.using( [ 'jquery.ui', 'mediawiki.api', ], function() {
		"use strict";

		if ( isWikidata() ) {
			return;
		}

		/** @return {string} */
		var entityId = mw.config.get( 'wgWikibaseItemId' );
		if ( typeof entityId === 'undefined' || entityId == null ) {
			return;
		}

		$.when( $.ajax( {
			type: 'GET',
			url: getWikidataApiPrefix() + '&action=query&prop=revisions&titles=' + entityId.toUpperCase() + '&rvprop=' + encodeURIComponent( 'content|ids' ),
			dataType: 'json',
		} ), $.ajax( {
			type: 'GET',
			url: '/w/api.php?format=json&action=query&prop=revisions&titles=Module:WikidataCache/' + entityId.toUpperCase() + '&rvprop=' + encodeURIComponent( 'content|ids' ),
			dataType: 'json',
		} ) ).done( function( wikidataResponse, wikipediaResponse ) {
			var wikidataPage = getFirstObjectValue( wikidataResponse[0].query.pages );

			if ( typeof wikidataPage.missing !== "undefined" ) {
				mw.log.warn( 'Entity is missing on Wikidata: ' + entityId );
				return;
			}

			var wikipediaPage = getFirstObjectValue( wikipediaResponse[0].query.pages );
			if ( typeof wikipediaPage.missing !== "undefined" ) {

				// cache is missed
				var li = $( document.createElement( 'li' ) ).addClass( 'plainlinks' );
				$( document.createElement( 'a' ) ).addClass( 'wef-menuitem-freeze' ).addClass( 'wef-menuitem-freeze-missing' ).click( function() {
					dialogForNotCached();
				} ).text( i18n.menuButton ).appendTo( li );
				$( '#p-tb div ul' ).append( li );

			} else {
				// check revision
				var content = wikipediaPage.revisions[0]["*"];
				var storedRevision = Number( content.match( /lastrevid\s*\=\s*\s*([0-9]+),/ )[1] );
				var existedRevision = Number( wikidataPage.revisions[0].revid );

				if ( storedRevision === existedRevision ) {

					// cache is exist and actual
					var li = $( document.createElement( 'li' ) ).addClass( 'plainlinks' );
					$( document.createElement( 'a' ) ).addClass( 'wef-menuitem-freeze' ).addClass( 'wef-menuitem-freeze-current' ).click( function() {
						dialogForCurrent();
					} ).text( i18n.menuButton ).appendTo( li );
					$( '#p-tb div ul' ).append( li );

				} else {

					// cache is exist and outdated
					var li = $( document.createElement( 'li' ) ).addClass( 'plainlinks' );
					$( document.createElement( 'a' ) ).addClass( 'wef-menuitem-freeze' ).addClass( 'wef-menuitem-freeze-outdated' ).click( function() {
						dialogForOutdated( storedRevision );
					} ).text( i18n.menuButton ).appendTo( li );
					$( '#p-tb div ul' ).append( li );

				}
			}
		} );

	} );
} )();