User:BrandonXLF/TodoList.js

/*** Todo List ***/

// Adds a todo list that also has a convient popup // Documentation at en:w:User:BrandonXLF/TodoList // By en:w:User:BrandonXLF

$.when(mw.loader.using([ 'mediawiki.user', 'oojs-ui-core', 'oojs-ui-windows', 'oojs-ui.styles.icons-movement', 'oojs-ui.styles.icons-editing-core', 'oojs-ui.styles.icons-interactions', 'oojs-ui.styles.icons-moderation', 'oojs-ui.styles.icons-content' ]), $.ready).then(function {	var messages = function {		var translations = {				en: {					todoPortlet: 'Todo',					todoHover: 'Click to see your todo list',					saving: 'Saving changes...',					drag: 'Drag',					delete: 'Delete',					edit: 'Edit',					pageNameUrl: 'Page name or URL',					comment: 'Comment',					save: 'Save',					cancel: 'Cancel',					moreinfo: 'More information',					undo: 'Undo',					redo: 'Redo',					clear: 'Clear',					clearConfirm: 'Are you sure you want to clear your todo list?',					download: 'Download',					upload: 'Upload',					help: 'Help',					add: 'Add',					close: 'Close',					yourList: 'Your todo list',					noPopup: 'Unable to open todo list popup while on the todo list page.',					addedTime: 'Added: $dateDate $dateMonth, $dateYear at $dateHours:$dateMinutes'				},				nb: {					todoPortlet: 'Huskeliste',					todoHover: 'Klikk for å se huskelista di',					saving: 'Lagrer endringer …', drag: 'Dra', delete: 'Slett', edit: 'Rediger', pageNameUrl: 'Sidenavn eller URL', comment: 'Merknad', save: 'Lagre', cancel: 'Avbryt', moreinfo: 'Mer informasjon', undo: 'Angre', redo: 'Angre angring', clear: 'Tøm', clearConfirm: 'Er du sikker på at du vil tømme huskelista di?', download: 'Last ned', upload: 'Last opp', help: 'Hjelp', add: 'Legg til', close: 'Lukk', yourList: 'Huskelista di', noPopup: 'Kan ikke åpne oppsprettsboksen med huskelista mens du er på huskelistesiden.', addedTime: 'Lagt til: $dateDate. $dateMonth $dateYear kl. $dateHours.$dateMinutes' },				nn: { todoPortlet: 'Hugseliste', todoHover: 'Klikk for å sjå hugselista di', saving: 'Lagrar endringar …', drag: 'Dra', delete: 'Slett', edit: 'Endre', pageNameUrl: 'Sidenamn eller URL', comment: 'Merknad', save: 'Lagre', cancel: 'Avbryt', moreinfo: 'Meir informasjon', undo: 'Angre', redo: 'Angre angring', clear: 'Tøm', clearConfirm: 'Er du sikker på at du vil tømme hugselista di?', download: 'Last ned', upload: 'Last opp', help: 'Hjelp', add: 'Legg til', close: 'Lukk', yourList: 'Hugselista di', noPopup: 'Kan ikkje opna oppsprettsboksen med hugselista mens du er på hugselistesida.', addedTime: 'Lagt til: $dateDate. $dateMonth $dateYear kl. $dateHours.$dateMinutes' },				ka: { todoPortlet: 'გასაკეთებელი', todoHover: 'დააწკაპეთ თქვენი გასაკეთებლების სიის სანახავად', saving: 'ინახება…', drag: 'გადაადგილება', delete: 'წაშლა', edit: 'რედაქტირება', pageNameUrl: 'გვერდის სახელი ან ბმული', comment: 'კომენტარი', save: 'შენახვა', cancel: 'გაუქმება', moreinfo: 'მეტი ინფორმაცია', undo: 'უკან დაბრუნება', redo: 'წინ დაბრუნება', clear: 'გასუფთავება', clearConfirm: 'დარწმუნებული ხართ, რომ თქვენი სიის გასუფთავება გსურთ?', download: 'ჩამოტვირთვა', upload: 'ატვირთვა', help: 'დახმარება', add: 'დამატება', close: 'დახურვა', yourList: 'თქვენი გასაკეთებლების სია', noPopup: 'გასაკეთებლების სიის გვერდზე ყოფნის დროს შეუძლებელია სიის ფანჯრის გახსნა.', addedTime: 'დამატების თარიღი: $dateDate $dateMonth, $dateYear; $dateHours:$dateMinutes' }			},			chain = mw.language.getFallbackLanguageChain, len = chain.length, ret = {}, i = len - 1;

while (i >= 0) { if (translations.hasOwnProperty(chain[ i ])) { $.extend(ret, translations[ chain[ i ] ]); }

i = i - 1; }

return ret; };

mw.util.addCSS(		'.userjs-todo-list .item:hover .act { visibility: visible !important; }' +		'[data-drop="above"]::before { display: block; border-top: 2px solid #066; margin-top: -2px; content: ""; }' +		'[data-drop="below"]::after { display: block; border-top: 2px solid #066; margin-top: -2px; content: ""; }' +		'.userjs-todo-list .items:empty { border: none !important; margin: 0 !important; padding: 0 !important;}' +		'.userjs-todo-moreinfo > a { min-width: unset !important; min-height: unset !important; padding: 0.65em !important; }' +		'.userjs-todo-moreinfo > a > span { min-width: unset !important; min-height: unset !important; width: 1.3em !important; left: 0 !important; }'	);

if (mw.config.get('skin') === 'minerva') { mw.util.addCSS(			'.userjs-todo-list .item .act { visibility: visible !important; }' +			'#userjs-todo-popup { top: 0 !important; bottom: 0 !important; left: 0 !important; right: 0 !important; width: 100%; height: 100%; }' +			'#userjs-todo-popup .userjs-todo-list { width: 100%; height: 100%; } '		); }

var api = new mw.Api, links = $(			['', '-sticky-header']				.map(function(suffix) { return mw.util.addPortletLink(						'p-personal' + suffix,						mw.util.getUrl('Special:BlankPage/todo'),						messages.todoPortlet,						'userjs-todo',						messages.todoHover,						'd',						'#pt-preferences' + suffix					); })				.filter(function(elOrNull) { return elOrNull !== null; })		),		changes = [mw.user.options.get('userjs-todo-script')], undo = 0, req = 0, parent, ispopup = false, loader = $('').on('change', function {			if (!this.files || !this.files[0]) return;

var file = this.files[0], reader = new FileReader;

reader.onload = function { save(reader.result, true, true); };

reader.readAsText(file); }).appendTo(document.body);

$(window).on('beforeunload', function {		if (req > 0) return true;	});

function save(what, record, relist) { if (typeof what != 'string') { what = JSON.stringify(what); }

mw.user.options.set('userjs-todo-script', what); req++; parent.find('.status').text(messages.saving);

api.saveOption('userjs-todo-script', what).done(function {			req--;

if (req === 0) { parent.find('.status').text(''); }		});

if (record !== false) { while (undo > 0) { changes.pop; undo--; }

changes.push(mw.user.options.get('userjs-todo-script'));

if (changes.length > 50) changes.shift; }

if (relist) { list;

if (req > 0) { parent.find('.status').text(messages.saving); }		}	}

Math.clamp = function(max, x, min) { return Math.min(max, Math.max(min, x)); };

function item(parent, array) { var date = new Date(+array[2]), url = array[0] || '', txt = array[0] || '';

if (url.match(/^(https:|http:|:)\/\//) && url.match(/ /)) { var reg = /(.*?) (.*)/.exec(url);

url = reg[1]; txt = reg[2]; } else if (!url.match(/^(https:|http:|:)\/\//)) { url = mw.util.getUrl(url); }

parent.append($(' ')			.attr('data-page', array[0] || )			.attr('data-info', array[1] || )			.attr('data-date', array[2] || '')			.append((new OO.ui.IconWidget({icon: 'draggable', title: messages.drag})).$element .css({					cursor: 'move',					height: '1.2em',					width: '1.2em',					minWidth: 'unset',					minHeight: 'unset',					marginRight: '0.5em'				}) .on('mousedown', function {					$(this).parent.attr('draggable', 'true');				}) .on('mouseup', function {					$(this).parent.attr('draggable', 'false');				}) )			.append($('').addClass('page').attr('href', url).text(txt))			.append(array[0] && array[1] ? ' . . ' : )			.append($(' ').addClass('info').text(array[1] || ))			.append( array[0] || array[1] ? ' . . '					: '')			.append((new OO.ui.IconWidget({ icon: 'trash', title: messages.delete, flags: ['destructive'] })).$element .css({					cursor: 'pointer',					visibility: 'hidden',					height: '1.1em',					width: '1.1em',					minWidth: 'unset',					minHeight: 'unset',					marginRight: '0.5em'				}) .addClass('act') .click(function {					var arr = [];

$(this).parent.siblings.each(function {						arr.push([ $(this).attr('data-page'), $(this).attr('data-info'), $(this).attr('data-date') ]);					});

$(this).parent.remove;

save(arr); })			)			.append((new OO.ui.IconWidget({				icon: 'edit',				title: messages.edit,				flags: ['progressive']			})).$element				.css({ cursor: 'pointer', visibility: 'hidden', height: '1em', width: '1em', minWidth: 'unset', minHeight: 'unset', marginRight: '0.5em' })				.addClass('act edit')				.click(function { $(this).css('display', 'none'); $(' ')						.append((new OO.ui.TextInputWidget({							placeholder: messages.pageNameUrl,							classes: ['todo-pageNameUrl'],							value: $(this).parent.attr('data-page')						})).$element.css({maxWidth: 'unset', margin: '4px 0'})) .append((new OO.ui.TextInputWidget({							placeholder: messages.comment,							classes: ['todo-comment'],							value: $(this).parent.attr('data-info')						})).$element.css({maxWidth: 'unset', margin: '4px 0'})) .append($(' ').append($(' ') .append($(' ').append((new OO.ui.ButtonWidget({ label: messages.save, flags: ['progressive'] }).$element.css('width', '100%').children.css('width', '100%').parent).click(function {								var editbox = $(this).parent.parent.parent.parent,									listitem = editbox.parent;

listitem.find('.page').text(editbox.find('.todo-pageNameUrl input').val); listitem.attr('data-page', editbox.find('.todo-pageNameUrl input').val); listitem.find('.info').text(editbox.find('.todo-comment input').val);

if (listitem.find('.info')[0].previousSibling.nodeType != 3 && editbox.find('.todo-comment input').val !== '') { listitem.find('.info').before(' . . '); } else if (									listitem.find('.info')[0].previousSibling.nodeType == 3 &&									editbox.find('.todo-comment input').val === ''								) { $(listitem.find('.info')[0].previousSibling).remove; }

listitem.attr('data-info', editbox.find('.todo-comment input').val);

var arr = [];

listitem.parent.children.each(function {									arr.push([$(this).attr('data-page'), $(this).attr('data-info'), $(this).attr('data-date')]);								});

save(arr);

listitem.find('.edit').css('display', 'inline-block'); editbox.remove; })							))							.append($(' ').append((new OO.ui.ButtonWidget({								label: messages.cancel,								flags: ['destructive']							}).$element.css('width', '100%').children.css('width', '100%').parent).click(function { var editbox = $(this).parent.parent.parent.parent, listitem = editbox.parent;

listitem.find('.edit').css('display', 'inline-block'); editbox.remove; })							))						)).appendTo(this.parentNode);				}) )			.append(array[2] ? (new OO.ui.PopupButtonWidget({ icon: 'info', framed: false, label: messages.moreinfo, invisibleLabel: true, title: messages.moreinfo, popup: { $content: $(' ').text(messages.addedTime						.replace('$dateDate', date.getDate)						.replace('$dateMonth', mw.config.get('wgMonthNames')[date.getMonth + 1])						.replace('$dateYear', date.getFullYear)						.replace('$dateHours', date.getHours)						.replace('$dateMinutes', ('' + date.getMinutes).padStart(2, '0'))					), padded: true, align: 'forwards' }			})).$element .addClass('userjs-todo-moreinfo') .css({					cursor: 'pointer',					visibility: 'hidden',					height: '1.3em',					width: '1.3em',					minWidth: 'unset',					minHeight: 'unset',					marginRight: '0.5em'				}) .addClass('act') : '' )			.on('dragover', function(e) { e.preventDefault; e.dataTransfer = e.originalEvent.dataTransfer; if (e.offsetY < ($(this).height/2)) { this.setAttribute('data-drop', 'above'); } else { this.setAttribute('data-drop', 'below'); }			})			.on('dragleave', function { this.setAttribute('data-drop', ''); })			.on('drop', function(e) { e.dataTransfer = e.originalEvent.dataTransfer; this.setAttribute('data-drop', '');

var ele = $('[data-dragid="' + e.dataTransfer.getData('text/plain') + '"]')[0];

if (e.offsetY < ($(this).height/2)) { this.insertAdjacentElement('beforebegin', ele); } else { this.insertAdjacentElement('afterend', ele); }

var arr = [];

$(this).parent.children.each(function {					arr.push([ $(this).attr('data-page'), $(this).attr('data-info'), $(this).attr('data-date') || '' ]);				});

save(arr); })			.on('dragstart', function(e) { e.dataTransfer = e.originalEvent.dataTransfer;

var uid = ( + Math.random).replace('.', );

$(this).attr('data-dragid', uid); e.dataTransfer.setData('text/plain', uid); })		);	}

function list { $('.userjs-todo-list').remove; var todo = parent.append($(' ')			.append($(' ') .append(new OO.ui.ButtonWidget({ framed: false, icon: 'undo', invisibleLabel: true, title: messages.undo }).$element.click(function { if (changes[changes.length-undo-2] !== undefined) { save(changes[changes.length-undo-2], false, true); undo++; }				}))				.append(new OO.ui.ButtonWidget({ framed: false, icon: 'redo', invisibleLabel: true, title: messages.redo }).$element.click(function { if (changes[changes.length-undo] !== undefined) { save(changes[changes.length-undo], false, true); undo--; }				}))				.append(new OO.ui.ButtonWidget({ framed: false, icon: 'trash', invisibleLabel: true, title: messages.clear }).$element.click(function { OO.ui.confirm(messages.clearConfirm).done(function(confirmed) {						if (confirmed) {							parent.find('.items').empty;							save('', true, true);						}					}); }))				.append(new OO.ui.ButtonWidget({ framed: false, icon: 'download', invisibleLabel: true, title: messages.download }).$element.click(function { $($('')						.attr( 'href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(mw.user.options.get('userjs-todo-script')) )						.appendTo(document.body)[0].click					).remove; }))				.append(new OO.ui.ButtonWidget({ framed: false, icon: 'upload', invisibleLabel: true, title: messages.upload }).$element.click(function { loader.click; }))				.append(new OO.ui.ButtonWidget({ framed: false, icon: 'info', invisibleLabel: true, title: messages.help }).$element.click(function { window.open('https://en.wikipedia.org/wiki/User:BrandonXLF/TodoList/Help'); }))			)			.append($(' ') .append((new OO.ui.TextInputWidget({					placeholder: messages.pageNameUrl,					classes: ['todo-pageNameUrl'],					value: mw.config.get('wgPageName').replace(/_/g, ' ') != 'Special:BlankPage/todo'						? mw.config.get('wgPageName').replace(/_/g, ' ')						: ''				})).$element.css({maxWidth: 'unset', margin: '4px 0'})) .append((new OO.ui.TextInputWidget({					placeholder: messages.comment,					classes: ['todo-comment']				})).$element.css({maxWidth: 'unset', margin: '4px 0'})) .append(new OO.ui.ButtonWidget({ label: messages.add }).$element					.css('width', '100%')					.children.css('width', '100%')					.parent					.click(function { var opts = JSON.parse(mw.user.options.get('userjs-todo-script') || '[]'), arr = [];

arr.push($(this).parent.parent.find('.todo-pageNameUrl input').val); arr.push($(this).parent.parent.find('.todo-comment input').val);

if (!arr.join('')) return;

arr.push((new Date).getTime); item($('.userjs-todo-list .items'), arr); opts.push(arr); save(opts);

$('#userjs-todo-popup').css({							top: Math.max(parseInt($('#userjs-todo-popup').css('top')), 0)						}); }))			)			.append($(' '))			.append(' ')		); if (ispopup) { todo.find('.todo-menu') .css('cursor', 'grab') .append((new OO.ui.ButtonWidget({					framed: false,					icon: 'close',					invisibleLabel: true,					title: messages.close				}).$element.css({float: 'right', marginRight: '0'}).click(function {					parent.remove;				}))) .append(($('') .attr('href', mw.util.getUrl('Special:BlankPage/todo')) .append((new OO.ui.ButtonWidget({						framed: false,						icon: 'newWindow',						invisibleLabel: true,						title: messages.yourList					}).$element.css('float', 'right').click(function {						location.url = mw.util.getUrl('Special:BlankPage/todo');					}))) ))				.on('mousedown', function(e) {					if (e.target !== this) return;

this.style.cursor = 'grabbing';

var x = parent.position.left - e.pageX, y = parent.position.top - e.pageY, b = $(document.body);

function move(e) { parent.css({							left: Math.clamp($(window).width - parent.width, e.pageX + x, 0) + 'px',							top: Math.clamp($(window).height - parent.height, e.pageY +	y, 0) + 'px',							right: ,							bottom: 						}); }

function up { e.target.style.cursor = 'grab'; b.off('mousemove', move); b.off('mouseup', up); }

b.on('mousemove', move); b.on('mouseup', up); });		}

$.each(JSON.parse(mw.user.options.get('userjs-todo-script') || '[]'), function(a, b) {			item(todo.find('.items'), b);		});

return todo; }

function repos(pos, css, pvar, cond, pcss, ccss) { return $(' ') .attr('id', pos + 'gfdgfdgfd') .css('cursor', pos + '-resize') .css(css).on('mousedown', function(e) {				if (e.target !== this) return;

var p = parent, c = p.find('.userjs-todo-list'), q = eval(pvar), b = $(document.body);

b.css('user-select', 'none');

function move(e) { if (eval(cond)) return; (new Function('e', 'p', 'q', 'p.css(' + pcss + ')'))(e, p, q); (new Function('e', 'c', 'q', 'c.css(' + ccss + ')'))(e, c, q); }

function up { b.css('user-select', ''); b.off('mousemove', move); b.off('mouseup', up); }

b.on('mousemove', move); b.on('mouseup', up); });	}

if ((mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage') && (mw.config.get('wgPageName').split('/').pop === 'todo')) { document.title = messages.yourList + ' – ' + mw.config.get('wgSiteName');

$('#firstHeading').text(messages.yourList); parent = mw.util.$content.empty;

list;

links.find('a').click(function(e) {			e.preventDefault;			mw.notify(messages.noPopup, {tag: 'rtbyilounmt7udfnod'});		}); } else { links.find('a').click(function(e) {			e.preventDefault;

var link = $(e.target).closest(links);

if ($('#userjs-todo-popup')[0]) { $('#userjs-todo-popup').remove; return; }

ispopup = true;

parent = $(' ') .appendTo(document.body) .css('top', link.position.top + link.height + 8 + 'px') .append($(' ')					.append(repos('e', {top: 0, right: 0, width: '7px', height: '100%'}, 'p.position.left', 'e.clientX - q < 40', '{right:"",minWidth:"",left:q	+ "px"}', '{width:e.clientX - q + "px"}'))					.append(repos('n', {top: 0, height: '7px', width: '100%'}, 'p.position.top + p.height', 'q <= e.clientY + 40', '{top:Math.max(e.clientY, 0) + "px"}', '{height:q - e.clientY + "px"}'))					.append(repos('s', {bottom: 0, height: '7px', width: '100%'}, 'p.position.top', 'e.clientY - q < 40', '{top:Math.max(q,0) + "px"}', '{height:e.clientY - q + "px"}'))					.append(repos('w', {left: 0, width: '7px', height: '100%'}, 'p.position.left + parent.width', 'q <= e.clientX + 40', '{right:"",minWidth:"",left:e.clientX + "px"}', '{width:q - e.clientX + "px"}'))					.append($(' ').on('mousedown', function(e) { e.originalEvent.stopImmediatePropagation; $('#egfdgfdgfd').trigger('mousedown'); $('#ngfdgfdgfd').trigger('mousedown'); }))					.append($(' ').on('mousedown', function(e) { e.originalEvent.stopImmediatePropagation; $('#wgfdgfdgfd').trigger('mousedown'); $('#ngfdgfdgfd').trigger('mousedown'); }))					.append($(' ').on('mousedown', function(e) { e.originalEvent.stopImmediatePropagation; $('#egfdgfdgfd').trigger('mousedown'); $('#sgfdgfdgfd').trigger('mousedown'); }))					.append($(' ').on('mousedown', function(e) { e.originalEvent.stopImmediatePropagation; $('#wgfdgfdgfd').trigger('mousedown'); $('#sgfdgfdgfd').trigger('mousedown'); }))				);

parent.css('top', Math.max(parseInt(parent.css('top')), 0)); list; });	} });