مدیاویکی:Gadget-ListingEditor.js

از ویکی‌سفر، راهنمای آزاد سفر

نکته: پس از انتشار ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.

  • فایرفاکس / سافاری: کلید Shift را نگه دارید و روی دکمهٔ Reload کلیک کنید، یا کلید‌های Ctrl-F5 یا Ctrl-R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-R)
  • گوگل کروم: کلیدهای Ctrl+Shift+R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-Shift-R)
  • اینترنت اکسپلورر/ Edge: کلید Ctrl را نگه‌دارید و روی دکمهٔ Refresh کلیک کنید، یا کلید‌های Ctrl-F5 را با هم فشار دهید
  • اپرا: Ctrl-F5 را بفشارید.
// <pre>

// This will break toggling if run on mobile skin so exit early.
if ( mw.config.get( 'skin' ) === 'minerva' ) {
	return;
}
window.ListingEditor = {};

ListingEditor.allFields = {
	'type': {right:true, newline:true, parameter:'نوع', label:'نوع', tip:'نوع فهرست', regx:'(نوع فهرست|type)' },
	'name': {right:false, newline:false, parameter:'نام', label:'نام', tip:'نام مکان', regx:'(نام|name)'},
	'alt': {right:true, newline:false, parameter:'نام دیگر', label:'نام دیگر', tip:'همچنین با این نام نیز شناخته می\u200cشود', regx:'(نام دیگر|alt)'},
	'url': {right:false, newline:false, parameter:'نشانی اینترنتی', label:'وب\u200cسایت', tip:'http://www.example.com', regx:'(نشانی اینترنتی|url)'},
	'email': {right:true, newline:true, parameter:'پست الکترونیکی', label:'پست الکترونیکی', tip:'hello@example.com', regx:'(پست الکترونیکی|email|رایانامه)'},
	'address': {right:false, newline:false, parameter:'نشانی', label:'نشانی', tip:'نشانی مکان', regx:'(نشانی|address)'},
	'lat': {right:true, newline:false, parameter:'عرض جغرافیایی', label:'عرض جغرافیایی', tip:'11.11111', regx:'(عرض جغرافیایی|lat)'},
	'long': {right:true, newline:false, parameter:'طول جغرافیایی', label:'طول جغرافیایی', tip:'111.11111', regx:'(طول جغرافیایی|long)'},
	'directions': {right:false, newline:true, parameter:'مسیرها', label:'مسیرها', tip:'چه طوری به آنجا می\u200cرسید', regx:'(مسیرها|directions)'},
	'phone': {right:false, newline:false, parameter:'تلفن', label:'تلفن', tip: '+۹۸ ۲۱ ۸۸۸۸۸۸۸۸', regx:'(تلفن|phone)'},
	'tollfree': {right:true, newline:false, parameter:'تماس رایگان', label:'تماس رایگان', tip:'شماره تلفنی که تماس با آن خرج ندارد', regx:'(تماس رایگان|tollfree)'},
	'fax': {right:true, newline:false, parameter:'دورنگار', label:'دورنگار', tip: '+۹۸ ۲۱ ۸۸۸۸۸۸۸۸', regx:'(دورنگار|fax)'},
	'image': {right:true, newline:true, parameter:'تصویر', label:'تصویر', tip: 'تصویر مکان', regx:'(تصویر مکان|image)'},
	'hours': {right:false, newline:false, parameter:'ساعت\u200cها', label:'ساعت\u200cها', tip: 'ساعت\u200cهای کاری', regx:'(ساعت\u200cهای کاری|hours)'},
	'checkin': {right:true, newline:false, parameter:'زمان پذیرش', label:'زمان پذیرش', tip: 'زمان پذیرش', regx:'(زمان پذیرش|checkin)'},
	'checkout': {right:true, newline:false, parameter:'زمان اتمام پذیرش', label:'زمان اتمام پذیرش', tip: 'زمان اتمام پذیرش', regx:'(زمان اتمام پذیرش|checkout)'},
	'price': {right:false, newline:true, parameter:'بها', label:'بها', tip: 'بهای خدمات', regx:'(بها|price)'},
	'content': {rows:8, right:false, newline:true, parameter:'متن', label:'متن', tip: 'توضیح درباره مکان', regx:'(متن|content)'}
};

