// ==UserScript==
// @name            Comment Helper
// @description     Adds boxes to help with commenting and giving awards and group invitations without having to copy and paste every time
// @author          Susannah Relf. (code by cyclingrelf http://www.flickr.com/photos/cyclingrelf/)
// @namespace       http://www.flickr.com/photos/cyclingrelf/
// @include         http://*flickr.com/photos/*
// @include         http://*flickr.com/groups/*/discuss*
// @include         http://*flickr.com/groups*
// @include         http://*flickr.com/photos/*/editcomment*
// @exclude         http://*flickr.com/groups/*/pool*
// @exclude         http://*flickr.com/groups/*/admin*
// @exclude         http://*flickr.com/groups/*/map*
// @exclude         http://*flickr.com/groups_members*
// @exclude         http://*flickr.com/groups_invite*
// @version         0.1
// ==/UserScript==

//Thanks to the quickcomment script by Mortimer for much of the code which I reused in making this script
//http://userscripts.org/scripts/show/5106

// ======================================================================
// To add or edit a Group Invitation, Award or Comment go in the menu tools->User Scripts commands->
// And choose the relevant action.
// The name is what will apear in the select box.
// The comment is what will be added at the cursor position of the current comment field. This field will autofill with what you have selected on the page.
// ======================================================================

// --------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need FireFox  http://www.mozilla.org/firefox and the firefox extension called Greasemonkey: http://greasemonkey.mozdev.org/
// Install the Greasemonkey extension then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// To uninstall, go to Tools/Manage User Scripts,
// select "Comment Helper", and click Uninstall.
//
// --------------------------------------------------------------------


