chrome & ff

This commit is contained in:
alexadam 2016-07-30 18:40:44 +03:00
parent 2c4e0d023d
commit a391509e5d
16 changed files with 404 additions and 8781 deletions

View file

@ -1,139 +0,0 @@
/*
Called when the item has been created, or when creation failed due to an error.
We'll just log success/failure here.
*/
function onCreated(n) {
if (chrome.runtime.lastError) {
console.log("error creating item:" + chrome.runtime.lastError);
} else {
console.log("item created successfully");
}
}
/*
Called when the item has been removed, or when there was an error.
We'll just log success or failure here.
*/
function onRemoved() {
if (chrome.runtime.lastError) {
console.log("error removing item:" + chrome.runtime.lastError);
} else {
console.log("item removed successfully");
}
}
/*
Create all the context menu items.
*/
chrome.contextMenus.create({
id: "whole-page",
title: 'Whole Page',
contexts: ["all"]
}, onCreated);
chrome.contextMenus.create({
id: "selection",
title: "Selection",
contexts: ["all"]
}, onCreated);
chrome.contextMenus.create({
id: "separator-1",
type: "separator",
contexts: ["all"]
}, onCreated);
chrome.contextMenus.create({
id: "edit-buffer",
title: "edit Buffer",
contexts: ["all"]
}, onCreated);
//
// chrome.contextMenus.create({
// id: "selection-to-buffer",
// title: "Selection > Buffer",
// contexts: ["all"]
// }, onCreated);
//
// chrome.contextMenus.create({
// id: "separator-2",
// type: "separator",
// contexts: ["all"]
// }, onCreated);
//
// chrome.contextMenus.create({
// id: "save-buffer",
// title: "save Buffer",
// contexts: ["all"]
// }, onCreated);
/*
Set a colored border on the document in the given tab.
Note that this only work on normal web pages, not special pages
like about:debugging.
*/
// var blue = 'document.body.style.border = "5px solid blue"';
// var green = 'document.body.style.border = "5px solid green"';
//
// function borderify(tabId, color) {
// chrome.tabs.executeScript(tabId, {
// file: 'test.js'
// });
// }
//
// /*
// Toggle checkedState, and update the menu item's title
// appropriately.
//
// Note that we should not have to maintain checkedState independently like
// this, but have to because Firefox does not currently pass the "checked"
// property into the event listener.
// */
// function updateCheckUncheck() {
// checkedState = !checkedState;
// if (checkedState) {
// chrome.contextMenus.update("check-uncheck", {
// title: chrome.i18n.getMessage("contextMenuItemUncheckMe"),
// });
// } else {
// chrome.contextMenus.update("check-uncheck", {
// title: chrome.i18n.getMessage("contextMenuItemCheckMe"),
// });
// }
// }
/*
The click event listener, where we perform the appropriate action given the
ID of the menu item that was clicked.
*/
chrome.contextMenus.onClicked.addListener(function(info, tab) {
switch (info.menuItemId) {
case "whole-page":
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "whole-page"});
});
break;
case "selection":
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "selection"});
});
break;
case "selection-to-buffer":
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "selection-to-buffer"});
});
break;
case "save-buffer":
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "save-buffer"});
});
break;
case "edit-buffer":
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "show-buffer"});
});
break;
}
});

View file

@ -8,13 +8,18 @@
<h1>Chapter Editor</h1>
<div>
<ol id="chapters">
</ol>
</div>
<div class="">
<button id="closeButton" type="button" name="button">Cancel</button>
<button id="saveButton" type="button" name="button">Save</button>
</div>
<script src="jquery.js" charset="utf-8"></script>
<script src="filesaver.js" charset="utf-8"></script>
<script src="jszip.js" charset="utf-8"></script>
<script src="jszip-utils.js" charset="utf-8"></script>
<script src="utils.js" charset="utf-8"></script>
<script src="saveEbook.js" charset="utf-8"></script>
<script src="editor.js" charset="utf-8"></script>
</body>
</html>

View file