ListingEditor.currencySigns = ['\ufdfc','\u062f\u002e\u0625\u002e','\u0024','\u00A3', '\u20AC', '\u00A5', '\u20A9'];
ListingEditor.listingTypes = {'see':'دیدن', 'do':'انجام\u200cدادن', 'buy':'خریدن', 'eat':'خوردن', 'drink':'نوشیدن', 'sleep':'خوابیدن', 'listing':'فهرست\u200cبندی'};
ListingEditor.listingTypesLabels = {'see':'دیدن', 'do':'انجام\u200cدادن', 'buy':'خریدن', 'eat':'خوردن', 'drink':'نوشیدن', 'sleep':'خوابیدن', 'listing':'عمومی'};
ListingEditor.listingTypesRegex = {'see':'(دیدن|see)', 'do':'(انجام دادن|do|انجام\u200cدادن)', 'buy':'(خریدن|buy)', 'eat':'(خوردن|eat)', 'drink':'(نوشیدن|drink|آشامیدن)', 'sleep':'(خوابیدن|sleep|خواب)', 'listing':'(listing|فهرست\u200cنویسی|فهرست\u200cبندی|فهرست بندی|فهرست)'};
ListingEditor.addListingSections = {'see':'(دیدن|دیدنی ها|دیدنی\u200cها|جاذبه های گردشگری|جاذبه\u200cهای گردشگری|جاذبه ها|جاذبه\u200cها)','do':'(انجام\u200cدادن|انجام دادن|باید انجام داد|انجام دادنی\u200cها)','buy':'(خریدن|خرید کردن|خرید)','eat':'(خوردن|رستوران ها|رستوران\u200cها)','drink':'(نوشیدن|آشامیدن|نوشیدن و کافی شاپ|نوشیدن و کافی\u200cشاپ)','sleep':'(خوابیدن|خواب|هتل\u200cها|هتل ها)','listing':'(ارتباطات|آموزش|سفارت خانه\u200cها|سفارت خانه ها|سفارت\u200cخانه\u200cها|سفارت\u200cخانه ها)'};
ListingEditor.LICENSE_TEXT = 'با کلیک بر روی «ثبت»، شما با <a class="external" target="_blank" href="http://wikimediafoundation.org/wiki/Terms_of_use">شرایط استفاده</a> موافقت می\u200cکنید، و همچنین مشارکت شما برای مدت نامحدود تحت <a class="external" target="_blank" href="http://en.wikivoyage.org/wiki/Wikivoyage:Full_text_of_the_Attribution-ShareAlike_3.0_license">گواهینامه CC-BY-SA 3.0</a> منتشر خواهد شد.';
ListingEditor.translateStr = {
	'add': 'افزودن به فهرست',
	'edit': 'ویرایش',
	'preview': 'پیش\u200cنمایش',
	'closed': 'بسته شده؟',
	'saving': 'در حال ذخیره',
	'submit': 'ثبت',
	'cancel': 'لغو',
	'validationalert': 'خواهش می\u200cکنم نام و یا نشانی را وارد کنید',
	'added': 'افزودن: ',
	'updated': 'به روز رسانی: ',
	'removed': 'بسته شده: ',
	'cities': 'Cities',
	'destination': 'Other_destinations',
	'geomap': 'مشاهده موقعیت روی نقشه',
	'help-page': '//fa.wikivoyage.org/wiki/%D9%88%DB%8C%DA%A9%DB%8C%E2%80%8C%D8%B3%D9%81%D8%B1:%D9%88%DB%8C%D8%B1%D8%A7%DB%8C%D8%B4%DA%AF%D8%B1_%D9%81%D9%87%D8%B1%D8%B3%D8%AA%E2%80%8C%D8%A8%D9%86%D8%AF%DB%8C%E2%80%8C%D9%87%D8%A7',
	'enter-captcha': 'حروف به هم ریخته را وارد کنید',
	'external-links': 'ویرایش شما شامل پیوندهای خارجی تازه می\u200cباشد.'
};

ListingEditor.loadingIcon = 'data:image/gif;base64,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';

ListingEditor.ToolIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAAAzFBMVEUAAAAAoP+A0P9VwP9OnetVpO5aqfBVqfFSpfVVp/ZYqfZTo/FTpPJXqvRYq/VVpvVXpvFWpvJVpfNWqfVWp/JVpfJXqfNWp/NXqPNWpvRXqPNWp/NVp/RXqPRWp/JXqfRXqvRWpfJWpvJXqPNXqfNXqPRVpfNWpvRVpvRWqfRWpvJVpfJWp/NWpvNVpfNWp/RWp/NVpfNWqPNVpfNWpvNWp/NWp/NWp/NWp/NWp/NWp/NWqPNWp/NWp/NWp/NWp/NWp/NWp/NWp/NWp/Ox28KLAAAAQ3RSTlMAAQIGDQ8REhkbHSUoLzEzODtCSk1RUlZYXGdub3B5h4qLjpOZnLCys7S1trrDxcnMzs/R2ebn6Orr7u/x8/X5+v3+w2zu5gAAAJJJREFUeAFlyNW2gkAABdDD5drdIgYqdmC3InP+/5980DWLGfbjRljrvGtA0xH0K9pZgqxBsyAff3r2SDqA0a+HMkeSg5TLKSSjdOXXUp454c89L29Okl5A3grq+YmkPcyqJ2xI8trKzSKXcTaRK77eF/2wOqbR1Q7PERCnC8X28A+LTTWr4rQO9iZU5bHnxNT6AAC2HLAUiyuPAAAAAElFTkSuQmCC';

