Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
  • Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
  • Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
  • Opera: Нажмите Ctrl+F5.
mw.loader.using(['mediawiki.api'], function() {  var AsyncUtils = {     runSequence: function runSequence(functions, onSuccess, results) {         if (!results) {             results = [];         }          if (functions.length > 0) {             var firstFunction = functions[0];             firstFunction(function (result) {                 results.push(result);                 setTimeout( // hack to break recursion chain                 function () {                     AsyncUtils.runSequence(functions.slice(1), onSuccess, results);                 }, 0);             });         } else {             onSuccess(results);         }     },     runChunks: function runChunks(runSingleChunkFunction, maxChunkSize, data, onSuccess) {         var chunkRunFunctions = [];          var _loop = function _loop(dataNumStart) {             var dataChunk = data.slice(dataNumStart, dataNumStart + maxChunkSize);             chunkRunFunctions.push(function (onSuccess) {                 return runSingleChunkFunction(dataChunk, onSuccess);             });         };          for (var dataNumStart = 0; dataNumStart < data.length; dataNumStart += maxChunkSize) {             _loop(dataNumStart);         }         this.runSequence(chunkRunFunctions, function (chunkResults) {             var result = chunkResults.reduce(function (current, total) {                 return total.concat(current);             }, []);             onSuccess(result);         });     } };   var StringUtils = {     contains: function contains(string, substring) {         return string.indexOf(substring) >= 0;     },     trim: function trim(string) {         // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim         return string.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');     } };   var ArrayUtils = {     hasElement: function hasElement(array, element) {         return array.indexOf(element) >= 0;     },     inArray: function inArray(element, array) {         return this.hasElement(array, element);     } };   var ObjectUtils = {     merge: function merge(obj1, obj2) {         var result = {};         for (var prop in obj1) {             if (obj1.hasOwnProperty(prop)) {                 result[prop] = obj1[prop];             }         }         for (var _prop in obj2) {             if (obj2.hasOwnProperty(_prop)) {                 result[_prop] = obj2[_prop];             }         }         return result;     } };   var MediaWikiPage = {     getPageName: function getPageName() {         return mw.config.get('wgPageName');     },     isDiffMode: function isDiffMode() {         return $('table.diff').length > 0;     },     isLastRevision: function isLastRevision() {         return mw.config.get('wgCurRevisionId') === mw.config.get('wgRevisionId');     },     isViewAction: function isViewAction() {         return mw.config.get('wgAction') === 'view';     },     isViewSourceMode: function isViewSourceMode() {         return $('#ca-viewsource').length > 0;     },     isViewSpecificRevisionMode: function isViewSpecificRevisionMode() {         return $('#mw-revision-info').length > 0;     },     isRegularNamespace: function isRegularNamespace() {         var namespace = mw.config.get('wgNamespaceNumber');         return namespace === 0 || namespace === 2 || namespace === 4;     } };   var CommonsApi = {     baseUrl: 'https://commons.wikimedia.org/w/api.php',      executeRequest: function executeRequest(parameters, onSuccess) {         $.ajax({             url: this.baseUrl,             data: parameters,             crossDomain: true,             dataType: 'jsonp'         }).done(function (data) {             onSuccess(data);         });     },      getCategoryFiles: function getCategoryFiles(category, limit, onSuccess) {         var self = this;          self.executeRequest({             'action': 'query',             'list': 'categorymembers',             'cmtype': 'file',             'cmtitle': 'Category:' + category,             'cmlimit': 'max',             'format': 'json'         }, function (data) {             if (data.query && data.query.categorymembers) {                 var files = [];                 data.query.categorymembers.forEach(function (member) {                     if (member.title) {                         files.push(member.title);                     }                 });                  onSuccess(files);             }         });     },      getCategoryImages: function getCategoryImages(category, limit, onSucess) {         this.getCategoryFiles(category, limit, function (files) {             var images = [];             files.forEach(function (file) {                 var extension = file.toLowerCase().substr(file.length - 4);                 if (extension === '.jpg' || extension === '.png' || extension === '.gif') {                     images.push(file);                 }             });             onSucess(images);         });     },      getImageInfo: function getImageInfo(image, onSuccess) {         var self = this;          self.executeRequest({             'action': 'query',             'titles': image,             'prop': 'imageinfo|revisions',             'iiprop': 'url',             'iiurlwidth': '200',             'iiurlheight': '200',             'rvprop': 'content',             'rvlimit': '1',             'format': 'json'         }, function (data) {             if (!data.query || !data.query.pages) {                 return;             }              var pages = data.query.pages;             var firstPage = pages[Object.keys(pages)[0]];             if (!firstPage || !firstPage.imageinfo || firstPage.imageinfo.length <= 0) {                 return;             }             var text = '';             if (firstPage.revisions && firstPage.revisions.length > 0) {                 var revision = firstPage.revisions[0];                 if (revision['*']) {                     text = revision['*'];                 }             }              var imageInfo = firstPage.imageinfo[0];             onSuccess({                 'image': image,                 'thumb': imageInfo.thumburl,                 'text': text,                 'url': imageInfo.url             });         });     },      getImagesInfo: function getImagesInfo(images, onSuccess) {         var self = this;         AsyncUtils.runSequence(images.map(function (image) {             return function (onSuccess) {                 self.getImageInfo(image, onSuccess);             };         }), function (imageInfos) {             onSuccess(imageInfos);         });     },      /**      *      * @param categories list of category titles, e.g. ['Novosibirsk', 'Tomsk', 'Culture_of_Novosibirsk']      * @param onSuccess function which accepts single argument - list which has category      * titles for each category which has at least one file, e.g.      * ['Novosibirsk': 'Culture_of_Novosibirsk']      */     hasCategoriesFiles: function hasCategoriesFiles(categories, onSuccess) {         var _this = this;          var maxChunkSize = 30;         AsyncUtils.runChunks(function (categoriesChunk, onSuccess) {             _this.executeRequest({                 action: 'query',                 titles: categoriesChunk.join("|"),                 prop: 'categoryinfo',                 format: 'json'             }, function (data) {                 var result = [];                  if (!data || !data.query || !data.query.pages) {                     return;                 }                 Object.keys(data.query.pages).forEach(function (key) {                     var pageInfo = data.query.pages[key];                     if (pageInfo.title && pageInfo.categoryinfo && pageInfo.categoryinfo.files && pageInfo.categoryinfo.files > 0) {                         result.push(pageInfo.title);                     }                 });                  onSuccess(result);             });         }, maxChunkSize, categories, onSuccess);     },      /**      *      * @param categories list of category titles, e.g. ['Novosibirsk', 'Tomsk', 'Culture_of_Novosibirsk']      * @param onSuccess function which accepts single argument -  list where each item has category      * title and files count, e.g. [      *   {category: 'Novosibirsk', files: 51},      *   {category: 'Tomsk', files: 42}      *   {category: 'Culture_of_Novosibirsk', files: 48}      * ]      */     countCategoriesFiles: function countCategoriesFiles(categories, onSuccess) {         var _this2 = this;          var maxChunkSize = 30;         AsyncUtils.runChunks(function (categoriesChunk, onSuccess) {             _this2.executeRequest({                 action: 'query',                 titles: categoriesChunk.join("|"),                 prop: 'categoryinfo',                 format: 'json'             }, function (data) {                 var result = [];                  if (!data || !data.query || !data.query.pages) {                     return;                 }                 Object.keys(data.query.pages).forEach(function (key) {                     var pageInfo = data.query.pages[key];                     if (pageInfo.title) {                         var filesCount = pageInfo.categoryinfo && pageInfo.categoryinfo.files ? pageInfo.categoryinfo.files : 0;                         result.push({                             category: pageInfo.title,                             files: filesCount                         });                     }                 });                  onSuccess(result);             });         }, maxChunkSize, categories, onSuccess);     } };   var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }  var ListingTableHtml = function () {     function ListingTableHtml(listingTableElement) {         _classCallCheck(this, ListingTableHtml);          this._listingTableElement = listingTableElement;     }      _createClass(ListingTableHtml, [{         key: 'hasListingPhoto',         value: function hasListingPhoto() {             var hasPhoto = true;              this._listingTableElement.find('a').each(function () {                 var aElement = $(this);                 if (aElement.text() === 'Нет фото' || aElement.attr('title') === 'Нет фото') {                     hasPhoto = false;                     return false;                 }             });              return hasPhoto;         }     }, {         key: 'addWarning',         value: function addWarning(warningText) {             var nameElement = this._listingTableElement.find('span.monument-name').first();             if (!nameElement) {                 return;             }             var warningElement = $('<span>', { html: '&nbsp;[' + warningText + ']', style: 'color: red;' });             warningElement.insertAfter(nameElement);         }     }, {         key: 'addGalleryFilesCount',         value: function addGalleryFilesCount(filesCount) {             this._listingTableElement.find('a.extiw').each(function () {                 var linkElem = $(this);                 if (linkElem.attr('href').indexOf('https://commons.wikimedia.org') !== 0) return;                  linkElem.text(linkElem.text() + " (" + filesCount + ")");             });         }     }, {         key: 'findCommonsCategory',         value: function findCommonsCategory(parentCategory) {             var commonsCategory = null;              this._listingTableElement.find('a.extiw').each(function () {                 var linkElem = $(this);                 var href = linkElem.attr('href');                 var parentCategoryHtmlName = parentCategory.replace(/ /g, '_');                 if (!href) {                     return;                 }                  if (href.indexOf('https://commons.wikimedia.org/wiki/Category:' + parentCategoryHtmlName + '/') === 0) {                     commonsCategory = href.replace(/https:\/\/commons\.wikimedia\.org\/wiki\//, '');                     return false;                 }             });              return commonsCategory.replace(/_/g, ' ');         }     }]);      return ListingTableHtml; }();   var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }  var ListingSection = function () {     function ListingSection(headerElement, sectionIndex) {         _classCallCheck(this, ListingSection);          this._headerElement = headerElement;         this._sectionIndex = sectionIndex;     }      _createClass(ListingSection, [{         key: 'getHeaderElement',         value: function getHeaderElement() {             return this._headerElement;         }     }, {         key: 'getSectionIndex',         value: function getSectionIndex() {             return this._sectionIndex;         }     }]);      return ListingSection; }();  var ListingTable = function () {     function ListingTable(tableElement, sectionIndex, listingIndex) {         _classCallCheck(this, ListingTable);          this._tableElement = tableElement;         this._sectionIndex = sectionIndex;         this._listingIndex = listingIndex;     }      _createClass(ListingTable, [{         key: 'getTableElement',         value: function getTableElement() {             return this._tableElement;         }     }, {         key: 'getSectionIndex',         value: function getSectionIndex() {             return this._sectionIndex;         }     }, {         key: 'getListingIndex',         value: function getListingIndex() {             return this._listingIndex;         }     }]);      return ListingTable; }();  var ListingPageElements = function () {     function ListingPageElements(sections, listingTables) {         _classCallCheck(this, ListingPageElements);          this._sections = sections;         this._listingTables = listingTables;     }      /**      * @returns {ListingSection[]}      */       _createClass(ListingPageElements, [{         key: 'getSections',         value: function getSections() {             return this._sections;         }     }, {         key: 'getListingTables',         value: function getListingTables() {             return this._listingTables;         }     }]);      return ListingPageElements; }();  var ListingEditorUtils = {     isEditablePage: function isEditablePage() {         return MediaWikiPage.isRegularNamespace() && MediaWikiPage.isViewAction() && MediaWikiPage.isLastRevision() && !MediaWikiPage.isDiffMode() && !MediaWikiPage.isViewSpecificRevisionMode() && !MediaWikiPage.isViewSourceMode();     },       /**      * @returns {ListingPageElements}      */     getListingPageElements: function getListingPageElements() {         var pageBodyContentElement = $('#bodyContent');          var currentSectionIndex = 0;         var currentListingIndex = 0;          var sections = [];         var listingTables = [];          function isTableOfContentsHeader(headerElement) {             return headerElement.parents('.toc').length > 0;         }          // Here we add buttons to:         // - add new listing - for each section header         // - edit existing listing - for each existing listing         //         // - section index, to which we are going to add new listing         // - section index and listing index (within a section) for listing which we are going to edit         // To calculate section index and listing index, we iterate over all section header and listing         // table elements sequentially (in the same order as we have them in HTML).         // When we meet header - we consider that new section is started and increase current section index,         // and reset current listing index (listings are enumerated within section). All listings belong         // to that section until we meet the next header.         // When we meet listing table - we increase current listing index.         pageBodyContentElement.find('h1, h2, h3, h4, h5, h6, table.monument').each(function () {             if (ArrayUtils.inArray(this.tagName, ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'])) {                 var headerElement = $(this);                  if (!isTableOfContentsHeader(headerElement)) {                     currentSectionIndex++;                     currentListingIndex = 0;                     sections.push(new ListingSection(headerElement, currentSectionIndex));                 }             } else if (this.tagName === 'TABLE') {                 var listingTable = $(this);                 listingTables.push(new ListingTable(listingTable, currentSectionIndex, currentListingIndex));                 currentListingIndex++;             }         });          return new ListingPageElements(sections, listingTables);     } };   var pageTypes = [{     galleryTitle: "WLE",     parentCategoryName: "Protected areas of Russia",     pageNamespace: "Природные_памятники" }, {     galleryTitle: "WLM",     parentCategoryName: "WLM",     pageNamespace: "Культурное_наследие" }];  pageTypes.forEach(function (pageType) {     if (!ListingEditorUtils.isEditablePage() || !StringUtils.contains(MediaWikiPage.getPageName(), pageType.pageNamespace)) {         return;     }      var listingPageElements = ListingEditorUtils.getListingPageElements();     var listingTables = listingPageElements.getListingTables();      var listingCommonsCategory = [];      listingTables.forEach(function (listingTable) {         var listingTableElement = $(listingTable.getTableElement());         var listingTableHtml = new ListingTableHtml(listingTableElement);          var commonsCategory = listingTableHtml.findCommonsCategory(pageType.parentCategoryName);         if (!commonsCategory) {             return;         }         listingCommonsCategory.push({             listingTableHtml: listingTableHtml,             category: commonsCategory         });     });      CommonsApi.countCategoriesFiles(listingCommonsCategory.map(function (item) {         return item.category.replace(/_/g, ' ');     }), function (categoriesWithImages) {         var filesCountByCategory = {};         categoriesWithImages.forEach(function (item) {             filesCountByCategory[item.category] = item.files;         });          listingCommonsCategory.forEach(function (listingItem) {             var filesCount = filesCountByCategory[listingItem.category.replace(/_'/g, ' ')];             if (filesCount !== undefined) {                 listingItem.listingTableHtml.addGalleryFilesCount(filesCount);             }         });     }); });  });