User:Inductiveload/BookMaker.js

/*********************************************************************** * 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. * * 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 * **********************************************************************/

(function {	//"use strict";

var BookMaker = function { //constructor //this function does the essential set-up for the BookMaker. //Non-essential setup is done on initialise once the BookMaker is	//starting for sure

this.book_page_regex = /^(User:.*\/Books\/(.+)|Book:(.+)|Wikisource:Books\/(.+))$/ //matches pages that could be books

this.localstorage_name = 'wikisource_bookmakerpersistentdata'; this.localstorage_minimised_name = 'wikisource_bookmakerminimised';

this.central_bookstore = 'Wikisource:Books/'; this.user_bookstore = 'User:' + wgUserName + '/Books/';

this.initMsg = { //messages that are shown before the bookmaker starts 'editBook':'Edit this book', 'createBook':'Create a book', 'yourShelf':'Your bookshelf' }

this.add_create_book_link;

//if we are editing a book, display bookmaker right away if (this.localstorage_name in localStorage){ this.start_bookmaker; }	//this.start_bookmaker; //uncomment to load automatically for testing. };

BookMaker.prototype.initialise = function { this.bookdata = {};

this.book_template_name = 'saved book 2';

this.imgs = { 'x_red': 'http://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/X_Red.svg/15px-X_Red.svg.png', 'tag_off': 'http://upload.wikimedia.org/wikipedia/commons/8/8c/Tag_grey.png', 'tag_on': 'http://upload.wikimedia.org/wikipedia/commons/d/dd/Tag_red.png', 'go_to_page': 'http://upload.wikimedia.org/wikipedia/commons/9/92/Book_next.png' }

//messages this.msg = { '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?' }

this.add_styles_to_page; };

BookMaker.prototype.add_styles_to_page = function {

style = $('').appendTo('head');

style.append('#bookmaker { position:relative; background-color:#E0E9EA; border:1px solid #294584; padding:2em; margin-bottom:1em; }');

style.append('#book_editor {clear:both;}');

style.append('#book_namenotice {float:left;}'); style.append('#book_input_bookname {width:100%}');

style.append('#book_pagelist { margin:auto; height:100%; clear:both; overflow:auto }');

style.append('#book_pagemanip {width:6em; float:left; margin-top:2em; text-align:center}');

style.append('#book_displaycontrols { position:absolute; right:5px; top:5px; }');

style.append('#book_metadatainput {clear:both;}');

style.append('.book_inputpagemanip { width:4em; }');

style.append('.book_pagelist_container {width:40%; float:left;}'); style.append('.book_pagelist { background-color:white; border:1px solid #444444; width:100%; margin:5px 8px; height:100%; min-height:5em; list-style:none }');

style.append('.book_pagelist_title {font-weight:bold;}'); style.append('.book_pagelist_title > .book_clearall {float:right; color:red; cursor:pointer;}');

style.append('.book_displaycontrol { float:right; cursor:pointer; margin-right:10px; top:5px}');

style.append('#book_metadatafields {text-align:right; max-width:30em; float:left;}'); style.append('#book_commitcontrols {clear:both;}');

style.append('.book_pageitem {list-style-type:none; cursor:pointer;}'); style.append('.book_pageitem.book_mainitemsel {font-weight:bold;}');

style.append('.pageitem_sel { background-color:#DDDDDD;}'); style.append('.pageitem_sel_delete { background-color:#FF86A2;}');

style.append('#book_pagelistexist .book_tagmainitem { display:auto;}'); style.append('#book_pagelistpending .book_tagmainitem { display:none;}');

style.append('.book_updated {border:1px solid #00FF00 !important;}');

style.append('.book_pageitemtitle {padding-left:0.5em; }'); }

BookMaker.prototype.add_create_book_link = function { //Create or hijack the "Create a book link" in the sidebar $('#coll-create_a_book').remove;

var href, linktext;

if (wgPageName.match(this.book_page_regex)) { href = 'javascript:BookMakerInstance.start_bookmaker'; linktext = this.initMsg.editBook;

$('#open_bookmaker_link').replaceWith($('' + $('#open_bookmaker_link').text + '<\a>'));

if (wgUserName != null) { $('#wikisource_userbookstore_link').html('[ ' + this.generate_prefix_link(this.user_bookstore, this.initMsg.yourShelf) + ' ]'); } else { $('#wikisource_userbookstore_link').remove; }	} else { href = 'javascript:BookMakerInstance.start_bookmaker'; linktext = this.initMsg.createBook; }

$('#collectionPortletList').prepend('' + linktext + '');

this.sidebar_link = $('#coll-create_a_book > a'); }

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

var dialog = $(' ') .html(this.msg.closeWarning) .dialog({			title: this.msg.areYouSure,			modal: true,			buttons: {				"Cancel": function {					$(this).dialog("close");				},				"Close": function  {					$(this).dialog("close");

localStorage.removeItem(myself.localstorage_name);

$('#bookmaker').remove;

myself.add_create_book_link; }			}		}); }

BookMaker.prototype.toggle_mimimise_bookmaker = function { this.minimised = !this.minimised; this.minimised?this.minimise:this.maximise;

}

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

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

//generates a link element to a Special:PrefixIndex page BookMaker.prototype.generate_prefix_link = function (prefix, text) { return '' + (text ? text : prefix) + '' }

BookMaker.prototype.start_bookmaker = function {

if ($('#bookmaker').length > 0) { return }

this.initialise; //set up constants

this.sidebar_link.text('Close book creator').attr('href', 'javascript:BookMakerInstance.close_bookmaker');

var myself = this;

this.bookmaker = $(' ')

var prefixinput = $(' Book page prefix: \ ' + this.generate_prefix_link(this.user_bookstore) + ' \ ' + this.generate_prefix_link(this.central_bookstore) + '\ ');

var nameinput = $(' ');

var namecontrol = $(' ', {		'type': 'text',		'class': 'book_namecontrol',		'id': 'book_input_bookname',		'name': 'book_input_bookname',		'value': this.guess_title_from_page	}); var usetitleasname = $(' ', {		'type': 'button',		'id': 'book_usetitleasname',		'value': 'Use this page\'s title'	}).click(function {		namecontrol.val(myself.guess_title_from_page);		myself.set_book_name;	});

nameinput.append(' Choose the location to save your book: ', ' ', namecontrol, ' ', usetitleasname);

var booknamenotice = $('\ Editing book:    \ ');

var booknameselection = $(' ').append(prefixinput, nameinput, booknamenotice);

this.book_editor = $('<div id="book_editor"> ');

var get_pages_buttons = $(' <div id="book_getpages">\ '+this.msg.addPages+': \ <input type="button" class="book_addpagebutton" id="input_addthispage" name="input_addthispage" value="This page" onclick="BookMakerInstance.add_this_page" />\ <input type="button" class="book_addpagebutton" id="input_getfromcurrentpage" name="input_getfromcurrentpage" value="From links on this page" onclick="BookMakerInstance.get_pages_from_current" />\ <input type="button" class="book_addpagebutton" id="input_getfromsubpages" name="input_getfromsubpages" value="From subpages of this page" onclick="BookMakerInstance.get_pages_from_subpage" />\ <input type="button" class="book_addpagebutton" id="input_getfromsaved" name="input_getfromsaved" value="From saved version of this book" onclick="BookMakerInstance.get_pages_from_saved_book" />\ <input type="button" class="book_addpagebutton" id="input_getfromexisting" name="input_getfromexisting" value="From a saved book..." onclick="BookMakerInstance.get_pages_from_existing_book" />\ <input type="button" class="book_addpagebutton" id="input_getfrominput" name="input_getfrominput" value="From another page..." onclick="BookMakerInstance.get_page_from_input" />\ ');

var metadata_area = $('<div id="book_metadatainput"> \ Manual metadata: \ <input type="checkbox" id="input_manualmetadata" name="input_manualmetadata" value="" onclick="BookMakerInstance.select_metadata_mode" /> \ <input type="button" id="input_metadatafromthispage" name="input_metadatafromthispage" disabled=disabled value="Use this page\'s metadata" onclick="BookMakerInstance.get_metadata_from_this_page" />\ <div id="book_metadatafields">\ Title:\ <input class="book_manualmetadata" type="text" id="input_metadatatitle" name="input_metadatatitle" value="" disabled /> \ Author:\ <input class="book_manualmetadata" type="text" id="input_metadataauthor" name="input_metadataauthor" value="" disabled /> \ Translator:\ <input class="book_manualmetadata" type="text" id="input_metadatatranslator" name="input_metadatatranslator" value="" disabled /> \ Date:\ <input class="book_manualmetadata" type="text" id="input_metadatadate" name="input_metadatadate" value="" disabled /> \ ');

var page_list = $('<div id="book_pagelist"> ')

var pages_pending = $('<div class="book_pagelist_container"><div class="book_pagelist_title">Pending pages: <span class="book_clearall" onclick="BookMakerInstance.clear_list(\'book_pagelistpending\')">[Clear all] <ul id="book_pagelistpending" class="book_pagelist"></ul> '); var pages_exist = $('<div class="book_pagelist_container"><div class="book_pagelist_title"">Pages in book: <span class="book_clearall" onclick="BookMakerInstance.clear_list(\'book_pagelistexist\')">[Clear all] <ul id="book_pagelistexist" class="book_pagelist"></ul> ');

var pages_manipulation = $('<div 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\')" />\ ');

var commit_controls = $('<div id="book_commitcontrols"> \ <input type="button" id="book_commitbook" name="book_commitbook" value="Save book" onclick="BookMakerInstance.save_book" />\ ')

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

var closebutton = $('<a class="book_displaycontrol" id="book_closebutton">Close</a>').click(function {		myself.close_bookmaker	});

var minimisebutton = $('<a class="book_displaycontrol" id="book_minimisebutton">Minimize</a>').click(function {		myself.toggle_mimimise_bookmaker	});

var display_controls = $('<div id="book_displaycontrols"> ').append(minimisebutton, closebutton);

$('#bodyContent').prepend(this.bookmaker.append(display_controls, 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	}).disableSelection;

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

$('.book_namecontrol').bind('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 (wgPageName.match(this.book_page_regex)) { this.load_existing_book(wgPageName); } else { this.retrieve_book; }

}

BookMaker.prototype.guess_title_from_page = function {

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

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

}

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; }

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); }

BookMaker.prototype.check_title_exists = function (title, callback) { //check if a page exists, executing callback with the result, var myself = this;

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

var query = mw.config.get('wgServer') + mw.config.get('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); }	}); }

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(wgArticlePath.replace('$1', url), "_blank"); }	});