(function() {

	var usercomments = new Array();
	var userinvites = new Array();
	var userawards = new Array();

	/***********************************************************************
	 * Language personalisation
	 **********************************************************************/

	function $x1(xpath) {
		return document.evaluate(
								 xpath,
								 document,
								 null,
								 XPathResult.FIRST_ORDERED_NODE_TYPE, null
								 ).singleNodeValue;
	}
	//Define a LocalSettingsClass object with functions to initalise it with local settings
	var LocalSettingsClass = function(locals) {
		this.init(locals);
	}
	LocalSettingsClass.prototype = {
		selectedLang: undefined,
		localisations: undefined,
		getLanguage: function() {
			if(!this.selectedLang) {
				var langA = $x1("//p[@class='LanguageSelector']//a[contains(@class,'selected')]");
				if(langA) {
					var matches = /\/change_language.gne\?lang=([^&]+)&.*/.exec(langA.href);
					if(matches && matches[1]) {
						this.selectedLang = matches[1];
						return this.selectedLang;
					}
				}
				return false;
			} else return this.selectedLang;
		},

		init: function(locals) {
			this.localisations = locals;
		},

		localise: function(string, params) {
			if(this.localisations && this.getLanguage()) {
				var currentLang = this.localisations[this.selectedLang];
				if(!currentLang) currentLang = this.localisations[this.localisations.defaultLang];
				var local = currentLang[string];
				if(!local) {
					local = this.localisations[this.localisations.defaultLang][string];
				} 
				if(!local) return string;
				for(arg in params) {
					var rep = new RegExp('@'+arg+'@','g');
					local = local.replace(rep,params[arg]);
				}
				local =local.replace(/@[^@]+@/g,'');
				return local;
			} else return undefined;
		}

	}

	/*****************************Initialise a LocalSettingsClass object with the local settings**********************/


	var localiser = new LocalSettingsClass({
			'en-us' :{
				'Name':'Name',
				'choose_name': 'Choose a name',
				'Comment': 'Comment',
				'Add':'Add',
				'Cancel':'Cancel',
				'prompt_comment': "Add a new comment",
				'Save':'Save',
				'edit_comments': "Edit comments",
				'help_comments' : 'Comment',
				'add_title' : 'Add A New Comment',
				'edit_title' : 'Edit saved comments (uncheck to remove)',
				'invite' : 'Invite',
				'award' : 'Award',
				'choose_type' : 'Choose comment type',
				'Next' : 'Next'
			},
			'fr-fr': {
				// A
				'Add' : 'Ajouter',
				// C
				'Cancel' : 'Annuler',
				'Comment' : 'Commentaire',
				// N
				'Name' : 'Nom',
				// S
				'Save' : 'Sauver',
				// C
				'choose_name' : 'Choisir un nom',
				// E
				'edit_comments' : 'Editer la liste de commentaires',
				// P
				'prompt_comment' : 'Ajouter un nouveau commentaire',
				'help_comments' : 'Commentaires',
				'add_title' : 'Ajouter Un Nouveau Commentaire',
				'edit_title' : 'Editer les commentaires existants (d&eacute;cocher pour enlever)',
				'invite' : 'Invitez',
				'award' : 'Cadeau',
				'choose_type' : 'choisissez le type de commentaire',
				'Next' : 'apr�s'
			},
			'pt-br': {
				// A
				'Add' : 'Adicionar',
				// C
				'Cancel' : 'Cancelar',
				'Comment' : 'Coment&aacute;rio',
				// N
				'Name' : 'Nome',
				// S
				'Save' : 'Salvar',
				// A
				'add_title' : 'Adicionar coment&aacute;rio',
				// C
				'choose_name' : 'Escolher um nome',
				// E
				'edit_comments' : 'Editar coment&aacute;rio',
				'edit_title' : 'Editar coment&aacute;rio salvo (desmarcar para remover)',
				// P
				'prompt_comment' : 'Adicionar um novo coment&aacute;rio',
				// Q
				'help_comments' : 'Coment&aacute;rio',
				'invite' : 'Convide',
				'award' : 'Presente',
				'choose_type' : 'escolher o tipo de coment&aacute;rio',
				'Next' : 'em seguida'
			},
			defaultLang:'en-us'
		});

	/**************************/
	/**Define functions to use*/
	/**************************/

	//from http://www.nabble.com/-greasemonkey-users--GM_setValue-loses-unicode-characters-t2840046.html#a7943662
	//encode_utf8(s) and decode_utf8(s) are to prevent loss of unicode characters when using GM_setValue()
	function encode_utf8( s )
		{
			return unescape( encodeURIComponent( s ) );
		}
	
	function decode_utf8( s )
		{
			return decodeURIComponent( escape( s ) );
		} 

	//saveusercomments() stores the user data from an array called usercomments in a permanent variable named 'usercomment'
	function saveusercomments() {
		var value = '';
		for each(q in usercomments) {
			value += '@#@'+q[0]+'{#}'+q[1];
		}
		value = value.substr(3);
		GM_setValue('usercomment',encode_utf8(value));
	}
	
	//saveuserinvites() stores the user data from an array called userinvites in a permanent variable named 'userinvite'
	function saveuserinvites()
		{
			var value = '';
			for each(q in userinvites) {
						   value += '@#@'+q[0]+'{#}'+q[1];
						 }
			value = value.substr(3);
			GM_setValue('userinvite',encode_utf8(value));
		}

	//saveuserawards() stores the user data from an array called userawards in a permanent variable named 'useraward'
	function saveuserawards()
		{
			var value = '';
			for each(q in userawards) {
						   value += '@#@'+q[0]+'{#}'+q[1];
						 }
			value = value.substr(3);
			GM_setValue('useraward',encode_utf8(value));
		}
	
	//loadComment() retrieves the data from the permanent variable usercomment and stores it in array usercomments.
	function loadComment() {
		var value = decode_utf8(GM_getValue('usercomment'));
		if(value) {
			var split = value.split('@#@');
			for each(q in split) {
				var s = q.split('{#}');
				usercomments.push(new Array(s[0],s[1]));
			}
			usercomments.sort();
		}
	}

	//loadAward() retrieves the data from the permanent variable useraward and stores it in array userawards.
	function loadAward()
		{
			var value = decode_utf8(GM_getValue('useraward'));
			if(value) {
					var split = value.split('@#@');
					for each(q in split) {
								var s = q.split('{#}');
								userawards.push(new Array(s[0],s[1]));
							     }
					userawards.sort();
				  }
		}

	//loadInvite() retrieves the data from the permanent variable userinvite and stores it in array userinvites.
	function loadInvite()
		{
			var value = decode_utf8(GM_getValue('userinvite'));
			if(value) {
					var split = value.split('@#@');
					for each(q in split) {
								var s = q.split('{#}');
								userinvites.push(new Array(s[0],s[1]));
							     }
					userinvites.sort();
				  }
		}

	//createAutoCommenter() adds a comment box in the appropriate place with the user-defined comments listed to select from
	function createAutoCommenter() {
		
		var autocommenter="&nbsp;<form name='sfcommentform'>";

		if(usercomments.length >0) {
			autocommenter+="<select name='sfusercomment' onchange='sf_gpt_addcomment(document.forms.sfcommentform);'><option value='0'>-- "+localiser.localise('help_comments')+" --</option>";
			for (i=0;i<usercomments.length;i++) {
				var label = usercomments[i][0];
				var value = usercomments[i][1];
				autocommenter += '<option value="'+i+'">' +label +'</option>';
			}
			autocommenter += "</select>";
		}
		autocommenter+="  ";
		if(userinvites.length >0) {
						autocommenter+="<select name='sfuserinvite' onchange='sf_gpt_addcomment(document.forms.sfcommentform);'><option value='0'>-- "+localiser.localise('invite')+" --</option>";
						for (i=0;i<userinvites.length;i++) {
											var label = userinvites[i][0];
											var value = userinvites[i][1];
											autocommenter += '<option value="'+i+'">' +label +'</option>';
										  }
						autocommenter += "</select>";
						}
		autocommenter+="  ";
		if(userawards.length >0) {
						autocommenter+="<select name='sfuseraward' onchange='sf_gpt_addcomment(document.forms.sfcommentform);'><option value='0'>-- "+localiser.localise('award')+" --</option>";
						for (i=0;i<userawards.length;i++) {
											var label = userawards[i][0];
											var value = userawards[i][1];
											autocommenter+= '<option value="'+i+'">' +label +'</option>';
										  }
						autocommenter+= "</select>";
						}

		autocommenter += "</form>";
		return autocommenter;
	}

	/**************************LOADS USER DEFINED COMMENTS, AWARDS AND INVITATIONS FOR USE**********************/
	loadComment();
	loadAward();
	loadInvite();

	createSpan=document.createElement('span');

	//prompt_autocomment() sets up the boxes and buttons that enable a user to create a new comment
	var prompt_autocomment = function() {
		var select = unsafeWindow.getSelection();
		var back = document.body.appendChild(document.createElement('div'));
		back.id="poolCleaningBack";
		back.setAttribute('style',"position:absolute;background-color: black;opacity: 0.35; display: block; left: 0pt;");
		back.style.width = document.body.clientWidth+'px';
		back.style.height = document.body.clientHeight+'px';
		back.style.top = document.body.scrollTop+'px';
		var modal = document.body.appendChild(document.createElement('div'));
		modal.id="poolCleaning";
		modal.setAttribute('style',"position:absolute;background:white;border: 3px solid black;display: block;");
		modal.style.width = (document.body.clientWidth*2/3) +'px';
		modal.style.left = (document.body.clientWidth*1/6) +'px';
		modal.innerHTML = '<div style="padding:12px;background-color: #EEEEEE;clear:both;font-size: 14px;">'+localiser.localise('add_title')+'</div>';
		modal.style.top = document.body.scrollTop+(document.body.clientHeight/2)+'px';
		
		var dialog = modal.appendChild(document.createElement('div'));
		dialog.setAttribute('style',"padding: 18px 16px;clear:both; width:100%;");
		var content = dialog.appendChild(document.createElement('div'));
		content.setAttribute('align', 'left');

		//Add selection box to choose type of comment.

		var fs = content.appendChild(document.createElement('form'));
		fs.setAttribute('name', 'userselectionform');
		var l1 = fs.appendChild(document.createElement('label'));
		l1.setAttribute('for', 'sfcommenttype');
		l1.innerHTML = localiser.localise("choose_type")+':';
		l1.style.clear="both";

		var cs = fs.appendChild(document.createElement('select')); 
		cs.setAttribute('name', 'sfcommenttype');
		var userChoice = 0;
		o1 = cs.appendChild(document.createElement('option'));
		o1.value = '0';
		o1.innerHTML = localiser.localise('help_comments');
		o3 = cs.appendChild(document.createElement('option'));
		o3.value = '1';
		o3.innerHTML = localiser.localise('invite');
		o4 = cs.appendChild(document.createElement('option'));
		o4.value = '2';
		o4.innerHTML = localiser.localise('award');
		content.appendChild(document.createElement('br'));

		var l2 = content.appendChild(document.createElement('label'));
		l2.innerHTML = localiser.localise('Name')+':';
		l2.style.clear="both";
		var vname = content.appendChild(document.createElement('input'));
		vname.type="text";
		vname.value = localiser.localise("choose_name");
		
		content.appendChild(document.createElement('br'));
		content.appendChild(document.createElement('br'));
		var l3 = content.appendChild(document.createElement('label'));
		l3.innerHTML = localiser.localise('Comment')+':';
		content.appendChild(document.createElement('br'));
		content.appendChild(document.createElement('br'));
		var text = content.appendChild(document.createElement('textarea'));
		text.rows= 10;
		if(select.isCollapsed||select.length==0)
		{
			text.innerHTML = localiser.localise('prompt_comment');
		}
		else
		{
			text.innerHTML = select;
		}
		text.setAttribute('style','width: 90%');


		var buttons = dialog.appendChild(document.createElement('div'));
		var ok = buttons.appendChild(document.createElement('button'));
		ok.type ='button';
		ok.className='Butt';
		ok.innerHTML = localiser.localise('Add');
		var cancel = buttons.appendChild(document.createElement('button'));
		cancel.type ='button';
		cancel.className = 'Butt';
		cancel.innerHTML = localiser.localise('Cancel');
		
		cancel.addEventListener('click',function() {
									document.body.removeChild(back);
									document.body.removeChild(modal);
								},true);
		
/****************************************************************************************************************/
		//Defines code that will add the text to the relevant user comments list when the Ok button is pressed
		//It also immediately updates the selection boxes.
		ok.addEventListener('click',function() {
									if(cs.value==0)
									{
										usercomments.push(new Array(vname.value,text.value));
										usercomments.sort();
										saveusercomments();
										createSpan.innerHTML = createAutoCommenter();
									}
									else if(cs.value==1)
									{
										userinvites.push(new Array(vname.value,text.value));
										userinvites.sort();
										saveuserinvites();
										createSpan.innerHTML = createAutoCommenter();
									}
									else if(cs.value==2)
									{
										userawards.push(new Array(vname.value,text.value));
										userawards.sort();
										saveuserawards();
										createSpan.innerHTML = createAutoCommenter();
									}
									else
									{
										usercomments.push(new Array(vname.value,text.value));
										usercomments.sort();
										saveusercomments();
										createSpan.innerHTML = createAutoCommenter();
									}
									document.body.removeChild(back);
									document.body.removeChild(modal);
								},true);
		
		modal.style.top = document.body.scrollTop+((document.body.clientHeight-modal.scrollHeight)/2)+'px';
	};
	GM_registerMenuCommand(localiser.localise("prompt_comment"), prompt_autocomment);

	/****************************************************************************************************************/
	var prompt_editautocomment = function() {
		var back = document.body.appendChild(document.createElement('div'));
		back.id="poolCleaningBack";
		back.setAttribute('style',"position:absolute;background-color: black;opacity: 0.35; display: block; left: 0pt;");
		back.style.width = document.body.clientWidth+'px';
		back.style.height = document.body.clientHeight+'px';
		back.style.top = document.body.scrollTop+'px';
		var modal = document.body.appendChild(document.createElement('div'));
		modal.id="poolCleaning";
		modal.setAttribute('style',"position:absolute;background:white;border: 3px solid black;display: block;");
		modal.style.width = (document.body.clientWidth*2/3) +'px';
		modal.style.left = (document.body.clientWidth*1/6) +'px';
		modal.innerHTML = '<div style="padding:12px;background-color: #EEEEEE;clear:both;font-size: 14px;">'+localiser.localise('choose_type')+'.</div>';
		modal.style.top = document.body.scrollTop+(document.body.clientHeight/6)+'px';
		
		var dialog = modal.appendChild(document.createElement('div'));
		dialog.setAttribute('style',"padding: 18px 16px;clear:both; width:100%;overflow:auto;");
		var content = dialog.appendChild(document.createElement('div'));

		
		//Defines an ordered list
		var ul = document.createElement('ul');
		var inputs = new Array();
		var commentchoice = new Array();
		
		//Add selection box to choose type of comment.

		var fs = content.appendChild(document.createElement('form'));
		fs.setAttribute('name', 'userselectionform');
		var l1 = fs.appendChild(document.createElement('label'));
		l1.setAttribute('for', 'sfcommenttype');
		l1.innerHTML = localiser.localise("choose_type")+':';
		l1.style.clear="both";

		var cs = fs.appendChild(document.createElement('select')); 
		cs.setAttribute('name', 'sfcommenttype');
		o2 = cs.appendChild(document.createElement('option'));
		o2.value = '3';
		o2.innerHTML = localiser.localise('choose_type');
		o1 = cs.appendChild(document.createElement('option'));
		o1.value = '0';
		o1.innerHTML = localiser.localise('help_comments');
		o3 = cs.appendChild(document.createElement('option'));
		o3.value = '1';
		o3.innerHTML = localiser.localise('invite');
		o4 = cs.appendChild(document.createElement('option'));
		o4.value = '2';
		o4.innerHTML = localiser.localise('award');
		content.appendChild(document.createElement('br'));
		cs.addEventListener('change', function()
			{
				selectedI = this.selectedIndex;
				modal.innerHTML = '<div style="padding:12px;background-color: #EEEEEE;clear:both;font-size: 14px;">'+localiser.localise('edit_title')+'.</div>';
				var dialog = modal.appendChild(document.createElement('div'));
				dialog.setAttribute('style',"padding: 18px 16px;clear:both; width:100%;overflow:auto;");
				var content = dialog.appendChild(document.createElement('div'));

				arrayToUse = getArray(selectedI);
				//Defines an ordered list
				var ul = document.createElement('ul');
				var inputs = new Array();
				var commentchoice = new Array();
		
				//creates an ordered list of user comments with checked boxes and adds them to array inputs
				for (i=0;i<arrayToUse.length;i++) {
					var label = arrayToUse [i][0];
					var value = arrayToUse [i][1];
					var li = document.createElement('li');
					var l1 = li.appendChild(document.createElement('label'));
					l1.innerHTML = label;
					var vname = document.createElement('input');
					vname.type="checkbox";
					vname.value = i;
					vname.checked = true;
					li.appendChild(vname);
					ul.appendChild(li);
					inputs.push(vname);
				}
				content.appendChild(ul);
		
				var buttons = dialog.appendChild(document.createElement('div'));
				var ok = buttons.appendChild(document.createElement('button'));
				ok.type ='button';
				ok.className='Butt';
				ok.innerHTML = localiser.localise('Save');
				var cancel = buttons.appendChild(document.createElement('button'));
				cancel.type ='button';
				cancel.className = 'Butt';
				cancel.innerHTML = localiser.localise('Cancel');
		
				cancel.addEventListener('click',function() 	{
											document.body.removeChild(back);
											document.body.removeChild(modal);
										},true);
		
				//Defines code that will remove unchecked comments from the list and save. It also updates the selection list.
				ok.addEventListener('click',function() {
									new_q = new Array();
									if (selectedI==1)
									{
										for each(inp in inputs) {
													if(inp.checked)
													new_q.push(usercomments[inp.value]);
													}
										usercomments = new_q;
										saveusercomments();
										createSpan.innerHTML = createAutoCommenter();
									}
									else if (selectedI==2)
									{
										for each(inp in inputs) {
													if(inp.checked)
													new_q.push(userinvites[inp.value]);
													}
										userinvites = new_q;
										saveuserinvites();
										createSpan.innerHTML = createAutoCommenter();
									}
									else if (selectedI==3)
									{
										for each(inp in inputs) {
													if(inp.checked)
													new_q.push(userawards[inp.value]);
													}
										userawards = new_q;
										saveuserawards();
										createSpan.innerHTML = createAutoCommenter();
									}
									else
									{
										for each(inp in inputs) {
													if(inp.checked)
													new_q.push(usercomments[inp.value]);
													}
										usercomments = new_q;
										saveusercomments();
										createSpan.innerHTML = createAutoCommenter();
									}
									document.body.removeChild(back);
									document.body.removeChild(modal);
									},true);
		
				modal.style.top = document.body.scrollTop+((document.body.clientHeight-modal.scrollHeight)/2)+'px';
			}
		,true);
	}

	function getArray(selectedI){
					if(selectedI==1)
					{
						return usercomments;
					}
					else if(selectedI==2)
					{
						return userinvites;
					}
					else if(selectedI==3)
					{
						return userawards;
					}
					else
					{
						return usercomments;
					}
					}
/****************************************************************************************************************/
	//Adds the function "editautocomment" to the greasemonkey menu
	GM_registerMenuCommand(localiser.localise('edit_comments'), prompt_editautocomment);
	
	var w;
	if (unsafeWindow) 
		w=unsafeWindow;
	else
		w=window;
	
	w.sf_gpt_addcomment=function(theform) {
		
		thisTextArea = document.evaluate(
										 "//textarea[@name='message']",
										 document,
										 null,
										 XPathResult.FIRST_ORDERED_NODE_TYPE, null
										 ).singleNodeValue;
		
		if(thisTextArea) {
			mesg = '';
			if(theform.sfusercomment.selectedIndex != 0)
				{
				mesg += usercomments[theform.sfusercomment.options[theform.sfusercomment.selectedIndex].value][1];
				theform.sfusercomment.selectedIndex = 0;
				}
			else if(theform.sfuserinvite.selectedIndex != 0)
				{
				mesg += userinvites[theform.sfuserinvite.options[theform.sfuserinvite.selectedIndex].value][1];
				theform.sfuserinvite.selectedIndex = 0;
				}
			else if(theform.sfuseraward.selectedIndex != 0)
				{
				mesg += userawards[theform.sfuseraward.options[theform.sfuseraward.selectedIndex].value][1];
				theform.sfuseraward.selectedIndex = 0
				}
			try {
				thisTextArea.value = thisTextArea.value.substr(0,thisTextArea.selectionStart) 
					+ mesg
					+ thisTextArea.value.substr(thisTextArea.selectionStart);
			} catch(e) {
				thisTextArea.value += mesg;
			}
		}
	}
	
	createSpan.innerHTML= createAutoCommenter();
	var thisLink;
	if(document.location.pathname.indexOf('photos/') >= 0) {		
		if(document.location.pathname.indexOf('editcomment') >= 0) {
			thisLink = document.evaluate('//td[@id=\'GoodStuff\']/h3',
										 document,
										 null,
										 XPathResult.FIRST_ORDERED_NODE_TYPE,
										 null
										 ).singleNodeValue;	

			if(thisLink)
				thisLink.parentNode.insertBefore(createSpan, thisLink.nextSibling);			
		} else {
			var allLinks, thisLink;
			allLinks = document.evaluate('//div[@id="DiscussPhoto"]/h3',
										 document,
										 null,
										 XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
										 null);
			for (var i = 0; i < allLinks.snapshotLength; i++) {
				thisLink = allLinks.snapshotItem(i);
				thisLink.parentNode.insertBefore(createSpan, thisLink.nextSibling);
			}
		}
	} else if(document.location.pathname.indexOf('discuss/') >= 0) {		
		if(document.location.pathname.indexOf('edit') >= 0) {
			thisLink = document.evaluate('//td[@id=\'GoodStuff\']/form/p[1]/textarea',
										 document,
										 null,
										 XPathResult.FIRST_ORDERED_NODE_TYPE,
										 null
										 ).singleNodeValue;	
			if(thisLink)
				thisLink.parentNode.insertBefore(createSpan, thisLink);
		} else {
			thisLink = document.evaluate(
										 '//div[@id=\'DiscussTopic\']//td/h3',
										 document,
										 null,
										 XPathResult.FIRST_ORDERED_NODE_TYPE,
										 null
										 ).singleNodeValue;	
			if(thisLink)
				thisLink.parentNode.insertBefore(createSpan, thisLink.nextSibling);
		} 
	} else if(document.location.pathname == "/groups_newtopic.gne") {
		thisLink = document.evaluate('//td[@id=\'GoodStuff\']/form/table/tbody/tr[2]/td[2]/textarea',
									 document,
									 null,
									 XPathResult.FIRST_ORDERED_NODE_TYPE,
									 null
									 ).singleNodeValue;	
		if(thisLink)
			thisLink.parentNode.insertBefore(createSpan, thisLink);

	}
})();