@ -1,40 +1,30 @@
// var chapterHolder = document.getElementById('chapters');
var list = document.getElementById('chapters');
var title = localStorage.getItem('title');
if (title === null) {
title = [];
} else {
title = JSON.parse(title);
}
var allPages = getEbookPages();
for (var i = 0; i < title.length; i++) {
console.log(allPages.length);
for (var i = 0; i < allPages.length; i++) {
if (!allPages[i]) {
continue;
}
var listItem = document.createElement('li');
var label = document.createElement('span');
label.innerHTML = title[i].title;
label.innerHTML = allPages[i].title;
listItem.appendChild(label);
list.appendChild(listItem);
}
document.getElementById('closeButton').onclick = function () {
chrome.tabs.query({
currentWindow: true,
active: true
// Select active tab of the current window
}, function(tab) {
chrome.tabs.sendMessage(
// Send a message to the content script
tab[0].id, { line: 'countparas' }
);
});
};
browser.runtime.onMessage.addListener(function(request) {
console.log('cccccc', request);
});
document.getElementById('saveButton').onclick = function () {
window.open(window.location, '_self').close();
};
document.getElementById('saveButton').onclick = function () {
try {
buildEbook();
// window.open(window.location, '_self').close();
} catch (e) {
alert(e);
}
};

View file

@ -0,0 +1,257 @@
var allImgSrc = {};
//////
function getCurrentUrl() {
var url = window.location.href;
if (url.indexOf('?') > 0) {
url = window.location.href.split('?')[0];
}
url = url.substring(0, url.lastIndexOf('/')+1);
return url;
}
function getFileExtension(fileName) {
var tmpFileName = fileName.split('.').pop();
if (tmpFileName.indexOf('?') > 0) {
tmpFileName = tmpFileName.split('?')[0];
}
if (tmpFileName.trim() === '') {
return 'jpg'; //TODO
}
return tmpFileName;
}
function getImageSrc(srcTxt) {
if (!srcTxt) {
return '';
}
allImgSrc[srcTxt] = 'img-' + (Math.floor(Math.random()*1000000)) + '.' + getFileExtension(srcTxt);
return '../images/' + allImgSrc[srcTxt];
}
function getHref(hrefTxt) {
if (!hrefTxt) {
return '';
}
if (hrefTxt.indexOf('#') === 0) {
hrefTxt = window.location.href + hrefTxt;
}
if (hrefTxt.indexOf('/') === 0) {
hrefTxt = window.location.protocol + '//' + window.location.hostname + hrefTxt;
}
// hrefTxt = escape(hrefTxt); // TODO
return hrefTxt;
}
function force(contentString) {
try {
var tagOpen = '@@@';
var tagClose = '###';
var inlineElements = ['h1', 'h2', 'h3', 'sup', 'b', 'i', 'em', 'code', 'pre', 'p'];
var $content = $(contentString);
$content.find('img').each(function (index, elem) {
$(elem).replaceWith('<span>' + tagOpen + 'img src="' + getImageSrc($(elem).attr('src')) + '"' + tagClose + tagOpen + '/img' + tagClose + '</span>');
});
$content.find('a').each(function (index, elem) {
$(elem).replaceWith('<span>' + tagOpen + 'a href="' + getHref($(elem).attr('href')) + '"' + tagClose + $(elem).html() + tagOpen + '/a' + tagClose + '</span>');
});
if ($('*').length < 3000) { // TODO
inlineElements.forEach(function (tagName) {
$content.find(tagName).each(function (index, elem) {
$(elem).replaceWith('<span>' + tagOpen + tagName + tagClose + $(elem).html() + tagOpen + '/' + tagName + tagClose + '</span>');
});
});
}
contentString = $content.text();
var tagOpenRegex = new RegExp(tagOpen, 'gi');
var tagCloseRegex = new RegExp(tagClose, 'gi');
contentString = contentString.replace(tagOpenRegex, '<');
contentString = contentString.replace(tagCloseRegex, '>');
contentString = contentString.replace(/&amp;/gi, '&'); // TODO ??
contentString = contentString.replace(/&/gi, '&amp;');
return contentString;
} catch(e) {
console.log('ERROR');
console.log(e);
}
}
// https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
function sanitize(rawContentString) {
allImgSrc = {};
var srcTxt = '';
var dirty = null;
try {
// dirty = getHtmlAsString(rawContent);
wdirty = $.parseHTML(rawContentString);
$wdirty = $(wdirty);
$wdirty.find('script, style, svg, canvas, noscript').remove();
$wdirty.find('*:empty').not('img').remove();
dirty = '<div>' + $wdirty.html() + '</div>';
////////////////
return force(dirty); // TODO
// var dirty = '<div>' + document.getElementsByTagName('body')[0].innerHTML + '</div>';
var results = '';
var lastFragment = '';
var lastTag = '';
var inList = false;
var allowedTags = ['div', 'p', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'blockquote',
'img', 'a', 'ol', 'ul', 'li', 'b', 'i', 'sup', 'strong', 'strike',
'table', 'tr', 'td', 'th', 'thead', 'tbody', 'pre', 'em'
];
var allowedTextTags = ['h4', 'h5', 'h6', 'span'];
HTMLParser(dirty, {
start: function(tag, attrs, unary) {
lastTag = tag;
if (allowedTags.indexOf(tag) < 0) {
return;
}
if (tag === 'ol' || tag === 'ul') {
inList = true;
}
if (tag === 'li' && !inList) {
tag = 'p';
}
var tattrs = null;
if (tag === 'img') {
tattrs = attrs.filter(function(attr) {
return attr.name === 'src';
}).map(function(attr) {
return getImageSrc(attr.escaped);
});
lastFragment = tattrs.length === 0 ? '<img></img>' : '<img src="' + tattrs[0] + '" alt=""></img>';
} else if (tag === 'a') {
tattrs = attrs.filter(function(attr) {
return attr.name === 'href';
}).map(function(attr) {
return getHref(attr.escaped);
});
lastFragment = tattrs.length === 0 ? '<a>' : '<a href="' + tattrs[0] + '">';
} else {
lastFragment = '<' + tag + '>';
}
results += lastFragment;
lastFragment = '';
},
end: function(tag) {
if (allowedTags.indexOf(tag) < 0 || tag === 'img') {
return;
}
if (tag === 'ol' || tag === 'ul') {
inList = false;
}
if (tag === 'li' && !inList) {
tag = 'p';
}
results += "</" + tag + ">\n";
},
chars: function(text) {
if (lastTag !== '' && allowedTags.indexOf(lastTag) < 0) {
return;
}
results += text;
},
comment: function(text) {
// results += "<!--" + text + "-->";
}
});
// results = results.replace(/<([^>]+?)>\s*<\/\1>/gim, '');
results = results.replace(/&[a-z]+;/gim, '');
return results;
} catch (e) {
console.trace();
console.log(e);
return force(dirty);
}
}
function getContent(htmlContent) {
try {
var tmp = document.createElement('div');
tmp.appendChild(htmlContent.cloneNode(true));
var dirty = '<div>' + tmp.innerHTML + '</div>';
return sanitize(dirty);
} catch (e) {
console.log(e);
return '';
}
}
/////
function getPageUrl(url) {
return url.toLowerCase().replace(/\s+/g,'_').replace(/[^a-z0-9_]/g,'') + Math.floor(Math.random() * 10000) + '.xhtml';
}
function getPageTitle(inp) { //TODO
return inp;
}
function getSelectedNodes() {
if (document.selection) {
// return document.selection.createRange().parentElement();
return document.selection.createRange();
}
var selection = window.getSelection();
if (selection.rangeCount > 0) {
var range = selection.getRangeAt(0);
var selectionContents = range.cloneContents();
return selectionContents;
}
}
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log('Extract Html...');
allImgSrc = {};
var result = {};
var pageSrc = '';
var tmpContent = '';
if (request.type === 'extract-page') {
pageSrc = document.getElementsByTagName('body')[0];
tmpContent = getContent(pageSrc);
} else if (request.type === 'extract-selection') {
pageSrc = getSelectedNodes();
tmpContent = getContent(pageSrc);
}
if (tmpContent.trim() === '') {
return;
}
result = {
url: getPageUrl(document.title),
title: getPageTitle(document.title), //gatPageTitle(document.title),
baseUrl: getCurrentUrl(),
imgs: allImgSrc,
content: tmpContent
};
console.log('Html Extracted');
sendResponse(result);
});

