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.
window.checkEditBrackets = function ( ) {
	var editbox = $( '#wpTextbox1' );
	if ( !editbox || editbox.length === 0 ) {
		//TODO: log
		return;
	}
	var editboxtext = editbox.val( );
	if ( typeof editboxtext !== 'string' ) {
		//TODO: log
		return;
	}

	// not to be used in mainspace articles, but should be generally harmless
	editboxtext = editboxtext.replace( /<!--noChkBracket-->.*<!--\/noChkBracket-->/g, '' );

	var warningStr = '';
	var initialCountDone = ( typeof window.aBBParenBalance === 'number' && typeof window.aBBBraceBalance === 'number'
                              && typeof window.aBBSqrbkBalance === 'number' && typeof window.aBBAngleBalance === 'number' );

	var parenBalance = (editboxtext.match(/\(/g)||[]).length - (editboxtext.match(/\)/g)||[]).length;
	if ( !initialCountDone ) {
		window.aBBParenBalance = parenBalance;
	}
	if ( parenBalance !== 0 && ( ( initialCountDone && window.aBBParenBalance !== parenBalance ) || window.aBBWarnInitially ) ) {
		//XXX: Should these spans also have IDs?
		warningStr = '<span class="antibracketbot-paren">(' + window.aBBParenBalance;
		if ( window.aBBParenBalance !== parenBalance ) {
			warningStr += ' &rarr; ' + parenBalance;
		}
		warningStr += ')</span>';
	}

	var braceBalance = (editboxtext.match(/\{/g)||[]).length - (editboxtext.match(/\}/g)||[]).length;
	if ( !initialCountDone ) {
		window.aBBBraceBalance = braceBalance;
	}
	if ( braceBalance !== 0 && ( ( initialCountDone && window.aBBBraceBalance !== braceBalance ) || window.aBBWarnInitially ) ) {
		warningStr += '<br /><span class="antibracketbot-brace">{' + window.aBBBraceBalance;
		if ( window.aBBBraceBalance !== braceBalance ) {
			warningStr += ' &rarr; ' + braceBalance;
		}
		warningStr += '}</span>';
	}

	var sqrbkBalance = (editboxtext.match(/\[/g)||[]).length - (editboxtext.match(/\]/g)||[]).length;
	if ( !initialCountDone ) {
		window.aBBSqrbkBalance = sqrbkBalance;
	}
	if ( sqrbkBalance !== 0 && ( ( initialCountDone && window.aBBSqrbkBalance !== sqrbkBalance ) || window.aBBWarnInitially ) ) {
		warningStr += '<br /><span class="antibracketbot-sqrbk">[' + window.aBBSqrbkBalance;
		if ( window.aBBSqrbkBalance !== sqrbkBalance ) {
			warningStr += ' &rarr; ' + sqrbkBalance;
		}
		warningStr += ']</span>';
	}

	var angleBalance = (editboxtext.match(/</g)||[]).length - (editboxtext.match(/>/g)||[]).length;
	if ( !initialCountDone ) {
		window.aBBAngleBalance = angleBalance;
	}
	if ( angleBalance !== 0 && ( ( initialCountDone && window.aBBAngleBalance !== angleBalance ) || window.aBBWarnInitially ) ) {
		warningStr += '<br /><span class="antibracketbot-angle">&lt;' + window.aBBAngleBalance;
		if ( window.aBBAngleBalance !== angleBalance ) {
			warningStr += ' &rarr; ' + angleBalance;
		}
		warningStr += '&gt;</span>';
	}

	if ( warningStr.indexOf( '<br />' ) === 0 ) warningStr = warningStr.substr( 6 );
	if ( warningStr ) {
		warningStr = '<span class="antibracketbot-all">' + warningStr + '</span>';
		mw.notify( $( warningStr ) );
	}

}

if ( mw.config.get( 'wgAction' ) === 'edit' || mw.config.get( 'wgAction' ) === 'submit' ) {
	//needs to run on page load to store the initial counts to window.aBB(foo)Balance
	$( checkEditBrackets );
}