ListingEditor.wrapContent = function () {
	$('h2').each(function(){ 
		$(this).nextUntil("h2").addBack().wrapAll('<div class="mw-h2section" />');
	});
};

ListingEditor.addListingButtons = function () {
	if ($('#'+ListingEditor.translateStr['cities']).length || $('#'+ListingEditor.translateStr['destination']).length || $('#'+'Islands').length || $('#'+'print-districts').length) {
		return false;
	}
	var editButton = $('<span class="mw-addlisting noprint">')
		.css('cursor','pointer')
		.click(function() {
			var listingEntry = $(this).parent();
			ListingEditor.popupForm('add', listingEntry);
		}).append('&nbsp;[').append($('<a/>').text(ListingEditor.translateStr['add']).click( function(e) {
			e.preventDefault();
		})).append(']');
	$('.mw-h2section h2 span').each( function() {
		for (var key in ListingEditor.addListingSections) {
			var value = ListingEditor.addListingSections[key];
			var rgx = new RegExp('^'+value+'$','g');
			if($(this).text().match(rgx)!=null)
			{
				$(this).parent('h2').addClass('mw-addhere');
				$(this).closest('div.mw-h2section').children('h3').addClass('mw-addhere');
			}
		}
	});
	$('.mw-addhere').append(editButton);
};

ListingEditor.addEditButtons = function () {
	var editButton = $('<span class="vcard-edit-button noprint">')
		.css('cursor','pointer')
		.click(function() {
			var listingEntry = $(this).parent();
			ListingEditor.popupForm('edit', listingEntry);
		}).append('&nbsp;').append($('<a/>').text(ListingEditor.translateStr['edit']).click( function(e) {
			e.preventDefault();
		}));
	$('.listing-item').append( editButton );
};

ListingEditor.isInline = function (entry) {
	if (entry.parent('p').length == 0) return false;
	return true;
};

ListingEditor.findSectionNumber = function (entry) {
	var link = entry.find( '.mw-editsection a' ).attr( 'href' );
	if (link === undefined) link = entry.closest('div.mw-h2section').find( '.mw-editsection a' ).attr( 'href' );
	if (link != undefined) return link.split( '=' ).pop();
	return 0;
};

ListingEditor.findSectionType = function (entry) {
	var section = entry.closest('div.mw-h2section').children('h2').find('.mw-headline').text();
	for (var key in ListingEditor.addListingSections) {
		var value = ListingEditor.addListingSections[key];
		var rgx = new RegExp('^'+value+'$','g');
		if(section.match(rgx)) return key;
	}
	return ListingEditor.listingTypes.listing;
};

ListingEditor.replacements = {};

ListingEditor.getSectionText = function (number, entry) {
	var wikiText = $.ajax({
		url: mw.util.wikiScript(''),
		data: { title:mw.config.get('wgPageName'), action:'raw', section:number },
		async: false,
		cache: false, // required
		beforeSend: function () {
			var loading = $('<span/>');
			loading.attr('class','listing_loading');
			var img = $('<img/>');
			img.attr('src',ListingEditor.loadingIcon);
			loading.append(img);
			loading.prepend(' ');
			loading.insertAfter(entry);
		},
		complete: function() {
			$('.listing_loading').remove();
		},
	}).responseText;
	var comments = wikiText.match(/<!--[\s\S]*?-->/mig);
	if ( comments !== null ) {
		for(var i = 0; i < comments.length; i++ )
		{
			var comment = comments[i];
			var rep = '<<<COMMENT' + i + '>>>';
			wikiText = wikiText.replace(comment, rep);
			ListingEditor.replacements[rep] = comment;
		}
	}
	return wikiText;
};

ListingEditor.replaceSpecial = function ( str ) {
  return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
};