View file

@ -3,29 +3,63 @@
<head>
<meta charset="utf-8">
<style>
.row {
width: 320px;
margin-bottom: 5px;
}
.menu-holder {
padding: 5px;
padding-right: 10px;
margin: 0;
width: 320px;
/*height: 500px;*/
}
.menu-item {
display: block;
display: inline-block;
width: 49%;
}
.menu-item-full {
display: inline-block;
width: 100%;
margin: 5px;
}
ul, li {
display: block;
width: 300px;
margin: 0;
padding: 0;
}
li {
border-bottom: solid 1px #ccc;
padding: 5px 0;
}
</style>
</head>
<body>
<div class="menu-holder">
<h3>Save as Ebook:</h3>
<button id="savePage" type="button" name="button" class="menu-item">Save Page</button>
<button id="saveSelection" type="button" name="button" class="menu-item">Save Selection</button>
<div class="row">
<b>Save as Ebook:</b>
</div>
<div class="row">
<button id="savePage" type="button" name="button" class="menu-item">Save Page</button>
<button id="saveSelection" type="button" name="button" class="menu-item">Save Selection</button>
</div>
<hr/>
<button id="pageChapter" type="button" name="button" class="menu-item">Add Page as Chapter</button>
<button id="selectionChapter" type="button" name="button" class="menu-item">Add Selection as Chapter</button>
<div class="row">
<button id="pageChapter" type="button" name="button" class="menu-item">Add Page as Chapter</button>
<button id="selectionChapter" type="button" name="button" class="menu-item">Add Selection as Chapter</button>
</div>
<hr/>
<button id="editChapters" type="button" name="button" class="menu-item">Edit Chapters...</button>
<div class="row">
<button id="editChapters" type="button" name="button" class="menu-item-full">Edit Chapters...</button>
</div>
<div class="row">
<ul id="chapters" class="menu-item-full"></ul>
<button id="saveChapters" type="button" name="button" class="menu-item-full">Save Chapters</button>
</div>
</div>