var span = $('<span class="book_pageitemtitle">' + title + ' ').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 }

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

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; }

BookMaker.prototype.get_pages_from_subpage = function (prefix) {

var query = mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/api.php?action=query&list=allpages&format=json&apprefix=' + wgPageName + '/&apnamespace=0&apfilterredir=nonredirects&aplimit=500';

var title; var myself = this;

myself.add_page(wgTitle);

$.ajax({		url: query,		success: function (data) {			for (i in data['query']['allpages']) {				title = data['query']['allpages'][i]['title'];

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

BookMaker.prototype.get_page_from_input = function (failedTitle) { //prompt for a page title to load var myself = this; var input = $(' ', {'style':"width:100%", 'type':"text", 'value':failedTitle}); var prompt = failedTitle?'No page found, please check your spelling and try again:':'Enter the location of the page you want to add:' var title;

var dialog = $(' ') .append(prompt,' ', input) .dialog({			modal: true,			title: "Load an existing page",			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"); }			}		}); };

BookMaker.prototype.add_this_page = function { this.add_page(wgTitle); };

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

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

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 = $(' ').html('Replace or append to the current pending pages in BookMaker?').dialog({		title: "Replace or append?",		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);			}		}	}); }

BookMaker.prototype.load_existing_book2 = function (pageTitle, replace) { myself = this; $.ajax({		url: mw.util.wikiScript('index'),		//cache: false,		data: {			action: 'raw',			title: pageTitle,		},		success: function (data) {			myself.add_pages_from_wikitext(data, replace);		},		error: function (jqXHR, textStatus, errorThrown) {			var msg = 'The book at <a href="' + wgArticlePath.replace('$1', pageTitle) + '">' + pageTitle + '</a> could not be loaded.'			var title = 'Book not loaded'

$.alert(msg, title); },	}); }

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

var pagePattern = /[\*:]\s*\[\[([^\]\|]+)\|?.*]\]\s*/;

var lines = bookWikitext.split('\n');

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

//check lines for interesting things for (var i = 0; i < lines.length; i++) { match = pagePattern.exec(lines[i]);

if (match != null) { this.add_page(match[1]); }	} }

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 = $(' ').append('Enter the location of the book you want to load: ', 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"); }		}	}); };

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

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

return pageitem }

