గమనిక: భద్రపరచిన తర్వాత, మార్పులను చూడాలంటే మీ విహారిణి కోశాన్ని తీసేయాల్సిరావచ్చు.

  • ఫైర్‌ఫాక్స్‌ / సఫారి: Shift మీటని నొక్కిపట్టి Reloadని నొక్కండి లేదా Ctrl-F5 గానీ Ctrl-R (మాకింటోషులో ⌘-Shift-R) గానీ నొక్కండి
  • గూగుల్ క్రోమ్: Ctrl-Shift-R (మాక్ లో ⌘-Shift-R) నొక్కండి
  • ఇంటర్నెట్ ఎక్ప్లోరర్/ఎడ్జి: Ctrl ను నొక్కిపట్టి Refresh నొక్కండి లేదా Ctrl-F5 నొక్కండి.
  • ఒపేరా:* Ctrl-F5 నొక్కండి.
/***********************************************************************
 * bookMaker.js
 *
 * A tool to create page lists for use in creating books for export
 * The book lists are compatible with PediaPress and WSexport and are
 * designed to be more flexible and suitable for the Wikisource content
 * than the PediaPress extension.
 *
 * This script is loaded by [[MediaWiki:Gadget-BookMakerLoader.js]]
 * on demand to reduce network load if the tool isn't used
 *
 *
 * Maintainer: Inductiveload
 *
 * V 0.1  2012-04-20 - Initial release
 *        2012-06-07 - Fixed various bugs, add "use title as name"
 *                     button. Add a delay to the name-existance code to
 *                     prevent excessive queries.
 *        2012-06-09 - Many small tweaks, now stores and loads pending
 *                     pages too, add add page from input
 *        2012-06-17 - More tweaks, fix formatting issues (use tables)
 *                     Add QR code, break out some interface messages,
 *                     Use strict enabled.
 *        2012-10-29 - Update url of EPUB to labs (from TS) in 2 places
 *
 **********************************************************************/