View file

@ -1,71 +1,95 @@
var win = null;
document.getElementById("editChapters").onclick = function() {
win = window.open(chrome.extension.getURL('chapter-editor/editor.html'), '_blank');
win.focus();
// win = window.open(chrome.extension.getURL('chapter-editor/editor.html'), '_blank');
// win.focus();
// chrome.tabs.create({url:"editor.html"});
var list = document.getElementById('chapters');
var allPages = getEbookPages();
for (var i = 0; i < allPages.length; i++) {
var listItem = document.createElement('li');
var label = document.createElement('span');
label.innerHTML = allPages[i].title;
label.class = 'menu-item-full';
listItem.appendChild(label);
list.appendChild(listItem);
}
};
document.getElementById('savePage').onclick = function() {
localStorage.removeItem('ebook');
function dispatch(action, justAddToBuffer) {
if (!justAddToBuffer) {
localStorage.removeItem('ebook');
}
chrome.tabs.query({
currentWindow: true,
active: true
}, function(tab) {
chrome.tabs.sendMessage(
tab[0].id,
{
type: 'extract-page'
},
function (response) {
// chrome.tabs.sendMessage(
// tab[0].id, {
// type: action
// },
// function(response) {
// alert('nnn 3' + response);
// var allPages = getEbookPages();
// allPages.push(response);
// saveEbookPages(allPages);
// if (!justAddToBuffer) {
// buildEbook();
// }
// }
// );
chrome.tabs.executeScript(tab[0].id, {
file: 'chapter-editor/jquery.js'
});
chrome.tabs.executeScript(tab[0].id, {
file: 'pure-parser.js'
});
chrome.tabs.executeScript(tab[0].id, {
file: 'extractHtml.js'
}, function() {
// if (chrome.runtime.lastError) {
// alert(JSON.stringify(chrome.runtime.lastError));
// throw Error("Unable to inject script into tab " + tabId);
// }
chrome.tabs.sendMessage(tab[0].id, {
type: action
}, function(response) {
var allPages = getEbookPages();
allPages.push(response);
saveEbookPages(allPages);
buildEbook();
}
);
if (!justAddToBuffer) {
buildEbook();
}
});
});
});
}
document.getElementById('savePage').onclick = function() {
dispatch('extract-page', false);
};
document.getElementById('saveSelection').onclick = function() {
localStorage.removeItem('ebook');
chrome.tabs.query({
currentWindow: true,
active: true
}, function(tab) {
chrome.tabs.sendMessage(
tab[0].id,
{
type: 'extract-selection'
},
function (response) {
var allPages = getEbookPages();
allPages.push(response);
saveEbookPages(allPages);
buildEbook();
}
);
});
dispatch('extract-selection', false);
};
document.getElementById('title').onclick = function() {
chrome.tabs.query({
currentWindow: true,
active: true
}, function(tab) {
chrome.tabs.sendMessage(
tab[0].id, {
type: 'get-title'
},
function(response) {
var title = localStorage.getItem('title');
if (title === null) {
title = [];
} else {
title = JSON.parse(title);
}
title.push(response);
localStorage.setItem('title', JSON.stringify(title));
}
);
});
document.getElementById('pageChapter').onclick = function() {
dispatch('extract-page', true);
};
document.getElementById('selectionChapter').onclick = function() {
dispatch('extract-selection', true);
};
document.getElementById('saveChapters').onclick = function() {
buildEbook();
};

View file

@ -74,7 +74,6 @@ function deferredAddZip(url, filename, zip) {
// http://ebooks.stackexchange.com/questions/1183/what-is-the-minimum-required-content-for-a-valid-epub
function buildEbook() {
console.log('Prepare Content...');
var allPages = getEbookPages();
var zip = new JSZip();
@ -185,7 +184,6 @@ function buildEbook() {
///////////////
///////////////
var imgs = oebps.folder("images");

View file

@ -1,5 +1,4 @@
var allImgSrc = {};
//////
function getCurrentUrl() {
@ -253,6 +252,6 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
content: tmpContent
};
sendResponse(result);
console.log('Html Extracted');
sendResponse(result);
});

View file

@ -1,9 +0,0 @@
@import 'https://fonts.googleapis.com/css?family=Galada';
* {
/*font-family: 'Galada', cursive !important;*/
}
body, html {
/*font-family: 'Galada', cursive !important;*/
}

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -12,16 +12,15 @@
"content_scripts": [{
"matches": ["<all_urls>"],
"css": ["fonts.css"],
"js": ["chapter-editor/jquery.js", "filesaver.js", "jszip.js", "jszip-utils.js", "pure-parser.js", "extractHtml.js"]
"js": ["chapter-editor/jquery.js", "pure-parser.js", "extractHtml.js"]
}],
"background": {
"scripts": ["background2.js"]
"scripts": []
},
"browser_action": {
"default_icon": "",
"default_icon": "icon.png",
"default_title": "Beastify",
"default_popup": "chapter-editor/menu.html"
},
@ -29,7 +28,8 @@
"permissions": [
"contextMenus",
"activeTab",
"storage"
"storage",
"tabs"
]
}

View file

@ -1,5 +0,0 @@
chrome.runtime.onMessage.addListener(function(request) {
console.log('rrrrrrrrrr', request.type);
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,37 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test ebook</title>
</head>
<body>
<!-- <div class="contect">
<p>
Lorem ipsum dolor sit amet, sit ne eius falli clita. An eum viderer dolorem, et clita mnesarchum mei. Nec dico congue ea, cum eu enim similique. Denique oporteat at nam, pri saperet pericula conceptam eu, per summo omnium suavitate ad. Aliquip accumsan detracto id vel, eu habeo eripuit salutatus has, his laoreet vituperata at.
</p>
<p>
Lorem ipsum dolor sit amet, sit ne eius falli clita. An eum viderer dolorem, et clita mnesarchum mei. Nec dico congue ea, cum eu enim similique. Denique oporteat at nam, pri saperet pericula conceptam eu, per summo omnium suavitate ad. Aliquip accumsan detracto id vel, eu habeo eripuit salutatus has, his laoreet vituperata at.
Lorem ipsum dolor sit amet, sit ne eius falli clita. An eum viderer dolorem, et clita mnesarchum mei. Nec dico congue ea, cum eu enim similique. Denique oporteat at nam, pri saperet pericula conceptam eu, per summo omnium suavitate ad. Aliquip accumsan detracto id vel, eu habeo eripuit salutatus has, his laoreet vituperata at.
</p>
<p>
Lorem ipsum dolor sit amet, <a href="https://www.google.com">google</a> sit ne eius falli clita. An eum viderer dolorem, et clita mnesarchum mei. Nec dico congue ea, cum eu enim similique. Denique oporteat at nam, pri saperet pericula conceptam eu, per summo omnium suavitate ad. Aliquip accumsan detracto id vel, eu habeo eripuit salutatus has, his laoreet vituperata at.
</p>
<p>
Lorem ipsum dolor sit amet, sit ne eius falli clita. An eum viderer dolorem, et clita mnesarchum mei.
Nec dico congue ea, cum eu enim similique.
<img src="http://i.imgur.com/khaB4fN.png" alt="" />
Denique oporteat at nam, pri saperet pericula conceptam eu, per summo omnium suavitate ad.
Aliquip accumsan detracto id vel, eu habeo eripuit salutatus has, his laoreet vituperata at.
</p>
</div> -->
<div class="content">
<p>p1</p>
<p>p2</p>
<p>p3</p>
<p>p4 <a href="https://www.google.com">google</a></p>
<p>p5</p>
<p>p6 <img src="http://i.imgur.com/khaB4fN.png" alt="" /></p>
<p>p7</p>
</div>
</body>
</html>

533
test.js
View file

@ -1,533 +0,0 @@
// document.body.style.border = "5px solid red";
// https://stuk.github.io/jszip/
// https://github.com/eligrey/FileSaver.js/
var cssFileName = 'ebook.css';
var pageName = 'ebook.xhtml';
var ebookName = "ebook-" + document.title + ".epub";
var imageIndex = 0;
var allImgSrc = {};
var allExternalLinks = [];
//////
function getHtmlAsString(htmlContent) {
try {
var tmp = document.createElement('div');
tmp.appendChild(htmlContent.cloneNode(true));
var dirty = '<div>' + tmp.innerHTML + '</div>';
return dirty;
} catch (e) {
console.log(e);
return '';
}
}
function force(contentString) {
try {
var tagOpen = '@@@';
var tagClose = '###';
var inlineElements = ['h1', 'h2', 'h3', 'sup', 'b', 'i', 'em', 'code', 'pre', 'p'];
var $content = $(contentString);
$content.find('img').each(function (index, elem) {
$(elem).replaceWith('<span>' + tagOpen + 'img src="' + getImageSrc($(elem).attr('src')) + '"' + tagClose + tagOpen + '/img' + tagClose + '</span>');
});
$content.find('a').each(function (index, elem) {
$(elem).replaceWith('<span>' + tagOpen + 'a href="' + getHref($(elem).attr('href')) + '"' + tagClose + $(elem).html() + tagOpen + '/a' + tagClose + '</span>');
});
if ($('*').length < 3000) { // TODO
inlineElements.forEach(function (tagName) {
$content.find(tagName).each(function (index, elem) {
$(elem).replaceWith('<span>' + tagOpen + tagName + tagClose + $(elem).html() + tagOpen + '/' + tagName + tagClose + '</span>');
});
});
}
contentString = $content.text();
var tagOpenRegex = new RegExp(tagOpen, 'gi');
var tagCloseRegex = new RegExp(tagClose, 'gi');
contentString = contentString.replace(tagOpenRegex, '<');
contentString = contentString.replace(tagCloseRegex, '>');
contentString = contentString.replace(/&amp;/gi, '&'); // TODO ??
contentString = contentString.replace(/&/gi, '&amp;');
return contentString;
} catch(e) {
console.log(e);
}
}
//////
function getImageSrc(srcTxt) {
if (!srcTxt) {
return '';
}
allImgSrc[srcTxt] = 'img-' + (imageIndex++) + '.' + getFileExtension(srcTxt);
return '../images/' + allImgSrc[srcTxt];
}
function getHref(hrefTxt) {
if (!hrefTxt) {
return '';
}
if (hrefTxt.indexOf('#') === 0) {
hrefTxt = window.location.href + hrefTxt;
}
if (hrefTxt.indexOf('/') === 0) {
hrefTxt = window.location.protocol + '//' + window.location.hostname + hrefTxt;
}
// hrefTxt = escape(hrefTxt); // TODO
return hrefTxt;
}
// https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
function sanitize(rawContentString) {
var srcTxt = '';
var dirty = null;
try {
// dirty = getHtmlAsString(rawContent);
wdirty = $.parseHTML(rawContentString);
$wdirty = $(wdirty);
$wdirty.find('script, style, svg, canvas, noscript').remove();
$wdirty.find('*:empty').not('img').remove();
dirty = $wdirty.html();
// dirty = dirty.replace(/&nbsp;/gi, '');
// dirty = HTMLtoXML(dirty);
////////////////
return force(dirty);
// var dirty = '<div>' + document.getElementsByTagName('body')[0].innerHTML + '</div>';
var results = '';
var lastFragment = '';
var lastTag = '';
var inList = false;
var allowedTags = ['div', 'p', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'blockquote',
'img', 'a', 'ol', 'ul', 'li', 'b', 'i', 'sup', 'strong', 'strike',
'table', 'tr', 'td', 'th', 'thead', 'tbody', 'pre', 'em'
];
var allowedTextTags = ['h4', 'h5', 'h6', 'span'];
HTMLParser(dirty, {
start: function(tag, attrs, unary) {
lastTag = tag;
if (allowedTags.indexOf(tag) < 0) {
return;
}
if (tag === 'ol' || tag === 'ul') {
inList = true;
}
if (tag === 'li' && !inList) {
tag = 'p';
}
var tattrs = null;
if (tag === 'img') {
tattrs = attrs.filter(function(attr) {
return attr.name === 'src';
}).map(function(attr) {
return getImageSrc(attr.escaped);
});
lastFragment = tattrs.length === 0 ? '<img></img>' : '<img src="' + tattrs[0] + '" alt=""></img>';
} else if (tag === 'a') {
tattrs = attrs.filter(function(attr) {
return attr.name === 'href';
}).map(function(attr) {
return getHref(attr.escaped);
});
lastFragment = tattrs.length === 0 ? '<a>' : '<a href="' + tattrs[0] + '">';
} else {
lastFragment = '<' + tag + '>';
}
results += lastFragment;
lastFragment = '';
},
end: function(tag) {
if (allowedTags.indexOf(tag) < 0 || tag === 'img') {
return;
}
if (tag === 'ol' || tag === 'ul') {
inList = false;
}
if (tag === 'li' && !inList) {
tag = 'p';
}
results += "</" + tag + ">\n";
},
chars: function(text) {
if (lastTag !== '' && allowedTags.indexOf(lastTag) < 0) {
return;
}
results += text;
},
comment: function(text) {
// results += "<!--" + text + "-->";
}
});
// results = results.replace(/<([^>]+?)>\s*<\/\1>/gim, '');
results = results.replace(/&[a-z]+;/gim, '');
return results;
} catch (e) {
console.trace();
console.log(e);
return force(dirty);
}
}
function getPageUrl(url) {
return url.toLowerCase().replace(/\s+/g,'_').replace(/[^a-z0-9_]/g,'') + Math.floor(Math.random() * 10000) + '.xhtml';
}
var pageIndex = 0;
function getPageTitle(title) {
try {
if (title.trim() === '') {
return 'page-' + pageIndex++;
}
var tmp = title;
return title;
} catch (e) {
console.log(e);
}
}
function bibi(inp) {
return inp;
}
function getEbookPages() {
try {
var allPages = localStorage.getItem('ebook');
if (!allPages) {
allPages = [];
} else {
allPages = JSON.parse(allPages);
}
return allPages;
} catch (e) {
console.log(e);
return [];
}
}
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log('Start saving...');
var allPages = getEbookPages();
alert();
var pageSrc = '';
if (request.type === 'whole-page') {
pageSrc = document.getElementsByTagName('body')[0];
allPages.push({
url: getPageUrl(document.title),
title: bibi(document.title), //gatPageTitle(document.title),
content: getHtmlAsString(pageSrc)
});
localStorage.setItem('ebook', JSON.stringify(allPages));
buildEbook();
} else if (request.type === 'selection') {
pageSrc = getSelectedNodes();
allPages.push({
url: getPageUrl(document.title),
title: bibi(document.title),
content: getHtmlAsString(pageSrc)
});
console.log('PUSH', JSON.stringify(allPages));
localStorage.setItem('ebook', JSON.stringify(allPages));
buildEbook();
} else if (request.type === 'page-to-buffer') {
pageSrc = document.getElementsByTagName('body')[0];
allPages.push({
url: getPageUrl(document.title),
title: bibi(document.title), //gatPageTitle(document.title),
content: getHtmlAsString(pageSrc)
});
console.log('merge');
localStorage.setItem('ebook', JSON.stringify(allPages));
} else if (request.type === 'show-buffer') {
// window.open(chrome.extension.getURL('chapter-editor/chapter-editor.html'), 'Chapter Editor');
chrome.tabs.create({
url: chrome.extension.getURL('chapter-editor/chapter-editor.html'),
active: false
}, function(tab) {
// After the tab has been created, open a window to inject the tab
chrome.windows.create({
tabId: tab.id,
type: 'popup',
focused: true
// incognito, top, left, ...
});
});
}
});
function getSelectedNodes() {
if (document.selection) {
// return document.selection.createRange().parentElement();
return document.selection.createRange();
} else {
var selection = window.getSelection();
if (selection.rangeCount > 0) {
var range = selection.getRangeAt(0);
var selectionContents = range.cloneContents();
return selectionContents;
// console.log(selectionContents.children.length, selectionContents.children[0].outerHTML);
}
// return selection.getRangeAt(0);
// return selection.createRange();
}
}
function prepareEbookContent(page) {
var cleanContent = sanitize(page.content);
alert();
return '<?xml version="1.0" encoding="utf-8"?>' +
'<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">' +
'<head>' +
'<title>' + page.title + '</title>' +
'<link href="' + cssFileName + '" rel="stylesheet" type="text/css" />' +
'</head><body>' +
cleanContent +
'</body></html>';
}
function getImagesIndex() {
return Object.keys(allImgSrc).reduce(function(prev, elem, index) {
return prev + '\n' + '<item href="images/' + allImgSrc[elem] + '" id="img' + index + '" media-type="image/' + getFileExtension(elem) + '"/>';
}, '');
}
function getExternalLinksIndex() { // TODO ???
return allExternalLinks.reduce(function(prev, elem, index) {
return prev + '\n' + '<item href="' + elem + '" />';
}, '');
}
function getFileExtension(fileName) {
var tmpFileName = fileName.split('.').pop();
if (tmpFileName.indexOf('?') > 0) {
tmpFileName = tmpFileName.split('?')[0];
}
if (tmpFileName.trim() === '') {
return 'jpg'; //TODO
}
return tmpFileName;
}
// function walkDOM(main) {
// var arr = [];
// var loop = function(main) {
// do {
// try {
// if (allowElements.indexOf(main.tagName.toLowerCase()) > -1) {
// arr.push(main);
// }
// } catch (e) {
// }
// if (main.hasChildNodes()) {
// loop(main.firstChild);
// }
// }
// while (main = main.nextSibling);
// }
// loop(main);
// return arr;
// }
function deferredAddZip(url, filename, zip) {
var deferred = $.Deferred();
JSZipUtils.getBinaryContent(url, function(err, data) {
if (err) {
deferred.reject(err);
} else {
zip.file(filename, data, {
binary: true
});
deferred.resolve(data);
}
});
return deferred;
}
// http://ebooks.stackexchange.com/questions/1183/what-is-the-minimum-required-content-for-a-valid-epub
function buildEbook() {
var allPages = getEbookPages();
var zip = new JSZip();
zip.file('mimetype', 'application/epub+zip');
var metaInfFolder = zip.folder("META-INF");
metaInfFolder.file('container.xml',
'<?xml version="1.0"?>' +
'<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">' +
'<rootfiles>' +
'<rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>' +
'</rootfiles>' +
'</container>'
);
var oebps = zip.folder("OEBPS");
oebps.file('toc.xhtml',
'<?xml version="1.0" encoding="utf-8"?>' +
'<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">' +
'<head>' +
'<title>toc.xhtml</title>' +
'<link href="' + cssFileName + '" rel="stylesheet" type="text/css" />' +
'</head>' +
'<body>' +
'<nav id="toc" epub:type="toc">' +
'<h1 class="frontmatter">Table of Contents</h1>' +
'<ol class="contents">' +
// '<li><a href="pages/' + pageName + '">' + ebookName + '</a></li>' +
allPages.reduce(function (prev, page) {
return prev + '\n' + '<li><a href="pages/' + page.url + '">' + page.title + '</a></li>';
}, '') +
'</ol>' +
'</nav>' +
'</body>' +
'</html>'
);
oebps.file('toc.ncx',
'<?xml version="1.0" encoding="UTF-8" ?>' +
'<ncx version="2005-1" xml:lang="en" xmlns="http://www.daisy.org/z3986/2005/ncx/">' +
'<head>' +
'<meta name="dtb:uid" content="isbn"/>' +
'<meta name="dtb:depth" content="1"/>' +
'</head>' +
'<docTitle>' +
'<text></text>' +
'</docTitle>' +
'<navMap>' +
// '<content src="pages/' + pageName + '" />' +
allPages.reduce(function (prev, page, index) {
return prev + '\n' +
'<navPoint id="ebook' + index + '" playOrder="' + (index+1) + '">' +
'<navLabel><text>' + page.title + '</text></navLabel>' +
'<content src="pages/' + page.url + '" />' +
'</navPoint>';
}, '') +
'</navMap>' +
'</ncx>'
);
oebps.file(cssFileName, '');
var pagesFolder = oebps.folder('pages');
allPages.forEach(function (page) {
pagesFolder.file(page.url,
prepareEbookContent(page)
);
});
oebps.file('content.opf',
'<?xml version="1.0" encoding="UTF-8" ?>' +
'<package xmlns="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/" unique-identifier="db-id" version="3.0">' +
'<metadata>' +
'<dc:title id="t1">Title</dc:title>' +
'<dc:identifier id="db-id">isbn</dc:identifier>' +
'<meta property="dcterms:modified">2014-03-27T09:14:09Z</meta>' +
'<dc:language>en</dc:language>' +
'</metadata>' +
'<manifest>' +
'<item id="toc" properties="nav" href="toc.xhtml" media-type="application/xhtml+xml" />' +
'<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />' +
'<item id="template_css" href="' + cssFileName + '" media-type="text/css" />' +
// '<item id="ebook" href="pages/' + pageName + '" media-type="application/xhtml+xml" />' + //properties="remote-resources"
allPages.reduce(function (prev, page, index) {
return prev + '\n' + '<item id="ebook' + index + '" href="pages/' + page.url + '" media-type="application/xhtml+xml" />';
}, '') +
getImagesIndex() +
getExternalLinksIndex() +
'</manifest>' +
'<spine toc="ncx">' +
// '<itemref idref="ebook" />' +
allPages.reduce(function (prev, page, index) {
return prev + '\n' + '<itemref idref="ebook' + index + '" />';
}, '') +
'</spine>' +
'</package>'
);
///////////////
///////////////
var imgs = oebps.folder("images");
var imgsPromises = [];
// allImgSrc.forEach(function (imgSrc, index) {
Object.keys(allImgSrc).forEach(function(imgSrc, index) {
var tmpDeffered = deferredAddZip(imgSrc, allImgSrc[imgSrc], imgs);
imgsPromises.push(tmpDeffered);
});
var done = false;
$.when.apply($, imgsPromises).done(function() {
done = true;
zip.generateAsync({
type: "blob"
})
.then(function(content) {
saveAs(content, ebookName);
});
console.log("done !");
}).fail(function(err) {
alert(err);
});
setTimeout(function() {
if (done) {
return;
}
zip.generateAsync({
type: "blob"
})
.then(function(content) {
saveAs(content, ebookName);
});
}, 60000);
///////////// clean
localStorage.removeItem('ebook');
allImgSrc = {};
imageIndex = 0;
}