ListingEditor.getListingWikitextBraces = function (entry) {
	ListingEditor.sectionText = ListingEditor.sectionText.replace(/[^\S\n]+/g,' ');
	var parent = $(entry.parentsUntil('.mw-h2section').parent()[0]);
	var cnt = 0;
	parent.find('.listing-item').each( function(){
		if($(entry[0]).is($(this)))
		{
			return false;
		}
		cnt += 1;
	});
	var regex = []
	for(var key in ListingEditor.listingTypesRegex)
	{
		regex.push(ListingEditor.listingTypesRegex[key]);
	}
	var listingRegex = new RegExp("{{\\s*("+regex.join('|')+')','g');
	var matches = ListingEditor.sectionText.match(listingRegex);
	var string = matches[cnt];
	var parttxt = ListingEditor.sectionText;
	var index = 0;
	for(var i = 0; i < cnt; i++)
	{
		index += parttxt.indexOf(matches[i])+matches[i].length+1;
		parttxt = parttxt.slice(parttxt.indexOf(matches[i])+matches[i].length+1);
	}
	index += parttxt.indexOf(matches[cnt]);
	var curly = 2;
	var str1 = '', str2 = '';
	// search for open and close braces
	for (var i=index; i>0; i--) {
		if (ListingEditor.sectionText[i]=='}') ++curly;
		else if (ListingEditor.sectionText[i]=='{') --curly;
		if(curly == 0) {
			str1 = ListingEditor.sectionText.substr(i,index-i);
			break;
		}
	}
	if (string.indexOf('}}') < 0) curly = 2;
	var textLength = ListingEditor.sectionText.length;
	for (var j=index+string.length; j<textLength; j++) {
		if (ListingEditor.sectionText[j]=='{') ++curly;
		else if (ListingEditor.sectionText[j]=='}') --curly;          
		if (curly == 0) {
		   str2 = ListingEditor.sectionText.substr(index, j-index+1);
		   break;
		}
	}
	if (str2 === '') str2 = ListingEditor.sectionText.substr(index, textLength);
	string = str1 + str2;
	return $.trim(string);
};

ListingEditor.wikiTextToListing = function (string) {
	string = string.slice(0,-2);
	var type = 'listing';
	for(key in ListingEditor.listingTypesRegex)
	{
		var typeRegex = new RegExp('{{\\s*'+ListingEditor.listingTypesRegex[key],'g');
		var match = typeRegex.exec(string);
		if(match != null)
		{
			type = key;
		}
	}
	if(typeof ListingEditor.listingTypes[type.toLowerCase()] != "undefined") type = ListingEditor.listingTypes[type.toLowerCase()];
	var regex = []
	for(var key in ListingEditor.listingTypesRegex)
	{
		regex.push(ListingEditor.listingTypesRegex[key]);
	}
	var listingRegex = new RegExp("{{\\s*("+regex.join('|')+')','g');
	string = string.replace(listingRegex,'{{فهرست\u200cبندی| type=' + type);
	var listing = {};
	var lastKey;
	var OpenBracket = 0;
	var OpenSquareBrackets = 0;
	var listParams = [];
	var buff = '';
	for ( var i = 0; i < string.length; i++ )
	{
		var chr = string.charAt(i);
		if ( chr == '{' ) {
			OpenBracket++;
		} else if ( chr == '}' ) {
			OpenBracket--;
		} else if ( chr == '[' ) {
			OpenSquareBrackets++;
		} else if ( chr == ']' ) {
			OpenSquareBrackets--;
		}
		if ( chr == '|' && OpenBracket == 2 && OpenSquareBrackets === 0 ) {
			listParams.push(buff);
			buff = '';
		} else {
			buff += chr;
		}
	}
	if ( buff !== '' ) {
		listParams.push(buff);
		buff = '';
	}
	for (var j=1;j<listParams.length;j++) {
		var param = listParams[j];
		var index = param.indexOf('=');
		if (index > 0) {
			var key = $.trim(param.substr(0, index));
			var value = $.trim(param.substr(index+1));
			for(var key2 in ListingEditor.allFields)
			{
				var regx = new RegExp("^"+ListingEditor.allFields[key2]['regx']+"$")
				if(key.match(regx))
				{
					key = key2;
					break;
				}
			}
			listing[key] = value;
			lastKey = key;
		}
		else if (listing[lastKey].length) {
			listing[lastKey] += '|' + param;
		}
	}
	return listing;
};

ListingEditor.getListing = function (entry) {
	ListingEditor.listingText = ListingEditor.getListingWikitextBraces(entry);
	var listing = ListingEditor.wikiTextToListing(ListingEditor.listingText);
	return listing;
};