BookMaker.prototype.get_pages_from_current = function {

var myself = this;

myself.add_page(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(/^(Author|User|Index|Portal|Wikisource|Template|Category|Page|Special|File)(_talk)?:/i) || $(this).hasClass('extiw') || $(this).hasClass('image') || $(this).parents('.ws-noexport, #footertemplate, #headertemplate').length) { return true; }		myself.add_page(title); }); };

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

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); }

};

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; };

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); };

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'); };

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

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

if ($('#book_pagelistexist .book_pageitem').length === 0) { $.alert('There are no pages in the book. Please add some before trying to save.', 'Book is empty!'); 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 = $(' ').html('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?').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; } }

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 = $(' ').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); }

BookMaker.prototype.format_time_span = function (ms) { //return a string of the approximate length of time var s = Math.floor(ms / 1000); var mi = Math.floor(s / 60); var h = Math.floor(mi / 60); var d = Math.floor(h / 24) var m = Math.floor(d / 30); var 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; }

BookMaker.prototype.save_book_execute = function (edittoken) {

var wikitext = '\n\n'

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

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

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 = 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.');		}	}); }

jQuery(document).ready(function {

//check if we could be in a book namespace if ($.inArray(wgNamespaceNumber, [0, 2]) > -1) { //only trigger in sensible namespaces: main, book, user if (wgPageName.match(this.book_page_regex) //we could be looking at a book list		||		$.inArray(wgNamespaceNumber, [0]) > -1) { //main namespace, we could make a book 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) {						$(" ").dialog({ buttons: { "Ok": function { $(this).dialog("close"); }							},							close: function (event, ui) { $(this).remove; },							resizable: false, title: title, modal: true }).html(message);					}				});

BookMakerInstance = new BookMaker; });		}	} });

}); //END IIFE