onPageNote

By Chris Bouchard Last update Apr 19, 2006 — Installed 742 times.
// ==UserScript==
// @name           onPageNote
// @namespace      http://neko-mangaka.deviantart.com
// @description    Write and send notes without ever leaving the page
// @include        http://*.deviantart.com/*
// ==/UserScript==

(function()
{
	// =-=-=-=-=- C O N S T A N T S =-=
	var NEW_NOTE = 'http://i.deviantart.com/icons/notes/note-unread.gif';
	var SENT_NOTE = 'http://i.deviantart.com/icons/notes/note-replied.gif';
	var GRAY_NOTE = 'http://i.deviantart.com/icons/notes/note-read.gif';
	var THROBBER = 'http://s.deviantart.com/icons/misc/loading.gif';
	
	// =-=-=-=-=- V A R I A B L E S =-=
	var notesInterfaceOpen = false;
	var notePreviewOpen = false;
	
	// =-=-=-=-=- C O M P O N E N T S =-=	
	var interfaceDiv = document.createElement('div');
	
	var intHeadDiv = document.createElement('div');
	var headTitleH2 = document.createElement('h2');
	var headIconImg = document.createElement('img');
	var headRecipientSpan = document.createElement('span');
	var headFloatRightSpan = document.createElement('span');
	var headOpenNotesA = document.createElement('a');
	
	var intContainerDiv = document.createElement('div');
	var intSubjectText = document.createElement('input');
	var intMessageTextarea = document.createElement('textarea');
	var intSubjectLabel = document.createElement('label');
	var intMessageLabel = document.createElement('label');
	
	var intPreviewDiv = document.createElement('div');
	
	var intFootDiv = document.createElement('div');
	var footPreviewButton = document.createElement('input');
	var footSendButton = document.createElement('input');
	
	var intBottomDiv = document.createElement('div');
	var footCloseButton = document.createElement('input');
	
	var backgroundDiv = document.createElement('div');
	
	var alertDiv = document.createElement('div');
	var alertIconImg = document.createElement('img');
	var alertMessageLabel = document.createElement('label');
	var alertMessageSpan = document.createElement('span');
	
	// =-=-=-=-=- F U N C T I O N S =-=
	
	// ========== Handles clicks on the page ===
	var handleClicks = function(event)
	{		
		// Left mouse button used
		if (event.button == 0)
		{
			var target = event.target;
			
			// Close button
			if (target == footCloseButton)
			{
				hideInterface();
			}
			
			// Preview button
			if (target == footPreviewButton)
			{
				togglePreview();
			}
			
			// Send button
			else if (target == footSendButton)
			{
				sendNote();
				hideInterface();
			}
			else
			{
				// Find the first A element containing target
				// Stop if target becomes body --> there isn't an A in the chain
				while (target.nodeName.toLowerCase() != 'a' && target != document.body) target = target.parentNode;
				
				// Check to make sure that target fits the description:
				//           an A element pointing to 'http://my.deviantart.com/notes[/]?to=*'
				// I should probably use a Regular Expression here, but I don't know how
				if (target.nodeName.toLowerCase() == 'a' && target.href.indexOf('http://my.deviantart.com/notes') > -1 &&
					target.href.indexOf('?to=') > -1 && !notesInterfaceOpen)
				{					
					var deviant = target.href.substring(target.href.indexOf('?to=') + 4);
					
					showInterface(deviant);
					
					event.stopPropagation();
					event.preventDefault();
				}
			}
		}
	};
	
	// ========== Show the note interface ===
	var showInterface = function(deviant)
	{
		var interface = document.getElementById('opn-interface');
		var linkURL = interface.getElementsByTagName('a')[0].href.substring(0, interface.getElementsByTagName('a')[0].href.indexOf('=') + 1) + deviant;
		
		interface.getElementsByTagName('span')[1].innerHTML = deviant;
		interface.getElementsByTagName('a')[0].href = linkURL;
		interface.style.visibility = 'visible';
		
		if (notePreviewOpen) togglePreview();
		
		document.getElementById('opn-background').style.visibility = 'visible';
		
		notesInterfaceOpen = true;
	};
	
	// ========== Hide the note interface ===
	var hideInterface = function()
	{
		document.getElementById('opn-interface').style.visibility = 'hidden';
		document.getElementById('opn-background').style.visibility = 'hidden';
		
		notesInterfaceOpen = false;
	};
	
	// ========== Clear the note interface ===
	var clearInterface = function()
	{
		document.getElementById('opn-notesubject').value = '';
		document.getElementById('opn-notebody').value = '';
	};
	
	// ========== Send the note ===
	var sendNote = function()
	{
		var interface = document.getElementById('opn-interface');
		
		var ref = window.location.href;
		var recipients = interface.getElementsByTagName('span')[1].innerHTML;
		var subject = document.getElementById('opn-notesubject').value;
		var message = document.getElementById('opn-notebody').value;
		
		var noteData = encodeURI('ref=' + ref + '&recipients=' + recipients + '&subject=' + subject + '&body=' + message);
		
		GM_xmlhttpRequest(
		{
			method: 'POST',
			url: 'http://my.deviantart.com/notes/send',
			headers: 
			{
				'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
				'Accept': 'application/atom+xml,application/xml,text/xml',
				'Content-type': 'application/x-www-form-urlencoded'
			},
			data: noteData,
			onload: function(responseDetails)
			{
				changeAlertIcon(NEW_NOTE);
				showAlert('note sent...');
				clearInterface();
			},
			onerror: function(responseDetails)
			{
				changeAlertIcon(GRAY_NOTE);
				showAlert('error sending note (' + responseDetails.status + ')...');
			},
			onreadystatechange: function(responseDetails)
			{
				if (responseDetails.readyState == 1)
				{
					changeAlertIcon(THROBBER);
					showAlert('sending note...');
				}
			}
		});
	};
	
	// ========== Change the icon image of the alert ===
	var changeInterfaceIcon = function(imageURL)
	{
		document.getElementById('opn-interface').getElementsByTagName('img')[0].src = imageURL;
	};
	
	// ========== Show or hide the preview ===
	var togglePreview = function()
	{
		if (!notePreviewOpen)
		{
			var message = document.getElementById('opn-notebody').value;
			
			if (message.length == 0) return;
			
			var previewData = 'body=' + encodeURI(message).replace(/&/g, '%26');
			
			changeInterfaceIcon(THROBBER);
			intPreviewDiv.innerHTML = '. . .';
			document.getElementById('opn-notebody').parentNode.replaceChild(intPreviewDiv, document.getElementById('opn-notebody'));
			document.getElementById('opn-prevbutton').value = 'Edit';
			notePreviewOpen = true;
			
			GM_xmlhttpRequest(
			{
				method: 'POST',
				url: 'http://www.deviantart.com/global/preview.php',
				headers: 
				{
					'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
					'Accept': 'application/atom+xml,application/xml,text/xml',
					'Content-type': 'application/x-www-form-urlencoded'
				},
				data: previewData,
				onload: function(responseDetails)
				{
					changeInterfaceIcon(NEW_NOTE);
					document.getElementById('opn-preview').innerHTML = responseDetails.responseText;
				}
			});
		}
		else
		{
			changeInterfaceIcon(NEW_NOTE);
			document.getElementById('opn-preview').parentNode.replaceChild(intMessageTextarea, document.getElementById('opn-preview'));
			document.getElementById('opn-prevbutton').value = 'Preview';
			notePreviewOpen = false;
		}
	};
	
	// ========== Show an alert ===
	var showAlert = function(message)
	{
		var div = document.getElementById('opn-alert');
		
		div.getElementsByTagName('span')[0].innerHTML = message;
		
		div.style.opacity = '1';
		div.style.visibility = 'visible';
		
		window.setTimeout(fadeAlert, 900);
	};
	
	// ========== Hide the alert ===
	var hideAlert = function()
	{
		var div = document.getElementById('opn-alert');
		
		div.style.opacity = '0';
		div.style.visibility = 'hidden';
	};
	
	// ========== Fade out the alert ===
	var fadeAlert = function()
	{
		var div = document.getElementById('opn-alert');
		
		if (div.style.opacity > .1)
		{
			div.style.opacity = (div.style.opacity - .1);
			window.setTimeout(fadeAlert, 20);
		}
		else hideAlert();
	};
	
	// ========== Change the icon image of the alert ===
	var changeAlertIcon = function(imageURL)
	{
		document.getElementById('opn-alert').getElementsByTagName('img')[0].src = imageURL;
	};
	
	// =-=-=-=-=- S E T - U P =-=
	
	// ========== Create interface container ===
	interfaceDiv.id = 'opn-interface';
	interfaceDiv.className = 'first last output-primary section';
	interfaceDiv.style.position = 'fixed';
	interfaceDiv.style.zIndex = '1001';
	interfaceDiv.style.width = '50%';
	interfaceDiv.style.top = '15%';
	interfaceDiv.style.left = '25%';
	interfaceDiv.style.background = '#BBC2BB';
	interfaceDiv.style.visibility = 'hidden';
	intContainerDiv.className = 'trailing section-block';
	intContainerDiv.style.padding = '5px 5px 5px 5px';
	
	// ========== Create interface header ===
	intHeadDiv.className = 'section-head';
	headIconImg.className = 'icon';
	headIconImg.src = 'http://i.deviantart.com/icons/userpage/notes.gif';
	headFloatRightSpan.className = 'aside-right';
	headFloatRightSpan.style.marginTop = '5px';
	headFloatRightSpan.style.fontSize = 'x-small';
	headOpenNotesA.href = 'http://my.deviantart.com/notes/?to=unknown';
	headOpenNotesA.innerHTML = 'Open Notes...';
	
	// ========== Create subject textfield ===
	intSubjectText.id = 'opn-notesubject';
	intSubjectText.type = 'text';
	intSubjectText.className = 'text';
	intSubjectText.value = '';
	intSubjectText.size = '30';
	intSubjectText.maxLength = '80';
	intSubjectLabel.innerHTML = 'Subject:';
	
	// ========== Create message textarea ===
	intMessageTextarea.id = 'opn-notebody';
	intMessageTextarea.style.height = '200px';
	intMessageTextarea.style.width = '100%';
	intMessageLabel.innerHTML = 'Message:';
	intPreviewDiv.id = 'opn-preview';
	intPreviewDiv.style.height = '200px';
	intPreviewDiv.style.width = '100%';
	intPreviewDiv.style.border = '1px solid black';
	
	// ========== Create interface footer ===
	intBottomDiv.className = 'trailing section-foot';
	intFootDiv.className = 'trailing section-foot';
	footPreviewButton.id = 'opn-prevbutton';
	footPreviewButton.type = 'button';
	footPreviewButton.className = 'beacon';
	footPreviewButton.value = 'Preview';
	footSendButton.type = 'button';
	footSendButton.className = 'beacon aside-right';
	footSendButton.value = 'Send Note';
	footCloseButton.type = 'button';
	footCloseButton.className = 'beacon aside-left';
	footCloseButton.value = 'Close';
	
	// ========== Create background ===
	backgroundDiv.id = 'opn-background';
	backgroundDiv.style.position = 'fixed';
	backgroundDiv.style.zIndex = '1000';
	backgroundDiv.style.width = '100%';
	backgroundDiv.style.height = '100%';
	backgroundDiv.style.top = '0px';
	backgroundDiv.style.left = '0px';
	backgroundDiv.style.background = '#000000';
	backgroundDiv.style.opacity = '.75';
	backgroundDiv.style.visibility = 'hidden';
	
	// ========== Create alert ===
	alertDiv.id = 'opn-alert';
	alertDiv.style.position = 'fixed';
	alertDiv.style.zIndex = '1002';
	alertDiv.style.top = '20px';
	alertDiv.style.right = '20px';
	alertDiv.style.padding = '10px 20px 10px 10px';
	alertDiv.style.background = '#D6DBD6';
	alertDiv.style.border = '1px solid #4A584A';
	alertDiv.style.opacity = '0';
	alertDiv.style.visibility = 'hidden';
	alertDiv.addEventListener('click', hideAlert, true);
	alertIconImg.className = 'icon';
	alertMessageSpan.style.fontWeight = 'bold';
	
	// ========== Build the interface ===
	headTitleH2.appendChild(headIconImg);
	headTitleH2.innerHTML += ' Send a note to ';
	headTitleH2.appendChild(headRecipientSpan);
	headFloatRightSpan.appendChild(headOpenNotesA);
	
	intHeadDiv.appendChild(headFloatRightSpan);
	intHeadDiv.appendChild(headTitleH2);
	
	intContainerDiv.appendChild(intSubjectLabel);
	intContainerDiv.innerHTML += '     ';
	intContainerDiv.appendChild(intSubjectText);
	intContainerDiv.innerHTML += '<br /><br />';
	intContainerDiv.appendChild(intMessageLabel);
	intContainerDiv.innerHTML += '<br />';
	intContainerDiv.appendChild(intMessageTextarea);
	
	
	intBottomDiv.appendChild(footPreviewButton);
	
	intFootDiv.appendChild(footCloseButton);
	intFootDiv.appendChild(footSendButton);
	
	interfaceDiv.appendChild(intHeadDiv);
	interfaceDiv.appendChild(intContainerDiv);
	interfaceDiv.appendChild(intBottomDiv);
	interfaceDiv.appendChild(intFootDiv);
	
	// ========== Build the alert ===
	alertMessageLabel.appendChild(alertMessageSpan);
	
	alertDiv.appendChild(alertIconImg);
	alertDiv.innerHTML += '&nbsp;';
	alertDiv.appendChild(alertMessageLabel);
	
	// ========== Add everything to the DOM ===
	document.body.appendChild(interfaceDiv);
	document.body.appendChild(backgroundDiv);
	document.body.appendChild(alertDiv);
	
	// ========== Begin capturing clicks on the page ===
	document.addEventListener('click', handleClicks, true);
	
	// ========== Show the loaded alert ===
	changeAlertIcon(NEW_NOTE);
	showAlert('onPageNote loaded...');
})()