Difference between revisions of "User:Corey/vector.js"

From Yugipedia
Jump to: navigation, search
(Created page with "* * Main JS function.: (function(window, $, mw, console, delay) { "use strict"; * * Global objects.: const LAST_LOG = '14:22, 8 March 2018 (UTC)'; const c...")
 
 
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.

Latest revision as of 17:15, 10 April 2018

/**
 * Main JS function.
 */
(function(window, $, mw, console, delay) {
	"use strict";

	/**
	 * Global objects.
	 */

	const LAST_LOG = '14:22, 8 March 2018 (UTC)';
	
	/**
	 * Return.
	 */

	console.log(
		'%cUser Vector.js loaded! Last version from ' + LAST_LOG + '.',
		'color: blue;'
	);

	return true;

	/** End. */

})(window, window.jQuery, window.mediaWiki, window.console, (function(window, $) {
	"use strict";
	return function(t, fn) {
		if(!fn && $.isFunction(t)) {
			fn = t;
			t = undefined;
		}
		return window.setTimeout(fn, t);
	};
})(window, window.jQuery));
/** End. */