/*** Functions to handle form creation and editing ***/
ListingEditor.popupForm = function (mode, entry) {
  mw.loader.using( ['jquery.ui'], function () {
	var sectionType, listing;
	var sectionNumber = ListingEditor.findSectionNumber(entry);
	ListingEditor.inlineListing = ListingEditor.isInline(entry);
	ListingEditor.sectionText = ListingEditor.getSectionText(sectionNumber,entry);
	
	if (mode == 'add') {
		sectionType = ListingEditor.findSectionType(entry);
		listing = {};
		ListingEditor.sectionTitle = $($(entry.parent()[0]).find('h2 span')[0]).text();
	}
	else {
		sectionType = '';
		listing = ListingEditor.getListing(entry);
		ListingEditor.sectionTitle = $($(entry.parentsUntil('.mw-h2section').parent()[0]).find('h2 span')[0]).text();
	}

	var form = $(ListingEditor.createForm(mode, sectionType, listing));
	
	// modal form - must submit or cancel
	form.dialog({
		// IE different behavior
		drag: function(event, ui) {
			$(this).parent().css('right',
				(
					$(window).innerWidth() - parseInt($(this).parent().css('left')) - $(this).parent().width()
				)+'px'
			);
			$(this).parent().css('left','');
		},
		modal: true,
		height: 'auto',
		width: '800px',
		title: ListingEditor.translateStr[mode],
		buttons: [
			{   text: '؟', 
				id: 'listing-help',
				click: function() { window.open(ListingEditor.translateStr['help-page']);}},
			{text: ListingEditor.translateStr['cancel'], click: function() {$(this).dialog('destroy').remove()}},
			{   text: ListingEditor.translateStr['preview'], click: function() {
					if(ListingEditor.validateForm()) {
						ListingEditor.preview();
					}   
				}
			},
			{   text: ListingEditor.translateStr['submit'], click: function() {
					if(ListingEditor.validateForm()) {
						ListingEditor.formToText(mode, sectionNumber);
						$(this).dialog('close');
					}   
				}
			},
		],
		open: function() {
			$('.ui-dialog-buttonpane').append('<div style="width:320px;padding-top:0.8em;font-size:xx-small;">'+ListingEditor.LICENSE_TEXT+'</div>');
			if ($('#input-address').val() != '') {
				$('#geomap-link').attr('href', $('#geomap-link').attr('href') + '&location=' 
							+ encodeURIComponent($('#input-address').val()));
			}
			else if ($('#input-name').val() != '') {
				$('#geomap-link').attr('href', $('#geomap-link').attr('href') + '&location=' 
							+ encodeURIComponent($('#input-name').val()));
			}                
			$('#input-address').change( function () {
				var link = $('#geomap-link').attr('href');
				var index = link.indexOf('&location');
				if (index < 0) index = link.length;
				$('#geomap-link').attr('href', link.substr(0,index) + '&location='
					+ encodeURIComponent($('#input-address').val()));
			});
			// IE different behavior
			$(this).parent().css('right',
				(
					$(window).innerWidth() - parseInt($(this).parent().css('left')) - $(this).parent().width()
				)+'px'
			);
			$(this).parent().css('left','');
		},
		close: function() { $(this).dialog('destroy').remove()}
	});
  });
};

ListingEditor.buttonGenerator = function (callback, summaryText, replaceSummary, image, title, alt, idSuffix) {
	var button = $('<span>', { style: 'padding-right: 2px; cursor: pointer;' }).click(callback).append(
		$('<img>').attr({
			src: image,
			title: title,
			alt: alt
		}).on("error", function () {
			button.replaceWith($('<button>', { text: alt }).click(callback));
		})
	);
	return button;
};
		
