User:The Evil IP address/liedit.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/**
* Allows for one click modification of list items when viewing a page
* add importScript('User:The Evil IP address/liedit.js'); to your .js file to use it
* @author Janko Hoener (The Evil IP address)
* @version 0.9.9 (beta)
*/
mw.loader.load('mediawiki.api');
window.liedit = {
	REGEX_WIKI: /^\s*(\*+)[^\*]*/,
	REGEX_HTML: /^\s*<li>.+(<\/li>)?\s*$/,
	SIBLING: -1,
	PARENT: 1,
	EDIT_SUM_LIMIT: 170,
	
	arraysEqual: function(a, b) {
		// stolen from StackOverflow
		if (a === b) return true;
		if (a === null || b === null) return false;
		if (a.length != b.length) return false;
		
		// If you don't care about the order of the elements inside
		// the array, you should sort both arrays here.
		
		for (var i = 0; i < a.length; ++i) {
			if (a[i] !== b[i]) return false;
		}
		return true;
	},
	
	onclick: function (event, $e) {
		if ($e.is('table.infobox li')) {
			liedit.set_error_msg('Sorry, cannot edit list items in infoboxes using this script.');
			return;
		}
		if ($e.is('#toc li') || $e.has('form').length) {
			return;
		}
		if (liedit.$e) {
			liedit.cancel(event);
		}
		liedit.pagename = mw.config.get('wgPageName');
		liedit.api = new mw.Api();
		liedit.$e = $e;
		liedit.api.get( {
				action: 'parse',
				page: liedit.pagename,
				prop: 'sections',
				format: 'json'
		}).done( function (data) {
			liedit.sections = {};
			$.each(data.parse.sections, function (i, v) {
					if (v.byteoffset) {
						liedit.sections[v.anchor] = v.index;
					}
					
			}
	        );
	        cur_elem = liedit.$e[0];
	        liedit.index = 0;
	        abort = false;
	        last_move = liedit.SIBLING;
	        liedit.position = [0, 0];
	        for (i = 0; i < 10000 && !abort; i++) {
	        	$cur_elem = $(cur_elem);
	        	switch (cur_elem.localName) {
				case 'li':
					if (last_move == liedit.SIBLING) {
						liedit.position[1]++;
					}
					if (last_move == liedit.PARENT) {
						liedit.position.unshift(0);
					}
					break;
				case 'ul':
					liedit.position[0]++;
					break;
				case 'dl':
				case 'dd':
					if (last_move == liedit.PARENT) {
						liedit.set_error_msg('Cannot edit this list item using this script.');
						return;
					}
					break;
				case 'h1':
				case 'h2':
				case 'h3':
				case 'h4':
				case 'h5':
				case 'h6':
					if ($cur_elem.attr('class') == 'firstHeading') {
						liedit.index = 0;
						abort = true;
						break;
					}
					liedit.anchor = $cur_elem.find('span.mw-headline').first().attr('id');
					if (liedit.anchor in liedit.sections) {
						liedit.index = liedit.sections[liedit.anchor];
						abort = true;
					}
					break;
				}
				if (cur_elem.previousElementSibling) {
					cur_elem = cur_elem.previousElementSibling;
					last_move = liedit.SIBLING;
				}
				else {
					cur_elem = cur_elem.parentNode;
					last_move = liedit.PARENT;
				}
			}
	        liedit.api.get( {
	        		action: 'parse',
	        		page: liedit.pagename,
	        		section: liedit.index,
	        		prop: 'wikitext',
	        		format: 'json'
	        }).done( function (obj) {
	        	liedit.wikitext = obj.parse.wikitext['*'];
	        	liedit.wikitext = liedit.wikitext.replace(/([^\n])(<li>)/mgi, "$1\n$2");
	        	liedit.lines = liedit.wikitext.split('\n');
	        	currently_in_list = false;
	        	current_list_wiki = false;
	        	current_list_html = false;
	        	cur_pos = [];
	        	depth = liedit.position.length-1;
	        	for (i = 0; i < liedit.position.length; i++) {
	        		cur_pos.push(0);
	        	}
	        	for (i = 0; i < liedit.lines.length; i++) {
	        		line = liedit.lines[i];
	        		line_match_wiki = line.match(liedit.REGEX_WIKI);
	        		line_match_html = line.match(liedit.REGEX_HTML);
	        		if (line_match_html || line_match_wiki) {
	        			if (!currently_in_list) {
	        				currently_in_list = true;
	        				if (line_match_wiki) {
	        					current_list_wiki = true;
	        					current_list_html = false;
	        				}
	        				else if (line_match_html) {
	        					current_list_wiki = false;
	        					current_list_html = true;
	        				}
	        				cur_pos[0]++;
	        				cur_pos[1] = 1;
	        				for (j = 2; j < cur_pos.length; j++) {
	        					cur_pos[j] = 0;
	        				}
	        			}
	        			else if (line_match_wiki && currently_in_list && current_list_html) {
	        				cur_pos[0]++;
	        				cur_pos[1] = 1;
	        				for (j = 2; j < cur_pos.length; j++) {
	        					cur_pos[j] = 0;
	        				}
	        				current_list_wiki = true;
	        				current_list_html = false;
	        			}
	        			else if (line_match_html && currently_in_list && current_list_wiki) {
	        				cur_pos[0]++;
	        				cur_pos[1] = 1;
	        				for (j = 2; j < cur_pos.length; j++) {
	        					cur_pos[j] = 0;
	        				}
	        				current_list_wiki = false;
	        				current_list_html = true;
	        			}
	        			else if (currently_in_list && current_list_wiki && line_match_wiki) {
	        				line_depth = line.replace(liedit.REGEX_WIKI, '$1').length;
	        				if (line_depth > depth) {
	        					continue;
	        				}
	        				for (j = 1; j < line_depth; j++) {
	        					if (cur_pos[j] === 0) {
	        						cur_pos[j] = 1;
	        					}
	        				}
	        				cur_pos[line_depth]++;
	        				for (j = line_depth + 1; j < cur_pos.length; j++) {
	        					cur_pos[j] = 0;
	        				}
	        			}
	        			else if (currently_in_list && current_list_html && line_match_html) {
	        				cur_pos[1]++;
	        			}
	        			liedit.li_index = i;
	        			if (liedit.arraysEqual(liedit.position, cur_pos)) {
	        				liedit.li_wikitext = line;
	        				liedit.inputsize = liedit.li_wikitext.length*1.5;
	        				var form = $('<form>').css('display', 'inline').submit(liedit.save);
	        				var input = $('<input>').attr('id', 'liedit_input').attr('size', liedit.inputsize).val(liedit.li_wikitext);
	        				var button1 = $('<button>').attr('id', 'liedit_submit').attr('type', 'submit').text('Save');
	        				var button2 = $('<button>').attr('type', 'button').attr('id', 'liedit_cancel').text('Cancel').click(liedit.cancel);
	        				$(form).append(input).append(button1).append(button2);
	        				liedit.form = form;
	        				$e.append(form);
	        				return false;
	        			}
	        		}
	        		else if (currently_in_list) {
	        			currently_in_list = false;
	        		}
	        		
	        	}
	        	liedit.set_error_msg('Sorry, this script could not identify the list item in the wikitext.')
	        	liedit.li_wikitext = null;
	        	liedit.li_index = null;
	        	return false;
	        }
	        );
	    }
	    );
	    
	},
	
	save: function () {
		liedit.newwikitext = $(this).parent().find('input').val();
		if (liedit.newwikitext == liedit.li_wikitext) return false;
		if (liedit.newwikitext == '') {
			liedit.lines.splice(liedit.li_index, 1);
			liedit.line_removed = true;
		}
		else {
			liedit.lines[liedit.li_index] = liedit.newwikitext;
			liedit.line_removed = false;
		}
		liedit.wikitext = liedit.lines.join('\n');
		$('#liedit_input, #liedit_submit, #liedit_cancel').attr('disabled', 'disabled');
        liedit.summary = '';
        if (liedit.anchor) {
        	liedit.summary += '/* ' + liedit.anchor + ' */ ';
        }
        if (liedit.line_removed) {
          	liedit.summary += 'List item removal using a [[User:The Evil IP address/liedit|script]] [beta]: ' + liedit.li_wikitext;
        }
        else {
        	liedit.summary += 'List item amendment using a [[User:The Evil IP address/liedit|script]] [beta]';
        	fullsum = liedit.summary + ': ' + liedit.li_wikitext + ' → ' + liedit.newwikitext;
            if (fullsum.length < liedit.EDIT_SUM_LIMIT) {
            	liedit.summary = fullsum;
            }
        }
		liedit.api.edit(liedit.pagename, function ()  {
			return {
				action: 'edit',
				format: 'json',
				minor: true,
				section: liedit.index,
				summary: liedit.summary,
				text: liedit.wikitext,
			}; 
		}).then( function (resp) {
            if (resp.result == 'Success') {
        		window.location.reload();
        	}
        	else if (resp.result == 'Failure') {
        		liedit.set_error_msg('API returned error code ' + data.error.code + ': ' + data.error.info);
        	}
        	else {
        		liedit.set_error_msg('Unknown API error.');
        	}
            });
        return false;
    },
    
    set_error_msg: function (msg) {
    	mw.notify(msg, {title: 'Error!', autoHide: false});
    },
    
    cancel: function (event) {
    	if (liedit.form) {
    		liedit.$e.show();
    		liedit.form.remove();
    		event.stopPropagation();
    	}
    }
};

if (mw.config.get('wgNamespaceNumber') >= 0 && mw.config.get('wgAction') == 'view' && mw.config.get('wgIsProbablyEditable')) {
	$('#mw-content-text ul li').click(function ( event ) {
			liedit.onclick(event, $(this));
			event.stopPropagation();
	}
	);
}