FictionPress story export script.

Writes all chapters of the story on one page.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @version       1.0
// @include       *.fictionpress.com/s/*
// @namespace     ffnet
// @name          FictionPress story export script.
// @author        Alssn
// @description   Writes all chapters of the story on one page.
// ==/UserScript==

function find(expr)
{
	var posts = document.evaluate(expr, document, null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	var postsArray = new Array();
	for (var i = 0; i < posts.snapshotLength; ++i) {
	     postsArray.push(posts.snapshotItem(i));
	}
	return postsArray;
}

function addButtons(){
// Adding buttons
	// res = find("//f[@id='f_size']");
	res = document.getElementById("f_size").parentNode;

	//creating links
	var addHeadersButton=document.createElement('a');
	addHeadersButton.href='javascript:';
	addHeadersButton.innerHTML='Headers';
	addHeadersButton.setAttribute('title','Add header to each chapter');
	var addIndexButton=document.createElement('a');
	addIndexButton.href='javascript:';
	addIndexButton.innerHTML='Index';
	addIndexButton.setAttribute('title','Create table of contents');
	var expAllButton=document.createElement('a');
	expAllButton.id='exportAllButton';
	expAllButton.href='javascript:';
	expAllButton.setAttribute('title','Show the whole story on one page');
	expAllButton.innerHTML='Story';
	var expButton=document.createElement('a');
	expButton.setAttribute('title','Show only text');
	expButton.href='javascript:';
	expButton.innerHTML='Text';
	// Adding listeners - that's the only way to do something after main code finsihed working;
	expAllButton.addEventListener('click',exportAll,false);
	expButton.addEventListener('click',exportCh,false);
	addHeadersButton.addEventListener('click',addHeaders,false);
	addIndexButton.addEventListener('click',addIndex,false);
	//fE and fA
	var e = document.createElement('span');
	e.setAttribute('style','font-family: serif;font-weight:bolder;color:#61a4e4');
	e.setAttribute('title','Export');
	e.innerHTML = "fE: ";
	var a = document.createElement('span');
	a.setAttribute('title','Add');
	a.setAttribute('style','font-family: serif;font-weight:bolder;color:#61a4e4');
	a.innerHTML = "fA: ";
	
	res.insertBefore(document.createTextNode(' '),res.firstChild);
	res.insertBefore(addIndexButton,res.firstChild);
	res.insertBefore(document.createTextNode(' '),res.firstChild);
	res.insertBefore(addHeadersButton,res.firstChild);
	res.insertBefore(a,res.firstChild);
	res.insertBefore(document.createTextNode(' '),res.firstChild);
	res.insertBefore(expButton,res.firstChild);
	res.insertBefore(document.createTextNode(' '),res.firstChild);
	res.insertBefore(expAllButton,res.firstChild);
	res.insertBefore(e,res.firstChild);
}

//Adding buttons to page;
addButtons();

//Adding table of contents
function addIndex(){
	var storytext = document.getElementById('storytext'); //Point of insert
	var chapters = document.getElementsByName('ffnee_chapter');
	//Creating base structure
	var index = document.createElement('div');
	index.innerHTML = '<h2>Table of contents</h2>';
	index.setAttribute('id','ffnee_index');
	var toC = document.createElement('ol');
	index.appendChild(toC);
	//Processing chapters
	for (var i=0;i<chapters.length;i++){
		var item = chapters.item(i); //chapter we are currently processing
		var id = item.getAttribute('id');
		var entry = document.createElement('li'); //Entry corresponding to the chapter in ToC
		entry.innerHTML = '<a href="#'+id+'">'+item.getAttribute('title')+'</a>';
		toC.appendChild(entry);
	}	
	storytext.insertBefore(index,storytext.firstChild);
}
//adding headers, as entered by author
function addHeaders(){
	var chapters = document.getElementsByName('ffnee_chapter');
	for (var i=0;i<chapters.length;i++){
	var item = chapters.item(i); //chapter to which we are adding a header
	var header = document.createElement('p');
	header.innerHTML = '<h2>Chapter '+(i+1)+': '+item.getAttribute('title')+'</h2>';
	item.insertBefore(header,item.firstChild);
	}
}

function exportCh(){
document.body.innerHTML='<div style=\'padding-left:2em;padding-right:2em;padding-top:1em;\'>'+document.getElementById('storytext').innerHTML+'</div>';
//Sadly, it is not possible to automatically copy text to clipboard in firefox without changing browser settings;
}
function exportAll(){
// Main actions   
	var expDiv = document.getElementById('exportAllButton');
	// Progress indicator
	var expText = expDiv.childNodes[0];
	var chapters = new Array();
	var hr=location.href;
	var chapterNumIndex=hr.search(/\/\d{1,3}\//);
	//Getting number of chapters
	var storyLength=getLength(); 
	if (storyLength == 1){
		expText.nodeValue = 'Oneshot';
		return;
	}
	var totalStoryLength = storyLength;//reference
	//launching retrieving of all chapters simultaneously
	for (var i=1;i<=storyLength;i++){
	  loadChapter(i, function(chapterHtml, chapterUrl){
			parseChapter(chapterHtml,chapterUrl);
			storyLength--;
      console.log(storyLength);
			if (storyLength == 0){
				parseStory();
			}
		});
	}
//Functions
    // Converting chapters' array into a whole;
	function parseStory(){
    console.log('parsing story');
		var numCh= chapters.length;
		//document.body.innerHTML=chapters[0];
		var appendNode=document.getElementById('storytext');
		appendNode.innerHTML= '';
    	for (var i=0;i<numCh;i++){
			//findHeader(chapters[i]);  //smart header search
			var st=chapters[i];
			st.setAttribute('name','ffnee_chapter');
			st.setAttribute('id','ffnee_ch'+i);
			if (i!=0){
				st.style.marginTop='10em';
			}
    		appendNode.appendChild(st);
    	}
    	expText.nodeValue='story(re)';
	}
	//  Getting number of chapters;
	function getLength(){
		var chNum=document.evaluate('//SELECT[@id=\'chap_select\']',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		if (chNum.snapshotLength>0){
			var numChapters = chNum.snapshotItem(0).length;
		}else{
			numChapters = 1;
		}
		return (numChapters);
	}
	// This function loads chapters and extracts chapter's number and title 
	function loadChapter(num, callback){
		var replStr='\/'+String(num)+'\/';
		var currentURL=hr.replace(/\/\d{1,3}\//,replStr);
				try{
		var req = new XMLHttpRequest();
		req.open('get',currentURL,true);
		req.onload= function(){
				callback(req.responseText,currentURL);
				};
		req.send();
		}catch (e) {
			console.log(e);
		}
	}
function parseChapter(chapterHtml, chapterURL){
		//getting chapter number
		var iChaptr = String(chapterURL.match(/\/\d{1,3}\//));
		var chapterNumber=iChaptr.substring(1,iChaptr.length-1);
		var t=document.createElement('div');
		t.innerHTML=chapterHtml;
		//extracting text only
  		var ev='//div[@id=\'storytext\']';
		var xpathResult = document.evaluate(ev,t,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
     	chapterContent=document.createElement('div');
		chapterContent.setAttribute('title',getChapterName(t));
		chapterContent.innerHTML = xpathResult.snapshotItem(0).innerHTML;
		
		chapters[chapterNumber-1]=chapterContent;
		expText.nodeValue = 'Export: Chapter '+String(totalStoryLength-storyLength+1)+' out of '+totalStoryLength;
		function getChapterName(obj){
			var select = obj.getElementsByTagName('select')[1].getElementsByTagName('option');
			 for (var i=0;i<select.length;i++){
				 if (select[i].getAttribute('selected')!=null){
					 return(select[i].innerHTML.split(/[. ]{2}/)[1]);
				 }
			}
		}
	}
}