ListingEditor.createForm = function (mode, type, listing) {
	var form = $('<form id="listing-editor">');
	
	var leftFields = $('<table id="left-fields" style="display: inline-flex;">').appendTo(form);
	var rightFields = $('<table id="right-fields" style="display: inline-flex;">').appendTo(form);
	$('<div style="clear:both">').appendTo(form);

	//create form according to fields
	for (var key in ListingEditor.allFields) {
		var keyvalue = ListingEditor.allFields[key];
		var row = $('<tr class="input-text">')
			.attr('id', 'div_' + key);
		var cell = $('<td/>').appendTo(row);
		var label = $('<label>').appendTo(cell)
			.text(keyvalue['label'])
			.attr('for', 'input-' + key);
		var cell = $('<td/>').appendTo(row);
		// input text for everything except content which gets textarea
		var parameter = key;

		if (key == 'type') {
			var subnode = $('<select id="option-type">').appendTo(cell);
			for (var n in ListingEditor.listingTypes) {
				var option = $('<option value="'+ListingEditor.listingTypes[n]+'">');
				option.text(ListingEditor.listingTypesLabels[n]).appendTo(subnode);
			}
			if (mode == 'add') {
				subnode.val(ListingEditor.listingTypes[type]);
				listing[parameter] = ListingEditor.listingTypes[type];
			}
		}
		else if (key != 'content') {
			var subnode = $('<input type="text" style="width: 100%;">').appendTo(cell);
		}
		else {
			var subnode = $('<textarea style="width: 100%;">').appendTo(cell)
				.attr('rows', keyvalue['rows']);
		}

		subnode.attr('placeholder', keyvalue['tip'])
			   .attr('id', 'input-' + key);

		if (listing[parameter]) {
			subnode.val(listing[parameter]);
		}

		// customise hiding parameters
		if (listing[ListingEditor.allFields['type']['parameter']] == ListingEditor.listingTypes['sleep'] && key == 'hours') row.hide();
		if (key == 'checkin' || key == 'checkout' || key == 'fax' || key == 'image') row.hide();
		
		// some special form features
		if (key == 'type' && mode == 'edit') {
			var closedSpan = $('<span id="span_closed">');
			var closedLabel = $('<label for="input-closed">').appendTo(closedSpan)
				.text(ListingEditor.translateStr['closed']);
			var closedInput = $('<input type="checkbox">').appendTo(closedSpan)
				.attr('id', 'input-closed');
			cell.append(closedSpan);
		}
		if (key == 'price') {
			var currencySpan = $('<span id="span_currency">');
			for (var i=0; i < ListingEditor.currencySigns.length; i++) {
				var currencyButton = $('<span class="currency-signs">')
					.html('&#32;<u><a href="javascript:">'+ListingEditor.currencySigns[i]+'</a></u>' )
					.click(function() {
						var caretPos = document.getElementById('input-price').selectionStart;
						var price = $('#input-price').val();
						$('#input-price').val(price.substring(0, caretPos)
									+ $(this).find('a').text() + price.substring(caretPos) );
					});
				currencySpan.append(currencyButton);
			}
			cell.append(currencySpan);
		}
		if (key == 'lat') {
			var latlngStr = '?';
			if ($('#geodata').length) {
				var latlng = $('#geodata').text().split('; ');
				latlngStr += 'lat='+latlng[0]+'&lon='+latlng[1]+'&zoom=15';
			}
			cell.append('&nbsp;<u><a id="geomap-link" target="_blank" '
				+'href="http://maps.wikivoyage-ev.org/w/geomap.php'+latlngStr+'">'
				+ListingEditor.translateStr['geomap']+'</a></u>');
		}

		if (key == 'content') {
			var tb = $('<table/>').css('width','100%');
			$(row.children()[0]).css('width','1px');
			tb.append(row);
			form.append(tb);
		}
		else if (ListingEditor.allFields[key]['right'] == true) {
			rightFields.append(row);
		}
		else {
			leftFields.append(row);
		}
	}
	if(typeof persianWikiTools != 'undefined' && typeof persianWikiTools.superToolMove != 'undefined')
	{
		form.find('input[type=text]').each(function(){
			var me = $(this);
			me.css('width','88%');
			var btn = ListingEditor.buttonGenerator(
				function () {
					var me = $(this);
					var target = $(me.parent().children()[0]);
					target.val(persianWikiTools.superToolMove(target.val()));
				},
				'تصحیح خودکار',
				false,
				ListingEditor.ToolIcon,
				'تصحیح خودکار',
				'تصحیح خودکار',
				'super-tool'
			);
			btn.insertAfter(me);
		});
		form.find('textarea').each(function(){
			var me = $(this);
			me.parent().parent().append($('<td/>').css('width','1px'));
			var btn = ListingEditor.buttonGenerator(
				function () {
					var me = $(this);
					var target = $($(me.parent().parent().children()[1]).children()[0]);
					target.val(persianWikiTools.superToolMove(target.val()));
				},
				'تصحیح خودکار',
				false,
				ListingEditor.ToolIcon,
				'تصحیح خودکار',
				'تصحیح خودکار',
				'super-tool'
			);
			$(me.parent().parent().children()[2]).append(btn);
		});
	}
	//form.append($('<div style="text-align: center; font-size: 120%; padding: 10px; margin: 10px; color: rgb(255, 20, 28); border: 1px solid rgb(255, 254, 41); background-color: rgb(249, 254, 201); border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px;">قبل از ذخیره کردن، بر روی دکمهٔ <img alt="تصحیح خودکار" title="تصحیح خودکار" src="' + ListingEditor.ToolIcon + '"> در هر بخش کلیک کنید تا تصحیحات لازم انجام شود.</div>'));
	if (mw.config.get('wgUserName') == null)
	form.append($('<div style="background-color: rgb(255, 228, 225); color: rgb(0, 0, 0); border: 1px solid rgb(234, 164, 215); border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px; padding: 10px; margin: 10px;"><span style="color: #FF0000;">توجه:</span> شما با نام کاربری وارد نشده\u200cاید، ویرایش شما با آی\u200cپی شما ثبت می\u200cشود، می\u200cتوانید ابتدا <a href="//fa.wikivoyage.org/wiki/Special:Login/signup" target="_blank">ثبت نام کنید</a>، اگر در حال حاضر در ویکی\u200cسفر و یا در دیگر پروژه\u200cهای بنیاد ویکی\u200cمدیا حساب کاربری دارید می\u200cتوانید به سیستم <a href="//fa.wikivoyage.org/wiki/Special:Login" target="_blank">وارد شوید</a>، برای اطلاعات بیشتر <a href="//fa.wikivoyage.org/wiki/%D9%88%DB%8C%DA%A9%DB%8C%E2%80%8C%D8%B3%D9%81%D8%B1:%D9%88%D8%B1%D9%88%D8%AF_%DB%8C%DA%A9%D9%BE%D8%A7%D8%B1%DA%86%D9%87" target="_blank">ویکی\u200cسفر:ورود یکپارچه</a> را بخوانید.</div>'));
	return form;
};

