FictionPress story export script.

Writes all chapters of the story on one page.

  1. // ==UserScript==
  2. // @version 1.0
  3. // @include *.fictionpress.com/s/*
  4. // @namespace ffnet
  5. // @name FictionPress story export script.
  6. // @author Alssn
  7. // @description Writes all chapters of the story on one page.
  8. // ==/UserScript==
  9.  
  10. function find(expr)
  11. {
  12. var posts = document.evaluate(expr, document, null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  13. var postsArray = new Array();
  14. for (var i = 0; i < posts.snapshotLength; ++i) {
  15. postsArray.push(posts.snapshotItem(i));
  16. }
  17. return postsArray;
  18. }
  19.  
  20. function addButtons(){
  21. // Adding buttons
  22. // res = find("//f[@id='f_size']");
  23. res = document.getElementById("f_size").parentNode;
  24.  
  25. //creating links
  26. var addHeadersButton=document.createElement('a');
  27. addHeadersButton.href='javascript:';
  28. addHeadersButton.innerHTML='Headers';
  29. addHeadersButton.setAttribute('title','Add header to each chapter');
  30. var addIndexButton=document.createElement('a');
  31. addIndexButton.href='javascript:';
  32. addIndexButton.innerHTML='Index';
  33. addIndexButton.setAttribute('title','Create table of contents');
  34. var expAllButton=document.createElement('a');
  35. expAllButton.id='exportAllButton';
  36. expAllButton.href='javascript:';
  37. expAllButton.setAttribute('title','Show the whole story on one page');
  38. expAllButton.innerHTML='Story';
  39. var expButton=document.createElement('a');
  40. expButton.setAttribute('title','Show only text');
  41. expButton.href='javascript:';
  42. expButton.innerHTML='Text';
  43. // Adding listeners - that's the only way to do something after main code finsihed working;
  44. expAllButton.addEventListener('click',exportAll,false);
  45. expButton.addEventListener('click',exportCh,false);
  46. addHeadersButton.addEventListener('click',addHeaders,false);
  47. addIndexButton.addEventListener('click',addIndex,false);
  48. //fE and fA
  49. var e = document.createElement('span');
  50. e.setAttribute('style','font-family: serif;font-weight:bolder;color:#61a4e4');
  51. e.setAttribute('title','Export');
  52. e.innerHTML = "fE: ";
  53. var a = document.createElement('span');
  54. a.setAttribute('title','Add');
  55. a.setAttribute('style','font-family: serif;font-weight:bolder;color:#61a4e4');
  56. a.innerHTML = "fA: ";
  57. res.insertBefore(document.createTextNode(' '),res.firstChild);
  58. res.insertBefore(addIndexButton,res.firstChild);
  59. res.insertBefore(document.createTextNode(' '),res.firstChild);
  60. res.insertBefore(addHeadersButton,res.firstChild);
  61. res.insertBefore(a,res.firstChild);
  62. res.insertBefore(document.createTextNode(' '),res.firstChild);
  63. res.insertBefore(expButton,res.firstChild);
  64. res.insertBefore(document.createTextNode(' '),res.firstChild);
  65. res.insertBefore(expAllButton,res.firstChild);
  66. res.insertBefore(e,res.firstChild);
  67. }
  68.  
  69. //Adding buttons to page;
  70. addButtons();
  71.  
  72. //Adding table of contents
  73. function addIndex(){
  74. var storytext = document.getElementById('storytext'); //Point of insert
  75. var chapters = document.getElementsByName('ffnee_chapter');
  76. //Creating base structure
  77. var index = document.createElement('div');
  78. index.innerHTML = '<h2>Table of contents</h2>';
  79. index.setAttribute('id','ffnee_index');
  80. var toC = document.createElement('ol');
  81. index.appendChild(toC);
  82. //Processing chapters
  83. for (var i=0;i<chapters.length;i++){
  84. var item = chapters.item(i); //chapter we are currently processing
  85. var id = item.getAttribute('id');
  86. var entry = document.createElement('li'); //Entry corresponding to the chapter in ToC
  87. entry.innerHTML = '<a href="#'+id+'">'+item.getAttribute('title')+'</a>';
  88. toC.appendChild(entry);
  89. }
  90. storytext.insertBefore(index,storytext.firstChild);
  91. }
  92. //adding headers, as entered by author
  93. function addHeaders(){
  94. var chapters = document.getElementsByName('ffnee_chapter');
  95. for (var i=0;i<chapters.length;i++){
  96. var item = chapters.item(i); //chapter to which we are adding a header
  97. var header = document.createElement('p');
  98. header.innerHTML = '<h2>Chapter '+(i+1)+': '+item.getAttribute('title')+'</h2>';
  99. item.insertBefore(header,item.firstChild);
  100. }
  101. }
  102.  
  103. function exportCh(){
  104. document.body.innerHTML='<div style=\'padding-left:2em;padding-right:2em;padding-top:1em;\'>'+document.getElementById('storytext').innerHTML+'</div>';
  105. //Sadly, it is not possible to automatically copy text to clipboard in firefox without changing browser settings;
  106. }
  107. function exportAll(){
  108. // Main actions
  109. var expDiv = document.getElementById('exportAllButton');
  110. // Progress indicator
  111. var expText = expDiv.childNodes[0];
  112. var chapters = new Array();
  113. var hr=location.href;
  114. var chapterNumIndex=hr.search(/\/\d{1,3}\//);
  115. //Getting number of chapters
  116. var storyLength=getLength();
  117. if (storyLength == 1){
  118. expText.nodeValue = 'Oneshot';
  119. return;
  120. }
  121. var totalStoryLength = storyLength;//reference
  122. //launching retrieving of all chapters simultaneously
  123. for (var i=1;i<=storyLength;i++){
  124. loadChapter(i, function(chapterHtml, chapterUrl){
  125. parseChapter(chapterHtml,chapterUrl);
  126. storyLength--;
  127. console.log(storyLength);
  128. if (storyLength == 0){
  129. parseStory();
  130. }
  131. });
  132. }
  133. //Functions
  134. // Converting chapters' array into a whole;
  135. function parseStory(){
  136. console.log('parsing story');
  137. var numCh= chapters.length;
  138. //document.body.innerHTML=chapters[0];
  139. var appendNode=document.getElementById('storytext');
  140. appendNode.innerHTML= '';
  141. for (var i=0;i<numCh;i++){
  142. //findHeader(chapters[i]); //smart header search
  143. var st=chapters[i];
  144. st.setAttribute('name','ffnee_chapter');
  145. st.setAttribute('id','ffnee_ch'+i);
  146. if (i!=0){
  147. st.style.marginTop='10em';
  148. }
  149. appendNode.appendChild(st);
  150. }
  151. expText.nodeValue='story(re)';
  152. }
  153. // Getting number of chapters;
  154. function getLength(){
  155. var chNum=document.evaluate('//SELECT[@id=\'chap_select\']',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  156. if (chNum.snapshotLength>0){
  157. var numChapters = chNum.snapshotItem(0).length;
  158. }else{
  159. numChapters = 1;
  160. }
  161. return (numChapters);
  162. }
  163. // This function loads chapters and extracts chapter's number and title
  164. function loadChapter(num, callback){
  165. var replStr='\/'+String(num)+'\/';
  166. var currentURL=hr.replace(/\/\d{1,3}\//,replStr);
  167. try{
  168. var req = new XMLHttpRequest();
  169. req.open('get',currentURL,true);
  170. req.onload= function(){
  171. callback(req.responseText,currentURL);
  172. };
  173. req.send();
  174. }catch (e) {
  175. console.log(e);
  176. }
  177. }
  178. function parseChapter(chapterHtml, chapterURL){
  179. //getting chapter number
  180. var iChaptr = String(chapterURL.match(/\/\d{1,3}\//));
  181. var chapterNumber=iChaptr.substring(1,iChaptr.length-1);
  182. var t=document.createElement('div');
  183. t.innerHTML=chapterHtml;
  184. //extracting text only
  185. var ev='//div[@id=\'storytext\']';
  186. var xpathResult = document.evaluate(ev,t,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  187. chapterContent=document.createElement('div');
  188. chapterContent.setAttribute('title',getChapterName(t));
  189. chapterContent.innerHTML = xpathResult.snapshotItem(0).innerHTML;
  190. chapters[chapterNumber-1]=chapterContent;
  191. expText.nodeValue = 'Export: Chapter '+String(totalStoryLength-storyLength+1)+' out of '+totalStoryLength;
  192. function getChapterName(obj){
  193. var select = obj.getElementsByTagName('select')[1].getElementsByTagName('option');
  194. for (var i=0;i<select.length;i++){
  195. if (select[i].getAttribute('selected')!=null){
  196. return(select[i].innerHTML.split(/[. ]{2}/)[1]);
  197. }
  198. }
  199. }
  200. }
  201. }