(function () {
	"use strict";


window.BookMaker.prototype.initialise = function () {
	this.bookdata = {};
	this.initialised = true;
	this.bookTemplateName = 'saved book 2';
	this.helpLocation = 'Help:Gadget-BookMaker';
	this.localstorage_minimised_name = 'wikisource_bookmakerminimised';
	this.central_bookstore = 'Wikisource:Books/';
	this.user_bookstore = 'User:' + mw.config.get('wgUserName') + '/Books/';
	this.qrGenerator = 'http://toolserver.org/~inductiveload/wmf-qrcode/wmfqr.php';

	this.imgs = {
		'x_red': '//upload.wikimedia.org/wikipedia/commons/e/e9/Silk_cross.png',
		'tag_off': '//upload.wikimedia.org/wikipedia/commons/8/8c/Tag_grey.png',
		'tag_on': '//upload.wikimedia.org/wikipedia/commons/d/dd/Tag_red.png',
		'go_to_page': '//upload.wikimedia.org/wikipedia/commons/9/92/Book_next.png',
		'editBook': '//upload.wikimedia.org/wikipedia/commons/6/69/Book_edit.png'
	};
	
	this.exportURLs = {'EPUB with images': '//tools.wmflabs.org/wsexport/tool/book.php?lang=en&page=$1&format=epub'.replace('$1', mw.config.get('wgPageName')),
		'EPUB without images': '//tools.wmflabs.org/wsexport/tool/book.php?images=false&lang=en&page=$1&format=epub'.replace('$1', mw.config.get('wgPageName')),
		'This page':  window.location
	};

	//messages
	this.msg = {
		'yourShelf': 'Your bookshelf',
		'startBookMaker': 'Edit with BookMaker',
		'creation_summary': 'Creating book with the Book Maker tool.',
		'addPages': 'Add pages',
		'closeWarning': 'If you close the Book Maker, you will lose any unsaved data.',
		'areYouSure': 'Are you sure?',
		'closePortlet': 'Close book creator',
		'closeButton': 'Close',
		'minimise': 'Minimise',
		'maximise': 'Maximise',
		'help': 'Help',
		'save': 'Save book',
		'saveHint': 'Save this book to the title selected above.',
		'enterBookLocation': 'Enter the location of the book you want to load',
		'getQRCodes': 'Scan the relevant QR code below with your mobile device to get a direct link to the book',
		'qrCodeTitle': 'QR codes',
		'chooseLocation': 'Choose the location to save your book',
		'bookPagePrefix': 'Book page prefix',
		'useThisPagesTitle': "Use this page's title",
		'clearAll': 'Clear all',
		'pendingTitle': 'Pending pages',
		'existingTitle': 'Pages in book',
		'getThisPage': 'This page',
		'getThisPageLinks': 'From links on this page',
		'getThisPageSubpages': 'From subpages of this page',
		'getSavedVersion': 'From saved version of this book',
		'getSavedOther': 'From a saved book',
		'getOtherPage': 'Another page',
		'getCategory': 'From a category',
		'manualMetadata': 'Manual metadata',
		'useThisPagesMetadata': "Use this page's metadata",
		'metadataTitle': 'Title',
		'metadataAuthor': 'Author',
		'metadataTranslator': 'Translator',
		'metadataDate': 'Date',
		'replaceAppend': 'Replace or append to the current pending pages in BookMaker?',
		'replaceAppendTitle': 'Replace or append?',
		'enterCategory': 'Enter the category you want to add page from',
		'noCategoryPagesFound': 'No members of that category found, please check your spelling and try again',
		'category': 'Category',
		'noPageFound': 'No page found, please check your spelling and try again',
		'enterPage': 'Enter the location of the page you want to add',
		'loadPageTitle': 'Load an existing page',
		'bookNotLoaded': 'The book at $1 could not be loaded',
		'bookNotLoadedTitle': 'Book not loaded',
		'missingMetadata': 'There is missing metadata (such as title and author) attached to this book. You can add metadata by clicking a "tag" icon on a page to use that page as a source, or by filling it in manually. Continue, or cancel and add some?',
		'emptyBook': 'There are no pages in the book. Please add some before trying to save', 
		'emptyBookTitle': 'Book is empty!'
	};

	this.add_styles_to_page();
};

window.BookMaker.prototype.add_styles_to_page = function () {
	$('<style type="text/css" />').appendTo('head')
		.append('\
.ui-dialog {position: fixed;}\
#bookmaker { position:relative; background-color:#E0E9EA; border:1px solid #294584; padding:2em; margin-bottom:1em; }\
book_editor {clear:both;}\
#book_nameselection td {vertical-align:top; padding-right:1em;}\
#book_input_bookname {width:100%}\
#book_displaycontrols { position:absolute; right:5px; top:5px; }\
#book_pagelist { margin:auto; height:100%; clear:both;}\
#book_pagemanip {width:6em; float:left; margin-top:2em; text-align:center}\
.book_inputpagemanip { width:4em;}\
#book_pagelist_table {width:100%;}\
.book_pagelist_container {width:50%; vertical-align:top;}\
.book_pagelist { list-style:none; background-color:white; border:1px solid #444444; margin-left:0; width:100%; min-height:5em; max-height:40em; overflow-y:scroll;}\
.book_pagelist_title {font-weight:bold;}\
.book_pagelist_title > .book_clearall {float:right; color:red; cursor:pointer;}\
.book_displaycontrol { float:right; cursor:pointer; margin-right:10px; top:5px}\
#book_metadatafields {text-align:right; float:left;}\
.book_manualmetadata {width:25em}\
#book_commitcontrols {clear:both;}\
.book_pageitem {list-style-type:none; cursor:pointer;}\
.book_pageitem.book_mainitemsel {font-weight:bold;}\
.pageitem_sel { background-color:#DDDDDD;}\
.pageitem_sel_delete { background-color:#FF86A2;}\
#book_pagelistexist .book_tagmainitem { display:auto;}\
#book_pagelistpending .book_tagmainitem { display:none;}\
.book_updated {border:1px solid #00FF00 !important;}\
.book_pageitemtitle {padding-left:0.5em; }\
.qr_code_container {overflow-y:scroll !important; max-height:400px;}\
');
};

window.BookMaker.prototype.close_bookmaker = function () {
	//Close the bookmaker and remove all data
	var myself = this;

	var dialog = $('<div>')
		.html(myself.msg.closeWarning)
		.dialog({
			title: myself.msg.areYouSure,
			modal: true,
			buttons: {
				"Cancel": function () {
					$(this).dialog("close");
				},
				"Close": function () {
					$(this).dialog("close");
					localStorage.removeItem(myself.localstorage_name);
					$('#bookmaker').remove();
					myself.wantToStart = false;
					myself.add_create_book_link();
				}
			}
		});
};

window.BookMaker.prototype.toggle_mimimise_bookmaker = function () {
	this.minimised = !this.minimised;
	this.minimised?this.minimise():this.maximise();
};

window.BookMaker.prototype.minimise = function () {
	$('#book_nameselection,#book_editor').css('display', 'none');
	$('#book_minimisebutton').text(this.msg.maximise);
	localStorage.setItem(this.localstorage_minimised_name, '1'); //ls stores strings
};

window.BookMaker.prototype.maximise = function () {
	$('#book_nameselection,#book_editor').css('display', '');
	$('#book_minimisebutton').text(this.msg.minimise);
	localStorage.setItem(this.localstorage_minimised_name, '0');
};

window.BookMaker.prototype.start_bookmaker = function () {
	var myself = this;
	if ($('#bookmaker').length > 0) { return; }

	if (!this.initialised) {
		this.initialise(); //set up constants
	}

	$('#' + this.portletID + ' > a').text(this.msg.closePortlet).parent().off('click').click(function () {
		myself.close_bookmaker();
	});

	this.bookmaker = $('<div>', {id: "bookmaker"});

	var prefixinput = $('<td><span>' + this.msg.bookPagePrefix + ':</span><br>\
<input type="radio" class="book_namecontrol" id="book_bookdir_user" name="book-prefix" value="' + this.user_bookstore + '" checked="checked"/>' + this.generate_prefix_link(this.user_bookstore)[0].outerHTML + '<br />\
<input type="radio" class="book_namecontrol" id="book_bookdir_central" name="book-prefix" value="' + this.central_bookstore + '" />' + this.generate_prefix_link(this.central_bookstore)[0].outerHTML + '\
</td>');

	var namecontrol = $('<input>', {
		'type': 'text',
		'class': 'book_namecontrol',
		'id': 'book_input_bookname',
		'name': 'book_input_bookname',
		'value': this.guess_title_from_page()
	});
	var usetitleasname = $('<input>', {
		'type': 'button',
		'id': 'book_usetitleasname',
		'value': this.msg.useThisPagesTitle
	}).click(function () {
		namecontrol.val(myself.guess_title_from_page());
		myself.set_book_name();
	});

	var nameinput = $('<td>').append('<span>' + this.msg.chooseLocation + ':</span>', '<br>', namecontrol, '<br>', usetitleasname);

	var booknamenotice = $('<td id="book_namenotice">\
<div style="float:left; padding-right:3em">Editing book: <a id="book_fullocation" href="#"><span id="book_bookdirectory"></span><span id="book_bookname"></span></a></div>\
</td>');

	var booknameselection = $('<table>', {id:"book_nameselection"}).append($('<tr>').append(prefixinput, nameinput, booknamenotice));

	this.book_editor = $('<div>', {id:"book_editor"});

	var get_pages_buttons = $('<hr><div id="book_getpages">\
<table><tr>\
<td style="width:7em;">\
' + this.msg.addPages + '\
</td><td> \
<input type="button" class="book_addpagebutton" id="input_addthispage" name="input_addthispage" value="' + this.msg.getThisPage + '" onclick="BookMakerInstance.add_this_page()" />\
<input type="button" class="book_addpagebutton" id="input_getfromcurrentpage" name="input_getfromcurrentpage" value="' + this.msg.getThisPageLinks + '" onclick="BookMakerInstance.get_pages_from_current()" />\
<input type="button" class="book_addpagebutton" id="input_getfromsubpages" name="input_getfromsubpages" value="' + this.msg.getThisPageSubpages + '" onclick="BookMakerInstance.get_pages_from_subpage()" />\
<input type="button" class="book_addpagebutton" id="input_getfromsaved" name="input_getfromsaved" value="' + this.msg.getSavedVersion + '" onclick="BookMakerInstance.get_pages_from_saved_book()" />\
<input type="button" class="book_addpagebutton" id="input_getfromexisting" name="input_getfromexisting" value="' + this.msg.getSavedOther + '..." onclick="BookMakerInstance.get_pages_from_existing_book()" />\
<input type="button" class="book_addpagebutton" id="input_getfrominput" name="input_getfrominput" value="' + this.msg.getOtherPage + '..." onclick="BookMakerInstance.get_page_from_input()" />\
<input type="button" class="book_addpagebutton" id="input_getfromcategory" name="input_getfromcategory" value="' + this.msg.getCategory + '..." onclick="BookMakerInstance.get_page_from_category()" />\
</td></tr></table></div>');

	var metadata_area = $('<div id="book_metadatainput"><hr><div style="float:left;">\
' + this.msg.manualMetadata +': \
<input type="checkbox" id="input_manualmetadata" name="input_manualmetadata" value="" onclick="BookMakerInstance.select_metadata_mode()" /><br>\
<input type="button"  id="input_metadatafromthispage" name="input_metadatafromthispage" disabled=disabled value="' + this.msg.useThisPagesMetadata +'" onclick="BookMakerInstance.get_metadata_from_this_page()" />\
</div><div id="book_metadatafields">\
' + this.msg.metadataTitle +':\
<input class="book_manualmetadata" type="text" id="input_metadatatitle" name="input_metadatatitle" value="" disabled /><br>\
' + this.msg.metadataAuthor +':\
<input class="book_manualmetadata" type="text" id="input_metadataauthor" name="input_metadataauthor" value="" disabled /><br>\
' + this.msg.metadataTranslator +':\
<input class="book_manualmetadata" type="text" id="input_metadatatranslator" name="input_metadatatranslator" value="" disabled /><br>\
' + this.msg.metadataDate +':\
<input class="book_manualmetadata" type="text" id="input_metadatadate" name="input_metadatadate" value="" disabled /><br>\
<div></div>');

	var pages_pending = $('<td class="book_pagelist_container"><div class="book_pagelist_title">' + this.msg.pendingTitle +': <span class="book_clearall" onclick="BookMakerInstance.clear_list(\'book_pagelistpending\')">[' + this.msg.clearAll + ']</span></div><ul id="book_pagelistpending" class="book_pagelist"></ul></td>');
	var pages_exist = $('<td class="book_pagelist_container"><div class="book_pagelist_title"">' + this.msg.existingTitle +': <span class="book_clearall" onclick="BookMakerInstance.clear_list(\'book_pagelistexist\')">[' + this.msg.clearAll + ']</span></div><ul id="book_pagelistexist" class="book_pagelist"></ul></td>');

	var pages_manipulation = $('<td id="book_pagemanip" >\
<input type="button" class="book_inputpagemanip" id="input_move_right_all" name="input_move_right_all" value=">>" onclick="BookMakerInstance.move_page(\'right_all\')" />\
<input type="button" class="book_inputpagemanip" id="input_move_left_all" name="input_move_left_all" value="<<" onclick="BookMakerInstance.move_page(\'left_all\')" />\
</td>');

	var commit_controls = $('<div id="book_commitcontrols"><hr>\
<input type="button" id="book_commitbook" name="book_commitbook" value="' + this.msg.save +'" title="' + this.msg.saveHint + '" onclick="BookMakerInstance.save_book()" />\
</div>');

	var page_list_table = $('<table>', {id: "book_pagelist_table"}).append($('<tr>').append(pages_pending, pages_manipulation, pages_exist));
	var page_list = $('<div style="clear:both;">').append('<hr>', page_list_table);

	this.book_editor.append(get_pages_buttons, metadata_area, page_list, commit_controls);

	var closeButton = $('<a>', {'class': "book_displaycontrol", id: "book_closebutton", text: this.msg.closeButton}).click(function () {
		myself.close_bookmaker();
	});

	var minimiseButton = $('<a>', {'class': "book_displaycontrol", id: "book_minimisebutton", text: this.msg.minimise}).click(function () {
		myself.toggle_mimimise_bookmaker();
	});

	var helpButton = $('<a>', {'class': "book_displaycontrol", id: "book_minimisebutton", text: this.msg.help,
		href: mw.config.get('wgArticlePath').replace('$1', this.helpLocation)});

	var displayControls = $('<div>', {id: "book_displaycontrols"}).append(closeButton, minimiseButton, helpButton);

	$('#bodyContent').prepend(this.bookmaker.append(displayControls, booknameselection, this.book_editor));

	$("#book_pagelistpending, #book_pagelistexist").sortable({
		connectWith: ".book_pagelist",
		change: function (e) {
			myself.accept_page();
		},
		receive: function (e) {
			myself.accept_page();
		} //have to have both, otherwise lists tags aren't updated when move to other list
	});

	$('.book_manualmetadata,#input_manualmetadata').css({
		'padding-right': '1em'
	}).change(function () {
		myself.store_book();
	});

	$('.book_namecontrol').on('input keyup', function () {
		var $this = $(this);
		var delay = 1000; // 2 seconds delay after last input
		clearTimeout($this.data('timer'));
		$this.data('timer', setTimeout(function () {
			$this.removeData('timer');

			myself.set_book_name();
		}, delay));
	});

	//load minimisation state
	if (this.localstorage_minimised_name in localStorage){
		this.minimised = (localStorage.getItem(this.localstorage_minimised_name) === '1');
	} else {
		this.minimised = false;
	}

	if (this.minimised){
		this.minimise();
	}

	if ($('#' + this.bookTemplateID).length > 0) {
		this.load_existing_book(mw.config.get('wgPageName'));
	} else {
		this.retrieve_book();
	}
	//scroll to panel
	$('html,body').stop().animate({scrollTop: $('#bookmaker').position().top + mw.util.$content.position().top}, 500);
};

window.BookMaker.prototype.guess_title_from_page = function () {

	//strip bookstore prefixes
	if (mw.config.get('wgPageName').indexOf(this.user_bookstore) == [0]) {
		var title = mw.config.get('wgPageName').replace(this.user_bookstore, '');
	} else if (mw.config.get('wgPageName').indexOf(this.central_bookstore) == [0]) {
		var title = mw.config.get('wgPageName').replace(this.central_bookstore, '');
	} else {
		var title = mw.config.get('wgTitle');
	}

	title = title.replace(/_/g, ' ');
	return title;
};

window.BookMaker.prototype.set_book_name = function (use_stored_name) {
	var directory, name;
	if (use_stored_name) {

		//set radio buttons to match data
		if ($('#book_bookdirectory').text() == $('#book_bookdir_user').val()) {
			$('#book_bookdir_user').prop('checked', true);
		} else {
			$('#book_bookdir_central').prop('checked', true);
		}

		$('#book_input_bookname').val($('#book_bookname').text().replace(/_/g, ' '));

		directory = $('#book_bookdirectory').text();
		name = $('#book_bookname').text();

	} else { //get data from the user fields
		directory = $('input:radio[name=book-prefix]:checked').val().replace(/_/g, ' ');
		name = $('#book_input_bookname').val().replace(/_/g, ' ');

		if (name) {
			$('#book_bookdirectory').text(directory);
			$('#book_bookname').text(name);
		}
	}

	if (!use_stored_name) {
		this.store_book();
	}


	var update_bookname_link = function (exists) {
		if (exists) {
			$('#book_fullocation').removeClass('new');
			$('#input_getfromsaved').prop('disabled', false);
		} else {
			$('#book_fullocation').addClass('new');
			$('#input_getfromsaved').prop('disabled', true);
		}
	};

	this.check_title_exists(directory + name, update_bookname_link);
};

window.BookMaker.prototype.check_title_exists = function (title, callback) {

	if (!title){
		callback(false);
	}

	//check if a page exists, executing callback with the result,
	var myself = this;

	//set the link location
	$('#book_fullocation').attr('href', mw.config.get('wgArticlePath').replace('$1', title));

	var query = mw.config.get('wgServer') + wgScriptPath + '/api.php?action=query&prop=info|revisions&intoken=edit&titles=' + title + '&format=json';

	$.ajax({
		url: query,
		success: function (data) {

			var pageID;
			var pages = data['query']['pages'];
			for (var idloop in pages) {
				pageID = idloop;
				break;
			}


			var exists = (pageID > 0);
			var edittoken = pages[pageID]['edittoken'];
			var currtimestamp = pages[pageID]['starttimestamp'];

			var timestamp = null;
			var user = null;

			if (exists) {
				timestamp = new Date(pages[pageID]['revisions'][0]['timestamp']);
				user = pages[pageID]['revisions'][0]['user'];
			}

			callback(exists, edittoken, timestamp, user, currtimestamp);
		}
	});
};

window.BookMaker.prototype.create_pageitem = function (title, highlight) {
	var myself = this;
	//don't add if we already have the title
	if ($('.book_pageitemtitle').filter(function () {
		return ($(this).text() == title);
	}).length) {
		return null;
	}

	var page = $('<li class="book_pageitem"></li>');

	var delete_icon = $('<img class="book_deleteitem" src="' + this.imgs['x_red'] + '"/>').click(function (e) {
		if ($(this).parents('#book_pagelistpending').length > 0) {
			$(this).parent().remove();
		} else if ($(this).parents('#book_pagelistexist').length > 0) {
			$('#book_pagelistpending').append($(this).parent());
		}

		myself.store_book();
	});

	var tag_icon = $('<img class="book_tagmainitem" src="' + this.imgs['tag_off'] + '"/>').click(function (e) {
		var was_selected = $(this).parent().hasClass('book_mainitemsel');

		$('.book_tagmainitem').attr('src', myself.imgs['tag_off']).parent().removeClass('book_mainitemsel');

		if (!was_selected) {
			$(this).attr('src', myself.imgs['tag_on']).parent().addClass('book_mainitemsel');
			$('#input_manualmetadata').prop('checked', false);
			myself.select_metadata_mode();
		}
		myself.accept_page();
	});

	var go_icon = $('<img class="book_gotoitempage" src="' + this.imgs['go_to_page'] + '"/>').click(function (e) {
		var url = $(this).siblings('.book_pageitemtitle').text();

		if (url) {
			window.open(mw.config.get('wgArticlePath').replace('$1', url), "_blank");
		}
	});

	var span = $('<span class="book_pageitemtitle">' + title + '</span>').mouseover(function (e) {
		if (e.ctrlKey) {
			$(this).parent().addClass('pageitem_sel_delete');
		} else {
			$(this).parent().addClass('pageitem_sel');
		}
	}).mouseleave(function (e) {
		$(this).parent().removeClass('pageitem_sel pageitem_sel_delete');
	}).mouseup(function (e) {
		$(this).parent().removeClass('pageitem_sel pageitem_sel_delete');
	}).mousedown(function (e) {
		if (e.ctrlKey) {
			$(this).parent().remove();
		}
	}).click(function (e) {
		$(this).parent().addClass('pageitem_sel');
	}).dblclick(function (e) {
		if ($(this).parents('#book_pagelistpending').length > 0) {
			$('#book_pagelistexist').append($(this).parent());
		} else if ($(this).parents('#book_pagelistexist').length > 0) {
			$('#book_pagelistpending').append($(this).parent());
		}

		myself.accept_page();

		$(this).parent().removeClass('pageitem_sel pageitem_sel_delete');
	});
	$(document).keyup(function (e) {
		if (e.ctrlKey) {
			$('.pageitem_sel_delete').removeClass('pageitem_sel_delete');
		}
	}).keydown(function (e) {
		if (e.ctrlKey) {
			$('.pageitem_sel').addClass('pageitem_sel_delete');
		}
	});

	page.append(delete_icon, go_icon, tag_icon, span);

	if (highlight) {
		page.addClass('book_mainitemsel').find('.book_tagmainitem').attr('src', myself.imgs['tag_on']);
	}
	return page;
};

window.BookMaker.prototype.accept_page = function () {
	//called when the lists are modified
	this.store_book();
};

window.BookMaker.prototype.move_page = function (dir) {

	if (dir == 'right_all') {
		$('#book_pagelistexist').append($('#book_pagelistpending .book_pageitem'));
	} else if (dir == 'left_all') {
		$('#book_pagelistpending').append($('#book_pagelistexist .book_pageitem'));
		$('.book_mainitemsel .book_tagmainitem').attr('src', this.imgs['tag_on']);
	}
	this.accept_page();
};

window.BookMaker.prototype.get_pages_from_subpage = function (prefix) {

	var title, i,
		myself = this;

	myself.add_page(mw.config.get('wgTitle'));

	$.ajax({
		url: mw.util.wikiScript('api'),
		data: {
			format: 'json',
			action: 'query',
			list: 'allpages',
			apprefix: mw.config.get('wgPageName'),
			apnamespace: 0,
			apfilterredir: 'nonredirects',
			aplimit: 500
		},
		success: function (data) {
			for (i in data.query.allpages) {
				title = data.query.allpages[i].title;

				if (title) {
					myself.add_page(title);
				}
			}
		},
	});
};

window.BookMaker.prototype.get_page_from_input = function (failedTitle) {
	//prompt for a page title to load
	var myself = this;
	var input = $('<input>', {'style':"width:100%", 'type':"text", 'value':failedTitle});
	var prompt = failedTitle?this.msg.noPageFound:this.msg.enterPage;
	var title;

	var dialog = $('<div>')
		.append(prompt + ':','<br>', input)
		.dialog({
			modal: true,
			title: this.msg.loadPageTitle,
			buttons: {
				"OK": function () {
					$(this).dialog("close");

					if (input.val() != '') {
						title = input.val();

						myself.check_title_exists(title, function (exists) {
							if (exists) {
								myself.add_page(title);
							} else {
								myself.get_page_from_input(title); //try again
							}
						});
					}
				},
				"Cancel": function () {
					$(this).dialog("close");
				}
			}
		});
};

window.BookMaker.prototype.get_page_from_category = function (failedTitle) {
	//prompt for a page title to load
	var myself = this;
	var input = $('<input>', {'type':"text", 'value':failedTitle});
	var prompt = failedTitle?this.msg.noCategoryPagesFound:this.msg.enterCategory;
	var category;

	var dialog = $('<div>')
		.append(prompt + ':','<br>', this.msg.category + ': ', input)
		.dialog({
			modal: true,
			title: "Load pages from a category",
			buttons: {
				"OK": function () {
					$(this).dialog("close");

					if (input.val() != '') {
						category = input.val();
						$.ajax({
							url: mw.util.wikiScript('api'),
							data: {
								format: 'json',
								action: 'query',
								list: 'categorymembers',
								cmtitle: 'Category:' + category,
								cmlimit: 500
							},
							dataType: 'json',
							success: function (data) {
								if ( data && data.query && data.query.categorymembers ) {
									var cm = data.query.categorymembers;
									if (cm.length == 0) {
										myself.get_page_from_category(category); //try again
										return;
									}
									for (var i=0; i<cm.length; i++) {
										if (cm[i].ns == 0 || cm.ns == 2) { //main or user space
											myself.add_page(cm[i].title);
										}
									}
								}
							}
						});
					}
				},
				"Cancel": function () {
					$(this).dialog("close");
				}
			}
		});
};

window.BookMaker.prototype.add_this_page = function () {
	this.add_page(mw.config.get('wgTitle'));
};

window.BookMaker.prototype.add_page = function (title) {
	var page = this.append_pageitem(title, 'book_pagelistpending');

	if (page) { //store if we added anything
		this.store_book();
	}
};

window.BookMaker.prototype.load_existing_book = function (pageTitle) {
	var myself = this;

	//if there are no existing pages, just add them
	if ($('#book_pagelistpending .book_pageitem').length === 0 && $('#book_pagelistexist .book_pageitem').length === 0) {
		myself.load_existing_book2(pageTitle, true);
		return;
	}

	//otherwise prompt for replace/append
	var dialog = $('<div>', {text: this.msg.replaceAppend}).dialog({
		title: this.msg.replaceAppendTitle,
		modal: true,
		buttons: {
			"Replace": function () {
				$(this).dialog("close");
				myself.load_existing_book2(pageTitle, true);
			},
			"Append": function () {
				$(this).dialog("close");
				myself.load_existing_book2(pageTitle, false);
			}
		}
	});
};

window.BookMaker.prototype.load_existing_book2 = function (pageTitle, replace) {
	var myself = this,
		title, msg;
		
	$.ajax({
		url: mw.util.wikiScript('index'),
		//cache: false,
		data: {
			action: 'raw',
			title: pageTitle,
		},
		success: function (data) {
			myself.set_book_name(); //initialise book name
			myself.add_pages_from_wikitext(data, replace);
		},
		error: function (jqXHR, textStatus, errorThrown) {
			msg = this.msg.bookNotLoaded.replace('$1', '<a href="' + mw.config.get('wgArticlePath').replace('$1', pageTitle) + '">' + pageTitle + '</a>');
			title = this.msg.bookNotLoadedTitle;
			$.alert(msg + '.', title);
		},
	});
};

//parse wikitext to load an existing book
window.BookMaker.prototype.add_pages_from_wikitext = function (bookWikitext, replace) {

	var pagePattern = /[\*:]\s*\[\[([^\]\|]+)\|?.*]\]\s*/,
		lines = bookWikitext.split('\n'),
		match, i;

	if (replace) {
		this.clear_list('book_pagelistpending');
	}

	//check lines for interesting things
	for (i = 0; i < lines.length; i++) {
		match = pagePattern.exec(lines[i]);
		if (match != null) {
			this.add_page(match[1]);
		}
	}
};

window.BookMaker.prototype.get_pages_from_existing_book = function () {
	//prompt for a book title to load
	var myself = this;
	var input = $('<input style="width:100%" type="text" value="' + this.central_bookstore + '"/>');
	var dialog = $('<div>').append(this.msg.enterBookLocation + ':', '<br>', input)

	.dialog({
		modal: true,
		title: "Load an existing book",
		buttons: {
			"OK": function () {
				$(this).dialog("close");

				if (input.val() != '') {
					myself.load_existing_book(input.val());
				}
			},
			"Cancel": function () {
				$(this).dialog("close");
			}
		}
	});
};

window.BookMaker.prototype.get_pages_from_saved_book = function () {
	//load from an already saved book
	this.load_existing_book(this.bookdata.directory + this.bookdata.bookname);
};

window.BookMaker.prototype.append_pageitem = function (title, destid, highlight) {
	var pageitem = this.create_pageitem(title, highlight); //add current page
	$('#' + destid).append(pageitem);

	return pageitem;
};

window.BookMaker.prototype.get_pages_from_current = function () {
	var myself = this;
	myself.add_page(mw.config.get('wgTitle'));

	$('.mw-content-ltr a').each(function (index) {
		var title = $(this).attr('title');

		//reject external, no ns0, no-exported and header/footers
		if (title == undefined || title.match(/^(Help|Author|User|Index|Portal|Wikisource|Template|Category|Page|Special|File)(_talk)?:/i) || $(this).is('.image, .extiw, .internal') || $(this).parents('.ws-noexport, #footertemplate, #headertemplate').length) {
			return true;
		}
		myself.add_page(title);
	});
};

window.BookMaker.prototype.clear_list = function (id) {
	$('#' + id).empty();
};

window.BookMaker.prototype.select_metadata_mode = function () {

	if ($('#input_manualmetadata:checked').length > 0) {
		$('.book_manualmetadata, #input_metadatafromthispage').prop('disabled', false);
		$('.book_mainitemsel .book_tagmainitem').attr('src', this.imgs['tag_off']).parent().removeClass('book_mainitemsel');
	} else {
		$('.book_manualmetadata, #input_metadatafromthispage').prop('disabled', true);
	}


};

window.BookMaker.prototype.get_metadata_from_this_page = function () {

	$('#input_metadatatitle').val($('#ws-title').text());
	$('#input_metadatadate').val($('#ws-year').text());
	$('#input_metadataauthor').val($('#ws-author').text());
	$('#input_metadatatranslator').val($('#ws-translator').text());

	this.store_book();
};

window.BookMaker.prototype.store_book = function () {

	var myself = this;

	this.bookdata = {
		'directory': '',
		'bookname': '',
		'title': '',
		'author': '',
		'translator': '',
		'date': '',
		'pagelist': [],
		'pendinglist': [],
		'manual_metadata': false,
		'metadata_source': ''
	};

	$('#book_pagelistexist .book_pageitemtitle').each(function () {
		var title = $(this).text();
		if (title) {
			myself.bookdata['pagelist'].push(title);
		}
	});

	$('#book_pagelistpending .book_pageitemtitle').each(function () {
		var title = $(this).text();
		if (title) {
			myself.bookdata['pendinglist'].push(title);
		}
	});

	this.bookdata['directory'] = $('#book_bookdirectory').text();
	this.bookdata['bookname'] = $('#book_bookname').text();

	if ($('#input_manualmetadata:checked').length > 0) {
		this.bookdata['manual_metadata'] = true;
		this.bookdata['title'] = $('#input_metadatatitle').val();
		this.bookdata['author'] = $('#input_metadataauthor').val();
		this.bookdata['translator'] = $('#input_metadatatranslator').val();
		this.bookdata['date'] = $('#input_metadatadate').val();
	} else {
		this.bookdata['manual_metadata'] = false;
	}

	if ($('.book_mainitemsel').length > 0) {
		this.bookdata['metadata_source'] = $('.book_mainitemsel').parent().find('.book_mainitemsel').text();
	}

	localStorage.setItem(this.localstorage_name, JSON.stringify(this.bookdata));

	$('#bookmaker').addClass('book_updated');
	setTimeout(function () {
		$('#bookmaker').removeClass('book_updated');
	}, 600);
};

window.BookMaker.prototype.retrieve_book = function () {

	if (!(this.localstorage_name in localStorage)) {
		this.set_book_name(); //initialise book name
	}

	this.bookdata = JSON.parse(localStorage[this.localstorage_name]);

	$('#book_bookname').text(this.bookdata['bookname']);
	$('#book_bookdirectory').text(this.bookdata['directory']);
	this.set_book_name(true);

	if (this.bookdata['manual_metadata']) {
		$('#input_manualmetadata').prop('checked', true);
		this.select_metadata_mode();
		$('#input_metadatatitle').val(this.bookdata['title']);
		$('#input_metadataauthor').val(this.bookdata['author']);
		$('#input_metadatatranslator').val(this.bookdata['translator']);
		$('#input_metadatadate').val(this.bookdata['date']);

		$('#input_metadatafromthispage').prop('disabled', false);
	} else {
		$('#input_manualmetadata').prop('checked', false);
	}

	this.restore_pages(this.bookdata['pagelist'], 'book_pagelistexist');
	this.restore_pages(this.bookdata['pendinglist'], 'book_pagelistpending');
};

window.BookMaker.prototype.restore_pages = function (pagelist, dest) {
	var i, text;
	for (i in pagelist) {
		text = pagelist[i];
		if (text) {
			this.append_pageitem(text, dest, this.bookdata['metadata_source'] == text);
			this.accept_page();
		}
	}
};

window.BookMaker.prototype.save_book = function () {
	if (!('pagelist' in this.bookdata)) {
		return;
	}

	if ($('#book_pagelistexist .book_pageitem').length === 0) {
		$.alert(this.msg.emptyBook + '.', this.msg.emptyBookTitle);
		return;
	}

	var myself = this;

	if ((this.bookdata['manual_metadata'] && (!this.bookdata['title'] || !this.bookdata['author'])) || (!this.bookdata['manual_metadata'] && !this.bookdata['metadata_source'])) {

		var dialog = $('<div>').html(this.msg.missingMetadata).dialog({
			title: "Missing metadata!",
			modal: true,
			buttons: {
				"Continue": function () {
					$(this).dialog("close");
					myself.save_book2();
				},
				"Stop and add metadata": function () {
					$(this).dialog("close");
				}
			}
		});
	} else {
		myself.save_book2();
	}
};

window.BookMaker.prototype.save_book2 = function () {

	var myself = this;

	//callback for confirming potential save
	var confirm_save = function (exists, edittoken, timestamp, user, currtimestamp) {

			if (exists) {
				var edittimestring = timestamp.toUTCString();
				var now = new Date();
				var timeago = myself.format_time_span(now - timestamp);


				var dialog = $('<div></div>').html("A book exists in this location. The last revision was " + timeago + " ago, by " + user + ". Is it OK to overwrite it?").dialog({
					title: "Book exists!",
					modal: true,
					buttons: {
						"Overwrite": function () {
							$(this).dialog("close");
							myself.save_book_execute(edittoken);
						},
						"Stop": function () {
							$(this).dialog("close");
						}
					}
				});
			} else {
				myself.save_book_execute(edittoken);
			}
		};

	var savelocation = this.bookdata['directory'] + this.bookdata['bookname'];

	this.check_title_exists(savelocation, confirm_save);
};


window.BookMaker.prototype.format_time_span = function (ms) {
	//return a string of the approximate length of time
	var s = Math.floor(ms / 1000),
		mi = Math.floor(s / 60),
		h = Math.floor(mi / 60),
		d = Math.floor(h / 24),
		m = Math.floor(d / 30),
		y = Math.floor(d / 365);

	var span = ((y === 0) ? '' : (y + ' year' + ((y > 1) ? 's' : ''))) 
		+ ((m === 0 || y > 2) ? '' : (((y === 0) ? '' : ', ') + m % 12 + ' month' + ((m > 1) ? 's' : ''))) 
		+ ((d === 0 || y > 0) ? '' : (((m + y === 0) ? '' : ', ') + d % 30 + ' day' + ((d > 1) ? 's' : ''))) 
		+ ((h === 0 || d > 3) ? '' : (((m + y + d === 0) ? '' : ', ') + h % 24 + ' hour' + ((h > 1) ? 's' : ''))) 
		+ ((mi === 0 || h > 3) ? '' : (((m + y + d + h === 0) ? '' : ', ') + mi % 60 + ' minute' + ((mi > 1) ? 's' : ''))) 
		+ ((s === 0 || mi > 5) ? '' : (((m + y + d + h + mi === 0) ? '' : ', ') + s % 60 + ' second' + ((s > 1) ? 's' : '')));

	return span;
};

window.BookMaker.prototype.save_book_execute = function (edittoken) {

	var i, wikitext = '{{' + this.bookTemplateName;

	if (this.bookdata['manual_metadata']) {
		wikitext += '\n | title      = ' + this.bookdata['title'];
		wikitext += '\n | author     = ' + this.bookdata['author'];
		wikitext += '\n | translator = ' + this.bookdata['translator'];
		wikitext += '\n | year       = ' + this.bookdata['date'] + '';
	} else if (this.bookdata['metadata_source']) {
		wikitext += '\n | metadata   = ' + this.bookdata['metadata_source'] + '';
	}

	wikitext += '\n | contents   = \n';

	for (i in this.bookdata['pagelist']) {
		var pagename = this.bookdata['pagelist'][i];
		wikitext += ': [[' + pagename + '|' + pagename.split('/').pop() + ']]\n';
	}

	wikitext += '}}\n\n';

	var savelocation = this.bookdata['directory'] + this.bookdata['bookname'];

	this.edit_page(savelocation, wikitext, edittoken);
};

window.BookMaker.prototype.edit_page = function (pagetitle, content, editToken) {

	$.ajax({
		url: mw.util.wikiScript('api'),
		data: {
			format: 'json',
			action: 'edit',
			title: pagetitle,
			summary: this.msg['creation_summary'],
			text: content,
			token: editToken
		},
		dataType: 'json',
		type: 'POST',
		success: function (data) {
			if (data && data.edit && data.edit.result == 'Success') {
				window.location.href = mw.config.get('wgArticlePath').replace('$1', pagetitle); // load page if edit was successful
			} else if (data && data.error) {
				$.alert('Error: API returned error code "' + data.error.code + '": ' + data.error.info);
			} else {
				$.alert('Error: Unknown result from API.');
			}
		},
		error: function (xhr) {
			$.alert('Error: Request failed.');
		}
	});
};
window.BookMaker.prototype.construct_qr_url = function (data) {
	return this.qrGenerator + '?data=' + encodeURIComponent(data);
};

window.BookMaker.prototype.recentre_dialog = function () {
	$(".ui-dialog-content").dialog("option", "position", "center");
};

window.BookMaker.prototype.show_qr_codes = function () {
	
	var dialog = $('<div class="qr_code_container">').append(this.msg.getQRCodes, '<br>');

	var table = $('<table>');
	var imgLink, url;

	for (url in this.exportURLs) {
		table.append('<tr><td>' + url + '</td><td><img src="' + this.construct_qr_url(this.exportURLs[url]) + '"/></td></tr>');
	}

	dialog.append(table).dialog({
			modal: true,
			title: this.msg.qrCodeTitle,
			resizable: false,
			close: function (event, ui) {
				$(this).remove();
			},
		});
		
	$(".qr_code_container img").load(this.recentre_dialog);

};

window.BookMaker.prototype.process_saved_book_template = function () {
	//add functionality to the saved book template
	var myself = this;
	//replace the bookmaker link
	$('#open_bookmaker_link').append('[&nbsp;', $('<img>', {src:this.imgs.editBook}), '&nbsp;',
		$('<a>', {id: 'open_bookmaker_link', href: '', text: this.msg.startBookMaker}).click( function (e) {
			e.preventDefault();
			myself.start_bookmaker();
		}), '&nbsp;]');
	
	//linkify the user bookshelf link placeholder
	if (mw.config.get('wgUserName') !== null) {
		$('#wikisource_userbookshelf_link').append('[&nbsp;', this.generate_prefix_link(myself.user_bookstore, this.msg.yourShelf), '&nbsp;]').after('<br>');
	} 
	
	//linkify the QR code link
	$('#book_get_qr_codes a').replaceWith($('<a>', {href: '', text: $('#book_get_qr_codes a').text()}).click( function (e) {
		myself.show_qr_codes();
		e.preventDefault();
		}));
};
	window.BookMakerInstance.initialise();

	//check if we could be in a book namespace
	if ($.inArray(mw.config.get('wgNamespaceNumber'), [0, 2]) > -1) { //only trigger in sensible namespaces: main, book, user
		if (($('#' + window.BookMakerInstance.bookTemplateID).length > 0) //we are looking at a book list
		|| $.inArray(mw.config.get('wgNamespaceNumber'), [0]) > -1) { //main namespace, we could make a book
			window.BookMakerInstance.process_saved_book_template();
			mw.loader.using(['jquery.ui'], function () {

				//add a simple alert method
				//http://coding.abel.nu/2012/01/jquery-ui-replacement-for-alert/
				$.extend({
					alert: function (message, title) {
						$('<div>').dialog({
							buttons: {
								"OK": function () {
									$(this).dialog("close");
								}
							},
							close: function (event, ui) {
								$(this).remove();
							},
							resizable: false,
							title: title,
							modal: true
						}).html(message);
					}
				});
				
				$(window).resize(window.BookMakerInstance.recentre_dialog);

				$(document).triggerHandler('scriptLoaded', ['BookMaker', window.BookMakerInstance]);
			});
		}
	}


}()); //END IIFE