ListingEditor.validateForm = function () {
	//TODO more form validation?
	if ($('#input-name').val() == '' && $('#input-address').val() == '' && $('#input-alt').val() == '') {
		alert(ListingEditor.translateStr['validationalert']);
		return false;
	}
	$('#input-content').val($.trim($('#input-content').val()).replace(/\n+/g, ' '));
	var webRegex = new RegExp('^https?://', 'i');
	var url = $('#input-url').val();
	if (!webRegex.test(url) && url != '') {
		$('#input-url').val('http://' + url);
	}
	return true;
};

ListingEditor.upperCaseFirst = function (str) {
	str = str.toLowerCase().replace(/\b[a-z]/g, function(letter) {
		return letter.toUpperCase();
	});
	return str;
};

ListingEditor.preview = function () {
	var listing = {};
	for ( var key in ListingEditor.allFields ) {
		var parameter = ListingEditor.allFields[key]['parameter'];
		listing[parameter]= $("#input-"+key).val();
	}

	if (listing[ListingEditor.allFields['type']['parameter']] != ListingEditor.listingTypes.sleep) {
		listing[ListingEditor.allFields['checkin']['parameter']] = null;
		listing[ListingEditor.allFields['checkout']['parameter']] = null;
	}
	else {
		listing[ListingEditor.allFields['hours']['parameter']] = null;
	}

	var text = ListingEditor.listingToStr(listing);
	$.ajax({
		url: mw.config.get('wgScriptPath') + '/api.php?' + $.param({
			action: 'parse',
			prop: 'text',
			contentmodel: 'wikitext',
			format: 'json',
			'text': text,
		}),
		beforeSend: function (xhr) {
			if($('#listing-editor .preview-section').length == 0)
			{
				$('#listing-editor').append($('<hr/>'));
				$('#listing-editor').append($('<h5/>').text(ListingEditor.translateStr['preview']));
				$('#listing-editor').append($('<div/>')
					.attr('class','preview-section')
					.append($('<div/>')
						.attr('class','preview-content')
					)
					.css('background-color','#fff')
					.css('border-radius','10px')
					.css('-moz-border-radius','10px')
					.css('-webkit-border-radius','10px')
					.css('padding','10px')
				);
			}
			var loading = $('<span/>');
			loading.attr('class','listing_previewing');
			var img = $('<img/>');
			img.attr('src',ListingEditor.loadingIcon);
			loading.append(img);
			loading.prepend(' ');
			$('#listing-editor .preview-section').prepend(loading);
		},
		error: function(jqXHR, txt){
			$('#listing-editor .preview-section .listing_previewing').remove();
		},
		success: function (data) {
			$('#listing-editor .preview-section .listing_previewing').remove();
			$('#listing-editor .preview-section .preview-content').html(data.parse.text['*']);
		},
	});
};

ListingEditor.formToText = function (mode, number) {
	var listing = {};
	for ( var key in ListingEditor.allFields ) {
		var parameter = ListingEditor.allFields[key]['parameter'];
		listing[parameter]= $("#input-"+key).val();
	}

	if (listing[ListingEditor.allFields['type']['parameter']] != ListingEditor.listingTypes.sleep) {
		listing[ListingEditor.allFields['checkin']['parameter']] = null;
		listing[ListingEditor.allFields['checkout']['parameter']] = null;
	}
	else {
		listing[ListingEditor.allFields['hours']['parameter']] = null;
	}

	var text = ListingEditor.listingToStr(listing);

	var summary = '/* ' + ListingEditor.sectionTitle + ' */ ';
	if (mode == 'add') {
		summary += ListingEditor.translateStr['added'];
		var index = ListingEditor.sectionText.indexOf('===');
		if ( index == 0 ) {
			index = ListingEditor.sectionText.indexOf('====');
		}
		
		if ( index > 0 ) {
			ListingEditor.sectionText = ListingEditor.sectionText.substr(0, index) + '* ' + text 
					+ '\n' + ListingEditor.sectionText.substr(index);
		}
		else {
			ListingEditor.sectionText += '\n'+ '* ' +text;
		}
	}
	else {
		if ($('#input-closed').is(':checked')) {
			text = '';
			summary += ListingEditor.translateStr['removed'];
			var listRegex = new RegExp('\\n\\*+\\s?'+ListingEditor.replaceSpecial(ListingEditor.listingText));
			ListingEditor.sectionText = ListingEditor.sectionText.replace(listRegex, ListingEditor.listingText);
		}
		else {
			summary += ListingEditor.translateStr['updated'];
		}        
		ListingEditor.sectionText = ListingEditor.sectionText.replace(ListingEditor.listingText, text);
	}
	summary += $("#input-name").val();
	ListingEditor.saveForm(summary, ListingEditor.sectionText, number, '', '');
	return;
};

