|
|
Line 10: |
Line 10: |
| | | |
| const LAST_LOG = '14:22, 8 March 2018 (UTC)'; | | const LAST_LOG = '14:22, 8 March 2018 (UTC)'; |
− |
| |
− | const conf = mw.config.get([
| |
− | 'wgNamespaceNumber',
| |
− | 'wgPageName'
| |
− | ]);
| |
− | $.extend(conf, {
| |
− | action: mw.util.getParamValue('action'),
| |
− | api: function(query, callbacks, method) {
| |
− | return new mw.Api()[method || 'get'](query)
| |
− | .done(callbacks.done)
| |
− | .fail(callbacks.fail)
| |
− | .always(callbacks.always);
| |
− | }
| |
− | });
| |
− |
| |
− | const page = {
| |
− | hasContent: $('#ca-history').length && conf.wgNamespaceNumber !== -1,
| |
− | $content: $('#content'),
| |
− | $mwHead: $('#mw-head'),
| |
− | $userBar: $('#p-personal').children('ul'),
| |
− | $leftNav: $('#left-navigation').find('#p-namespaces').children('ul'),
| |
− | $rightNav: $('#right-navigation').find('#p-views').children('ul'),
| |
− | $rightMenu: $('#right-navigation').find('#p-cactions').find('ul'),
| |
− | $star: $('#ca-watch').length ? $('#ca-watch') : $('#ca-unwatch')
| |
− | };
| |
− |
| |
− | /** End. */
| |
− |
| |
− |
| |
− | /**
| |
− | * Constructors and prototypes.
| |
− | */
| |
− |
| |
− | // ------
| |
− | // String:
| |
− | String.prototype.strip = function(c) {
| |
− | return String(this).replace(new RegExp(c || ' ', 'g'), '');
| |
− | };
| |
− |
| |
− | // ------
| |
− | // Query:
| |
− | function Query(endpoint, query) {
| |
− | // query | url | full | endpoint
| |
− | if($.isPlainObject(endpoint)) {
| |
− | query = endpoint;
| |
− | endpoint = undefined;
| |
− | }
| |
− | this.query = query || {};
| |
− | this.url = this.fetch();
| |
− | this.full = this.extend();
| |
− | this.endpoint = String(endpoint || '').trim().toLowerCase().strip('.php');
| |
− | }
| |
− | // Extend prototype:
| |
− | $.extend(Query.prototype, {
| |
− | // "Private" methods:
| |
− | extend: function() {
| |
− | return $.extend({}, this.url, this.query);
| |
− | },
| |
− | fetch: function(parameters) {
| |
− | const query = {};
| |
− | const getValue = function(parameter) {
| |
− | return query[parameter];
| |
− | };
| |
− | const getValues = function(parameters) {
| |
− | return (Array.isArray(parameters)
| |
− | ? parameters.map(function(v) {
| |
− | return getValue(v);
| |
− | })
| |
− | : getValue(parameters)
| |
− | );
| |
− | };
| |
− | window.location.search.slice(1).split('&').forEach(function(component) {
| |
− | if(!component.trim()) return;
| |
− | const parts = component.split('=');
| |
− | query[window.decodeURIComponent(parts[0])] = window.decodeURIComponent(parts[1] || '');
| |
− | });
| |
− | return parameters ? getValues(parameters) : query;
| |
− | },
| |
− |
| |
− | // "Getters":
| |
− | getEndpoint: function() {
| |
− | return this.endpoint;
| |
− | },
| |
− | getQuery: function(type) {
| |
− | switch((type+'').trim().toLowerCase()) {
| |
− | case 'full': return this.full;
| |
− | case 'url': return this.url;
| |
− | default: return this.query;
| |
− | }
| |
− | },
| |
− |
| |
− | // Other:
| |
− | print: function(full) {
| |
− | const query = this.getQuery(full && 'full');
| |
− | return [
| |
− | mw.util.wikiScript(this.getEndpoint()),
| |
− | Object.keys(query).map(function(key) {
| |
− | return [
| |
− | window.encodeURIComponent(key),
| |
− | window.encodeURIComponent(query[key])
| |
− | ].join('=');
| |
− | }).join('&')
| |
− | ].join('?');
| |
− | },
| |
− | });
| |
− |
| |
− | // Static method:
| |
− | Query.fetch = Query.prototype.fetch;
| |
− |
| |
− | // ------
| |
− | // State:
| |
− | function State(message) {
| |
− | this.message = message || 'Pending...';
| |
− | this.status = 'pending';
| |
− | }
| |
− | $.extend(State.prototype, {
| |
− | getMessage: function() {
| |
− | return this.message;
| |
− | },
| |
− | setMessage: function(m) {
| |
− | this.message = m || '';
| |
− | return this;
| |
− | },
| |
− | getStatus: function() {
| |
− | return this.status;
| |
− | },
| |
− | setStatus: function(s) { // Private.
| |
− | this.status = s || '';
| |
− | return this;
| |
− | },
| |
− | pending: function() {
| |
− | return this.setStatus('pending');
| |
− | },
| |
− | done: function() {
| |
− | return this.setStatus('done');
| |
− | },
| |
− | failed: function() {
| |
− | return this.setStatus('failed');
| |
− | },
| |
− | clear: function() {
| |
− | this.setMessage();
| |
− | this.setStatus();
| |
− | return this;
| |
− | },
| |
− | reset: function() { // TODO: .assign or something
| |
− | this.setMessage('Pending...');
| |
− | this.setStatus('pending');
| |
− | return this;
| |
− | },
| |
− | show: function() {
| |
− | items.$specialMessages.removeClass('hide')
| |
− | .children()
| |
− | .removeClass().addClass(this.getStatus())
| |
− | .text(this.getMessage());
| |
− | return this;
| |
− | }
| |
− | });
| |
− |
| |
− | /** End. */
| |
− |
| |
− |
| |
− | /**
| |
− | * Utility functions.
| |
− | */
| |
− |
| |
− |
| |
− | /** End. */
| |
− |
| |
− |
| |
− | /**
| |
− | * Buttons.
| |
− | */
| |
| | | |
− | const items = {
| |
− | $searchBar: $('<li>', {
| |
− | id: 'pt-search',
| |
− | html: $('#p-search')
| |
− | }),
| |
− | $specialMessages: $('<li>', {
| |
− | id: 'pt-special-messages',
| |
− | html: $('<span>', {
| |
− | id: 'special-messages',
| |
− | class: 'hide'
| |
− | })
| |
− | }),
| |
− | $userBarBottom: $('<div>', {
| |
− | id: 'p-bottom'
| |
− | }),
| |
− |
| |
− | $diff: $('<li>', {
| |
− | id: 'ca-diff',
| |
− | class: 'collapsible',
| |
− | html: $('<span>', {
| |
− | html: $('<a>', {
| |
− | title: 'Last diff for this page.',
| |
− | text: 'Diff'
| |
− | })
| |
− | })
| |
− | }),
| |
− | $nullEdit: $('<li>', {
| |
− | id: 'ca-null',
| |
− | class: 'collapsible',
| |
− | html: $('<span>', {
| |
− | html: $('<a>', {
| |
− | href: '#',
| |
− | title: 'Null edit this page.',
| |
− | text: 'Null'
| |
− | })
| |
− | })
| |
− | }),
| |
− |
| |
− | $raw: $('<li>', {
| |
− | id: 'ca-raw',
| |
− | html: $('<a>', {
| |
− | href: new Query({
| |
− | title: conf.wgPageName,
| |
− | action: 'raw'
| |
− | }).print(),
| |
− | title: 'View the raw content.',
| |
− | text: 'Raw'
| |
− | })
| |
− | }),
| |
− | $safe: $('<li>', {
| |
− | id: 'ca-safe',
| |
− | html: $('<a>', {
| |
− | href: new Query({
| |
− | title: conf.wgPageName,
| |
− | safemode: true
| |
− | }).print(true),
| |
− | title: 'View the page in safemode.',
| |
− | text: 'Safe'
| |
− | })
| |
− | }),
| |
− | $debug: $('<li>', {
| |
− | id: 'ca-debug',
| |
− | html: $('<a>', {
| |
− | href: new Query({
| |
− | title: conf.wgPageName,
| |
− | debug: true
| |
− | }).print(true),
| |
− | title: 'View page in debug mode.',
| |
− | text: 'Debug'
| |
− | })
| |
− | }),
| |
− | $toggle: $('<li>', {
| |
− | id: 'ca-toggle',
| |
− | html: $('<a>', {
| |
− | href: '#',
| |
− | title: 'Toggle hidden content.',
| |
− | text: 'Toggle'
| |
− | })
| |
− | })
| |
− | };
| |
− |
| |
− | // Change "View history" button label to simply "History".
| |
− | $('#ca-history').find('a').text('History');
| |
− |
| |
− | // Move the search bar and add container for special messages to the user bar:
| |
− | page.$userBar
| |
− | .prepend(items.$searchBar)
| |
− | .append(items.$specialMessages);
| |
− |
| |
− | // Place the buttons for the page inside a container in the user bar:
| |
− | page.$mwHead.append(
| |
− | items.$userBarBottom.append(
| |
− | $('#left-navigation')
| |
− | ).append(
| |
− | $('#right-navigation')
| |
− | )
| |
− | );
| |
− |
| |
− | // Add user contributions button to the top of the page:
| |
− | page.$leftNav.append(
| |
− | $('#t-contributions').attr('id', 'ca-contribs')
| |
− | .find('a')
| |
− | .wrap('<span>')
| |
− | .text('Contributions')
| |
− | .end()
| |
− | );
| |
− |
| |
− | // Extract the buttons from the right "More" menu and place them next to the other ones:
| |
− | page.$rightMenu.children().each(function() {
| |
− | const $this = $(this);
| |
− | page.$rightNav.append(
| |
− | $this.addClass('collapsible').html(function() {
| |
− | return $('<span>', {
| |
− | html: $this.html()
| |
− | });
| |
− | })
| |
− | );
| |
− | });
| |
− |
| |
− | // Add null edit button (only when reading the page):
| |
− | if($('#ca-view.selected').length) {
| |
− | page.$rightNav.append(items.$nullEdit);
| |
− | }
| |
− |
| |
− | // Add button to display raw content and last diff (only if the page has content):
| |
− | if(page.hasContent) {
| |
− | page.$rightMenu.append(items.$raw);
| |
− | $('#ca-history').after(items.$diff);
| |
− | }
| |
− |
| |
− | // Add a safemode button and a toggel button to the right "More" menu:
| |
− | page.$rightMenu
| |
− | .append(items.$safe)
| |
− | .append(items.$debug)
| |
− | .append(items.$toggle);
| |
− |
| |
− | // Place the (un)watch star in the last spot.
| |
− | page.$rightNav.append(page.$star);
| |
− |
| |
− | /** End. */
| |
− |
| |
− |
| |
− | /**
| |
− | * Procedures.
| |
− | */
| |
− |
| |
− | // Adapt the top buttons to the diff pages: ("prettyfy")
| |
− | $(function selectDiffButton() {
| |
− | if(Query.fetch('diff')) {
| |
− | $('#ca-diff').addClass('selected');
| |
− | $('#ca-view').removeClass('selected');
| |
− | }
| |
− | });
| |
− |
| |
− | // Get lastest diff link:
| |
− | $(function getLatestDiffLink() {
| |
− | const mainFunctionName = 'getLatestDiffLink'; //arguments.callee.name;
| |
− | const query = {
| |
− | action: 'query',
| |
− | prop: 'revisions',
| |
− | titles: conf.wgPageName,
| |
− | rvprop: 'ids|user|timestamp|size|parsedcomment|flags',
| |
− | rvlimit: 2,
| |
− | formatversion: 2,
| |
− | format: 'json'
| |
− | };
| |
− | const callbacks = {
| |
− | done: function(data) {
| |
− | try {
| |
− | const currentRevision = data.query.pages[0].revisions[0];
| |
− | items.$diff.off('click')
| |
− | .find('a')
| |
− | .attr('href', new Query({
| |
− | diff: currentRevision.revid
| |
− | }).print());
| |
− | } catch(e) {
| |
− | console.log('%c«' + mainFunctionName + '()» error!', 'color: red;');
| |
− | if(e instanceof TypeError) {
| |
− | console.log("%cCan't get diff!", 'color: red;', '(known)');
| |
− | } else {
| |
− | console.error(e);
| |
− | }
| |
− | console.log(new Query('api', query).print(), data);
| |
− | return;
| |
− | }
| |
− | },
| |
− | fail: console.log,
| |
− | always: function() {
| |
− | console.log('%cDiff getter loaded!', 'color: blue;');
| |
− | }
| |
− | };
| |
− | return conf.api(query, callbacks);
| |
− | });
| |
− |
| |
− | // Toggle stuff I've hidden:
| |
− | $(function toggleWhatIHaveHidden() {
| |
− | [ // Add toggle class to all selectors:
| |
− | '#siteNotice', '#editpage-copywarn',
| |
− | '.mw-editTools', '.smw-editpage-help',
| |
− | '#n-Twitter', '#n-Facebook', '#p-random'
| |
− | ].forEach(function(selector) {
| |
− | $(selector).addClass('toggle');
| |
− | });
| |
− | });
| |
− |
| |
− | // Automatically fill Special:Upload page for card images:
| |
− | $(function makeUploadingCardImagesEasier() {
| |
− |
| |
− | // Get the card name on the galleries and place it on upload links:
| |
− | (function getName(MutationObserver, encode) {
| |
− |
| |
− | // Only for "Card Gallery" and "Set Card Galleries" namespaces:
| |
− | if(![3004, 3024].includes(conf.wgNamespaceNumber)) {
| |
− | return;
| |
− | }
| |
− |
| |
− | // Function to guess the card name
| |
− | // and append it to the empty galleries' links:
| |
− | const guessCardName = function() {
| |
− | $('.gallerybox').each(function() {
| |
− | const $this = $(this);
| |
− | const cardName = (function() {
| |
− | var brFlag = false;
| |
− | if(conf.wgNamespaceNumber === 3004) {
| |
− | // Card galleries:
| |
− | const $navBoxHeader = $('.navbox-title').children('div');
| |
− | return $navBoxHeader.text() + $navBoxHeader.find('a').attr('title').replace(/.+?( \(.*\))?/g, function(_, $1) {
| |
− | return $1 || '';
| |
− | });
| |
− | } else {
| |
− | // Set galleries:
| |
− | return $this.find('.gallerytext').find('p').contents().filter(function() {
| |
− | const isBR = this.nodeName === 'BR';
| |
− | brFlag = isBR ? !brFlag : brFlag;
| |
− | return brFlag && !isBR;
| |
− | }).filter('a').first().text();
| |
− | }
| |
− | })();
| |
− |
| |
− | // Append the name to the url, in the form of «_name=cardName»:
| |
− | $this.find('.noFile').attr('href', function(_, href) {
| |
− | return [
| |
− | href,
| |
− | [
| |
− | '_name',
| |
− | encode(cardName)
| |
− | ].join('=')
| |
− | ].join('&');
| |
− | });
| |
− | });
| |
− | };
| |
− |
| |
− | // Execute guessing the card name:
| |
− | guessCardName();
| |
− |
| |
− | // Check if there were changes in the DOM
| |
− | // to keep the links with the card name in there (when previewing edits, etc.):
| |
− | if(MutationObserver && ['edit', 'submit'].includes(conf.action)) {
| |
− |
| |
− | // Wait for the editor to load:
| |
− | mw.loader.using('jquery.wikiEditor').then(function() {
| |
− |
| |
− | // (jQuery) Node to observe:
| |
− | const $targetNode = $('.wikiEditor-ui-view-preview').find('.wikiEditor-preview-contents');
| |
− |
| |
− | // Options for the observer (which mutations to observe).
| |
− | // We only want to see when content is loaded from the backend
| |
− | // and then appended.
| |
− | // Therefore, observe when new child nodes are placed or removed:
| |
− | const options = {
| |
− | childList: true
| |
− | };
| |
− |
| |
− | // Create an observer instance and observe:
| |
− | new MutationObserver(function(mutationsList) {
| |
− | // No need to iterate over mutationList,
| |
− | // since we just want to execute the following function once.
| |
− | delay(function() {
| |
− | guessCardName();
| |
− | });
| |
− | }).observe($targetNode[0], options);
| |
− |
| |
− | });
| |
− |
| |
− | }
| |
− |
| |
− | })(window.MutationObserver, window.encodeURIComponent);
| |
− |
| |
− | // Populate Special:Upload descrption with the name from the url:
| |
− | (function populate(name) {
| |
− |
| |
− | // Fill in the upload description box at Special:Upload:
| |
− | $('#wpUploadDescription').val(
| |
− | name ? '{{OCG-TCG card image\n| name = ' + name + '\n}}' : null
| |
− | );
| |
− |
| |
− | })(Query.fetch('_name'));
| |
− |
| |
− | });
| |
− |
| |
− | /** End. */
| |
− |
| |
− |
| |
− | /**
| |
− | * Events.
| |
− | */
| |
− |
| |
− | // Diff:
| |
− | items.$diff.click(function(e) {
| |
− | // Prevent default event:
| |
− | e.preventDefault();
| |
− | // Display state as failed: (if successfull, the Api request done() will "off" this event.)
| |
− | new State('Failed to get diff!').failed().show();
| |
− | });
| |
− |
| |
− | // Null edit:
| |
− | items.$nullEdit.click(function(e) {
| |
− | // Prevent default event:
| |
− | e.preventDefault();
| |
− | // Request state:
| |
− | const state = new State('Null editing...').show();
| |
− | // Request:
| |
− | const query = {
| |
− | action: 'edit',
| |
− | title: conf.wgPageName,
| |
− | token: mw.user.tokens.get('editToken'),
| |
− | summary: 'Something bad happened!',
| |
− | prependtext: ''
| |
− | };
| |
− | const callbacks = {
| |
− | done: function() {
| |
− | state.done().setMessage('Null edited!');
| |
− | page.$content.load(window.location.href + ' #content > *');
| |
− | },
| |
− | fail: function(message, errorObject) {
| |
− | console.log('%cNull edit error!', 'color: red;', '(message, errorObject)');
| |
− | console.log(message, errorObject);
| |
− | state.failed().setMessage('Null edit failed! (' + (message === 'http'
| |
− | ? errorObject.textStatus
| |
− | : message
| |
− | ) + ')');
| |
− | },
| |
− | always: function() {
| |
− | state.show();
| |
− | }
| |
− | };
| |
− | const method = 'post';
| |
− | return conf.api(query, callbacks, method);
| |
− | });
| |
− |
| |
− | // Toggle:
| |
− | items.$toggle.click(function(e) {
| |
− | e.preventDefault();
| |
− | $('.toggle').toggle();
| |
− | });
| |
− |
| |
− | /** End. */
| |
− |
| |
− |
| |
| /** | | /** |
| * Return. | | * Return. |