ListingEditor.savingForm = function () {
	var progress = $('<div id="progress-dialog">'+ListingEditor.translateStr['saving']+'...</div>');
	progress.dialog({
		modal: true,
		height: 100,
		width: 300,
		title: ''
	});
	$(".ui-dialog-titlebar").hide();
};

ListingEditor.saveForm = function (summary, content, number, cid, answer) {
	for (var key in ListingEditor.replacements) {
		var val = ListingEditor.replacements[key];
		content = content.replace(key, val);
	}
	ListingEditor.replacements = {};
	$.ajax( {
		url: mw.util.wikiScript( 'api' ),
		data: {
			'format': 'json',
			'action': 'edit',
			'title': mw.config.get('wgPageName'),
			'section': number,
			'token': mw.user.tokens.get( 'csrfToken' ),
			'text': content,
			'summary': summary,
			'captchaid': cid,
			'captchaword': answer
		},
		type: 'POST',
		datatype: 'json',
		success: function( data ) {
			if ( data && data.edit && data.edit.result == 'Success' ) {
			  window.location.reload(); // reload page if edit was successful
			} else if ( data && data.error ) {
				alert( 'خطا: API کد مقابل را برگرداند "' + data.error.code + '": ' + data.error.info );
			} else if ( data && data.edit.spamblacklist ) {
				alert( 'خطا: "'+ data.edit.spamblacklist + '" در فهرست سیاه قرار دارد' );
				$('#progress-dialog').dialog('destroy').remove();
			} else if ( data && data.edit.captcha ) {
				var captcha = $('<div id="captcha-dialog">').text(ListingEditor.translateStr['external-links']);
				var image = $('<img class="fancycaptcha-image">')
					.attr('src', data.edit.captcha.url)
					.appendTo(captcha);
				var label = $('<label for="input-captcha">').text(ListingEditor.translateStr['enter-captcha']).appendTo(captcha);
				var input = $('<input id="input-captcha" type="text">').appendTo(captcha);
				captcha.dialog({
					title: ListingEditor.translateStr['enter-captcha'],
					buttons: [
						{   text: ListingEditor.translateStr['submit'], click: function() {
								ListingEditor.saveForm(summary, content, number, data.edit.captcha.id, $('#input-captcha').val());
								}
						},
						{   text: ListingEditor.translateStr['cancel'], click: function() {
								$(this).dialog('destroy').remove();
								$('#progress-dialog').dialog('destroy').remove();
						}}
					]
				});
			} else {
				alert( 'خطا: نتیجه نامعلوم از API دریافت شد.' );
			}
		},
		error: function( xhr ) {
			alert( 'خطا: درخواست ناموفق بود.' );
		}
	} )
	ListingEditor.savingForm();
};

ListingEditor.listingToStr = function (listing) {
	var saveStr = '{{'+listing[ListingEditor.allFields['type']['parameter']]; 
	if (!ListingEditor.inlineListing && ListingEditor.allFields['type']['newline']) saveStr += '\n';
	for ( var key in ListingEditor.allFields ) {
		var parameter = ListingEditor.allFields[key]['parameter'];
		if (key != 'type' && listing[parameter] != null) {
			if (ListingEditor.inlineListing) {
				if (listing[parameter] != '') {
					saveStr += ' | ' + parameter + '=' +listing[parameter];
				}
			}
			else {
				if (key != 'image' || listing[ListingEditor.allFields['image']['parameter']] != '') { 
					saveStr +='| '+parameter+ '=' + listing[parameter];
				}
				if (ListingEditor.allFields[key]['newline']) {
					saveStr += '\n';
				}
				else {
					saveStr += ' ';
				}
			}
		}
	}
	saveStr += '}}';
	return saveStr;
};
 
ListingEditor.init = function() {

    var namespace = mw.config.get( 'wgNamespaceNumber' );
    if (namespace != 0 && namespace != 2 && namespace != 4) {
        return;
    }
    
    if ( mw.config.get('wgAction') != 'view' || $('#mw-revision-info').length 
            || mw.config.get('wgCurRevisionId') != mw.config.get('wgRevisionId')
            || $('#ca-viewsource').length ) {
        return;
    }
    
    ListingEditor.wrapContent();
    ListingEditor.addListingButtons();
    ListingEditor.addEditButtons();
 
};

// only run on supported skins
// (on mobile this breaks section collapsing)
if ( mw.config.get( 'skin' ) !== 'minerva' ) {
	$(ListingEditor.init);
}