mirror of
https://github.com/alexadam/save-as-ebook.git
synced 2025-09-10 09:24:49 +00:00
styles - wip
This commit is contained in:
parent
9f3d349ffe
commit
7dd34efee5
10 changed files with 1119 additions and 38 deletions
|
@ -1,3 +1,5 @@
|
|||
// var GLOBAL_CURRENT_STYLE = null;
|
||||
|
||||
|
||||
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
||||
if (request.type === 'get') {
|
||||
|
@ -27,5 +29,29 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
if (request.type === 'set title') {
|
||||
chrome.storage.local.set({'title': request.title});
|
||||
}
|
||||
if (request.type === 'get styles') {
|
||||
chrome.storage.local.get('styles', function (data) {
|
||||
if (!data || !data.styles) {
|
||||
sendResponse({styles: []});
|
||||
} else {
|
||||
sendResponse({styles: data.styles});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (request.type === 'set styles') {
|
||||
chrome.storage.local.set({'styles': request.styles});
|
||||
}
|
||||
if (request.type === 'get current style') {
|
||||
chrome.storage.local.get('currentStyle', function (data) {
|
||||
if (!data || !data.currentStyle) {
|
||||
sendResponse({currentStyle: 0});
|
||||
} else {
|
||||
sendResponse({currentStyle: data.currentStyle});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (request.type === 'set current style') {
|
||||
chrome.storage.local.set({'currentStyle': request.currentStyle});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
|
181
web-extension/cssEditor.css
Normal file
181
web-extension/cssEditor.css
Normal file
|
@ -0,0 +1,181 @@
|
|||
body.dragging, body.dragging * {
|
||||
cursor: move !important;
|
||||
}
|
||||
.dragged {
|
||||
position: absolute;
|
||||
opacity: 0.5;
|
||||
z-index: 2000;
|
||||
}
|
||||
ul.cssEditor-chapters-list li.placeholder {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: solid 10px #f5f5f5;
|
||||
}
|
||||
|
||||
#cssEditor-Title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
font-family: "sans-serif";
|
||||
}
|
||||
|
||||
#cssEditor-ebookTitleHolder {
|
||||
background-color: #eee;
|
||||
padding: 10px 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#cssEditor-ebookTitleLabel {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-family: sans-serif;
|
||||
margin-right: 10px;
|
||||
}
|
||||
#cssEditor-ebookTitle {
|
||||
padding: 5px;
|
||||
font-size: 15px;
|
||||
font-family: "sans-serif";
|
||||
width: 85%;
|
||||
}
|
||||
ul, ul.cssEditor-chapters-list {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none !important;
|
||||
}
|
||||
.cssEditor-chapter-item {
|
||||
display: block;
|
||||
line-height: 35px;
|
||||
height: 35px;
|
||||
padding: 5px 0;
|
||||
vertical-align: middle;
|
||||
font-size: 17px;
|
||||
list-style-type: none;
|
||||
}
|
||||
.cssEditor-chapter-item:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.cssEditor-drag-handler {
|
||||
cursor: move;
|
||||
padding: 3px;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.cssEditor-chapter-item > input[type="text"] {
|
||||
padding: 5px;
|
||||
font-size: 15px;
|
||||
font-family: "sans-serif";
|
||||
width: 80%;
|
||||
border: none;
|
||||
border-bottom: solid 1px #aaa;
|
||||
}
|
||||
|
||||
#cssEditor-modalHeader {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: 20px;
|
||||
}
|
||||
#cssEditor-modalList {
|
||||
display: block;
|
||||
/*width: 100%;*/
|
||||
/*border-top: solid 1px black;*/
|
||||
/*border-bottom: solid 1px black;*/
|
||||
padding: 20px;
|
||||
padding-top: 0;
|
||||
}
|
||||
#cssEditor-modalFooter {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
.chapter-item * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
#dragHandler {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
|
||||
.cssEditor-text-button {
|
||||
border: none;
|
||||
font-size: 15px;
|
||||
font-family: "sans-serif";
|
||||
padding: 5px;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
margin: 0 3px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.cssEditor-text-button:hover {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
.cssEditor-text-red {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.cssEditor-float-left {
|
||||
float: left;
|
||||
}
|
||||
.cssEditor-float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.cssEditor-footer-button {
|
||||
padding: 18px 20px;
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
border: none;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
display: inline-block;
|
||||
}
|
||||
.cssEditor-footer-button:hover {
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
cursor: pointer;
|
||||
}
|
||||
.cssEditor-save-button {
|
||||
background-color: yellow;
|
||||
}
|
||||
.cssEditor-cancel-button {
|
||||
/*background-color: black;*/
|
||||
}
|
||||
|
||||
@media (max-width: 1700px) {
|
||||
.cssEditor-chapter-item {
|
||||
line-height: 30px;
|
||||
height: 30px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.cssEditor-chapter-item > input[type="text"] {
|
||||
padding: 3px;
|
||||
font-size: 12px;
|
||||
width: 75%;
|
||||
}
|
||||
.cssEditor-text-button {
|
||||
font-size: 12px;
|
||||
padding: 3px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
.cssEditor-footer-button {
|
||||
padding: 15px 20px;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
border: none;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.cssEditor-chapter-item {
|
||||
line-height: 30px;
|
||||
height: 60px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.cssEditor-chapter-item > input[type="text"] {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
356
web-extension/cssEditor.js
Normal file
356
web-extension/cssEditor.js
Normal file
|
@ -0,0 +1,356 @@
|
|||
for (var i=0; i<document.styleSheets.length; i++) {
|
||||
document.styleSheets.item(i).disabled = true;
|
||||
}
|
||||
|
||||
var tmp = document.getElementById('cssEditor-Modal');
|
||||
if (tmp) {
|
||||
tmp.parentNode.removeChild(tmp);
|
||||
}
|
||||
|
||||
var allPagesRef = null;
|
||||
var allStyles = [{
|
||||
title: 'reddit',
|
||||
url: 'reddit.com',
|
||||
style: `
|
||||
.class: {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
}];
|
||||
var currentStyle = null;
|
||||
var currentStyleIndex = -1;
|
||||
|
||||
|
||||
showEditor();
|
||||
|
||||
|
||||
function showEditor() {
|
||||
|
||||
var body = document.getElementsByTagName('body')[0];
|
||||
var modalContent = document.createElement('div');
|
||||
modalContent.id = 'cssEditor-modalContent';
|
||||
var modalHeader = document.createElement('div');
|
||||
modalHeader.id = 'cssEditor-modalHeader';
|
||||
var modalList = document.createElement('div');
|
||||
modalList.id = 'cssEditor-modalList';
|
||||
var modalFooter = document.createElement('div');
|
||||
modalFooter.id = 'cssEditor-modalFooter';
|
||||
|
||||
////////
|
||||
// Header
|
||||
var title = document.createElement('span');
|
||||
title.id = "cssEditor-Title";
|
||||
title.innerText = "Style Editor";
|
||||
var upperCloseButton = document.createElement('button');
|
||||
modalHeader.appendChild(title);
|
||||
upperCloseButton.onclick = closeModal;
|
||||
upperCloseButton.innerText = 'X';
|
||||
upperCloseButton.className = 'cssEditor-text-button cssEditor-float-right';
|
||||
modalHeader.appendChild(upperCloseButton);
|
||||
/////////////////////
|
||||
// Content List
|
||||
|
||||
var titleHolder = document.createElement('div');
|
||||
titleHolder.id = 'cssEditor-ebookTitleHolder';
|
||||
|
||||
var existingStyles = document.createElement('select');
|
||||
existingStyles.id = "selectStyle";
|
||||
existingStyles.onchange = function (event) {
|
||||
if (existingStyles.selectedIndex === 0) {
|
||||
currentStyle = null;
|
||||
currentStyleIndex = -1;
|
||||
hideStyleEditor();
|
||||
return;
|
||||
}
|
||||
currentStyle = allStyles[existingStyles.selectedIndex - 1];
|
||||
currentStyleIndex = existingStyles.selectedIndex - 1;
|
||||
editCurrentStyle();
|
||||
};
|
||||
var defaultOption = document.createElement('option');
|
||||
defaultOption.innerText = 'Select Existing CSS';
|
||||
existingStyles.appendChild(defaultOption);
|
||||
titleHolder.appendChild(existingStyles);
|
||||
|
||||
var titleLabel = document.createElement('label');
|
||||
titleLabel.innerText = ' or ';
|
||||
titleHolder.appendChild(titleLabel);
|
||||
|
||||
var createNewStyleButton = document.createElement('button');
|
||||
createNewStyleButton.innerText = 'Create New Style';
|
||||
createNewStyleButton.className = 'cssEditor-cancel-button';
|
||||
createNewStyleButton.onclick = createNewStyle;
|
||||
titleHolder.appendChild(createNewStyleButton);
|
||||
|
||||
modalList.appendChild(titleHolder);
|
||||
|
||||
function createNewStyle() {
|
||||
currentStyle = null;
|
||||
currentStyleIndex = -1;
|
||||
resetFields();
|
||||
showStyleEditor();
|
||||
hideRemoveStyle();
|
||||
createStyleList();
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
var editorHolder = document.createElement('div');
|
||||
editorHolder.style.display = 'none';
|
||||
editorHolder.id = 'cssEditor-styleEditor';
|
||||
|
||||
var nameLabelHolder = document.createElement('div');
|
||||
var nameLabel = document.createElement('label');
|
||||
nameLabel.innerText = 'Name';
|
||||
nameLabelHolder.appendChild(nameLabel);
|
||||
editorHolder.appendChild(nameLabelHolder);
|
||||
|
||||
var nameInputHolder = document.createElement('div');
|
||||
var cssNameInput = document.createElement('input');
|
||||
cssNameInput.id = 'cssEditor-styleName';
|
||||
cssNameInput.type = 'text';
|
||||
nameInputHolder.appendChild(cssNameInput);
|
||||
editorHolder.appendChild(nameInputHolder);
|
||||
|
||||
|
||||
var urlLabelHolder = document.createElement('div');
|
||||
var urlLabel = document.createElement('label');
|
||||
urlLabel.innerText = 'URL Starts With';
|
||||
urlLabelHolder.appendChild(urlLabel);
|
||||
editorHolder.appendChild(urlLabelHolder);
|
||||
|
||||
var urlInputHolder = document.createElement('div');
|
||||
var urlInput = document.createElement('input');
|
||||
urlInput.id = 'cssEditor-matchUrl';
|
||||
urlInput.type = 'text';
|
||||
urlInputHolder.appendChild(urlInput);
|
||||
editorHolder.appendChild(urlInputHolder);
|
||||
|
||||
var contentLabelHolder = document.createElement('div');
|
||||
var contentLabel = document.createElement('label');
|
||||
contentLabel.innerText = 'CSS';
|
||||
contentLabelHolder.appendChild(contentLabel);
|
||||
editorHolder.appendChild(contentLabelHolder);
|
||||
|
||||
var contentInputHolder = document.createElement('div');
|
||||
var contentInput = document.createElement('textarea');
|
||||
contentInput.id = 'cssEditor-styleContent';
|
||||
contentInputHolder.appendChild(contentInput);
|
||||
editorHolder.appendChild(contentInputHolder);
|
||||
|
||||
modalList.appendChild(editorHolder);
|
||||
|
||||
|
||||
|
||||
var saveButtonsHolder = document.createElement('div');
|
||||
|
||||
var removeCssButton = document.createElement('button');
|
||||
removeCssButton.id = 'cssEditor-removeStyle';
|
||||
removeCssButton.innerText = 'Remove Style';
|
||||
removeCssButton.className = 'cssEditor-footer-button cssEditor-float-left cssEditor-cancel-button';
|
||||
removeCssButton.onclick = removeStyle;
|
||||
saveButtonsHolder.appendChild(removeCssButton);
|
||||
|
||||
var saveCssButton = document.createElement('button');
|
||||
saveCssButton.innerText = 'Save Style';
|
||||
saveCssButton.className = 'cssEditor-footer-button cssEditor-float-right cssEditor-save-button';
|
||||
saveCssButton.onclick = saveStyle;
|
||||
saveButtonsHolder.appendChild(saveCssButton);
|
||||
|
||||
modalFooter.appendChild(saveButtonsHolder);
|
||||
|
||||
//////////////////////////
|
||||
|
||||
function createStyleList(allStylesTmp) {
|
||||
if (allStylesTmp && allStylesTmp.length > 0) {
|
||||
allStyles = allStyles.slice(0, 0).concat(allStylesTmp);
|
||||
}
|
||||
|
||||
while (existingStyles.hasChildNodes() && existingStyles.childElementCount > 1) {
|
||||
existingStyles.removeChild(existingStyles.lastChild);
|
||||
}
|
||||
|
||||
for (var i = 0; i < allStyles.length; i++) {
|
||||
var listItem = document.createElement('option');
|
||||
listItem.id = 'option_' + i;
|
||||
listItem.className = 'cssEditor-chapter-item';
|
||||
listItem.value = 'option_' + i;
|
||||
listItem.innerText = allStyles[i].title;
|
||||
if (currentStyle && (allStyles[i].title === currentStyle.title)) {
|
||||
listItem.selected = 'selected';
|
||||
}
|
||||
existingStyles.appendChild(listItem);
|
||||
}
|
||||
}
|
||||
|
||||
function editCurrentStyle() {
|
||||
if (!currentStyle) {
|
||||
return;
|
||||
}
|
||||
|
||||
showStyleEditor();
|
||||
showRemoveStyle();
|
||||
|
||||
document.getElementById('cssEditor-styleName').value = currentStyle.title;
|
||||
document.getElementById('cssEditor-matchUrl').value = currentStyle.url;
|
||||
document.getElementById('cssEditor-styleContent').value = currentStyle.style;
|
||||
|
||||
}
|
||||
|
||||
function resetFields() {
|
||||
document.getElementById('cssEditor-styleName').value = '';
|
||||
document.getElementById('cssEditor-matchUrl').value = '';
|
||||
document.getElementById('cssEditor-styleContent').value = '';
|
||||
}
|
||||
|
||||
function hideStyleEditor() {
|
||||
document.getElementById('cssEditor-styleEditor').style.display = 'none';
|
||||
}
|
||||
|
||||
function showStyleEditor() {
|
||||
document.getElementById('cssEditor-styleEditor').style.display = 'block';
|
||||
}
|
||||
|
||||
function showRemoveStyle() {
|
||||
document.getElementById('cssEditor-removeStyle').style.display = 'inline-block';
|
||||
}
|
||||
|
||||
function hideRemoveStyle() {
|
||||
document.getElementById('cssEditor-removeStyle').style.display = 'none';
|
||||
}
|
||||
|
||||
function saveStyle() {
|
||||
var tmpValue = {
|
||||
title: document.getElementById('cssEditor-styleName').value,
|
||||
url: document.getElementById('cssEditor-matchUrl').value,
|
||||
style: document.getElementById('cssEditor-styleContent').value
|
||||
}
|
||||
if (currentStyle === null) {
|
||||
allStyles.push(tmpValue);
|
||||
currentStyle = tmpValue;
|
||||
currentStyleIndex = allStyles.length - 1;
|
||||
} else {
|
||||
currentStyle = tmpValue;
|
||||
allStyles[currentStyleIndex] = currentStyle;
|
||||
}
|
||||
setStyles(allStyles);
|
||||
createStyleList();
|
||||
}
|
||||
|
||||
function removeStyle() {
|
||||
allStyles.splice(currentStyleIndex, 1);
|
||||
setStyles(allStyles);
|
||||
hideStyleEditor();
|
||||
createStyleList();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////
|
||||
|
||||
var modal = document.createElement('div');
|
||||
modal.id = 'cssEditor-Modal';
|
||||
|
||||
modalContent.appendChild(modalHeader);
|
||||
modalContent.appendChild(modalList);
|
||||
modalContent.appendChild(modalFooter);
|
||||
modal.appendChild(modalContent);
|
||||
|
||||
body.appendChild(modal);
|
||||
|
||||
modal.style.display = "none";
|
||||
modal.style.position = 'fixed';
|
||||
modal.style.zIndex = '1';
|
||||
modal.style.left = '0';
|
||||
modal.style.top = '0';
|
||||
modal.style.width = '100%';
|
||||
modal.style.height = '100%';
|
||||
modal.style.overflow = 'auto';
|
||||
modal.style.backgroundColor = 'rgba(210, 210, 210, 1)';
|
||||
|
||||
modalContent.style.zIndex = '2';
|
||||
modalContent.style.backgroundColor = '#fff';
|
||||
modalContent.style.margin = '5% auto';
|
||||
modalContent.style.padding = '0';
|
||||
modalContent.style.width = '70%';
|
||||
|
||||
window.onclick = function(event) {
|
||||
if (event.target == modal) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
modal.style.display = "block";
|
||||
|
||||
document.onkeydown = function(evt) {
|
||||
evt = evt || window.event;
|
||||
if (evt.keyCode == 27) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
function closeModal() {
|
||||
for (var i=0; i<document.styleSheets.length; i++) {
|
||||
document.styleSheets.item(i).disabled = false;
|
||||
}
|
||||
modal.style.display = "none";
|
||||
modalContent.parentNode.removeChild(modalContent);
|
||||
modal.parentNode.removeChild(modal);
|
||||
}
|
||||
|
||||
function removeListItem(atIndex) {
|
||||
return function() {
|
||||
allPagesRef[atIndex].removed = true;
|
||||
var tmpListElem = document.getElementById('li' + atIndex);
|
||||
tmpListElem.style.display = 'none';
|
||||
};
|
||||
}
|
||||
|
||||
function previewListItem(atIndex) {
|
||||
return function() {
|
||||
alert(allPagesRef[atIndex].content.trim().replace(/<[^>]+>/gi, '').replace(/\s+/g, ' ').substring(0, 1000) + ' ...');
|
||||
};
|
||||
}
|
||||
|
||||
function prepareEbook(newChapters) {
|
||||
try {
|
||||
if (newChapters.length === 0) {
|
||||
alert('Can\'t generate an empty eBook!');
|
||||
return;
|
||||
}
|
||||
buildEbookFromChapters();
|
||||
} catch (e) {
|
||||
console.log('Error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function saveChanges() {
|
||||
var newChapters = [];
|
||||
var newEbookTitle = ebookTilte.value;
|
||||
if (newEbookTitle.trim() === '') {
|
||||
newEbookTitle = 'eBook';
|
||||
}
|
||||
|
||||
try {
|
||||
var tmpChaptersList = document.getElementsByClassName('cssEditor-chapter-item');
|
||||
if (!tmpChaptersList || !allPagesRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < tmpChaptersList.length; i++) {
|
||||
var listIndex = Number(tmpChaptersList[i].id.replace('li', ''));
|
||||
if (allPagesRef[listIndex].removed === false) {
|
||||
newChapters.push(allPagesRef[listIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
saveEbookTitle(newEbookTitle);
|
||||
saveEbookPages(newChapters);
|
||||
return newChapters;
|
||||
} catch (e) {
|
||||
console.log('Error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
|
||||
getStyles(createStyleList);
|
||||
}
|
297
web-extension/cssjson.js
Normal file
297
web-extension/cssjson.js
Normal file
|
@ -0,0 +1,297 @@
|
|||
/**
|
||||
* CSS-JSON Converter for JavaScript
|
||||
* Converts CSS to JSON and back.
|
||||
* Version 2.1
|
||||
*
|
||||
* Released under the MIT license.
|
||||
*
|
||||
* Copyright (c) 2013 Aram Kocharyan, http://aramk.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
||||
to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
var CSSJSON = new function () {
|
||||
|
||||
var base = this;
|
||||
|
||||
base.init = function () {
|
||||
// String functions
|
||||
String.prototype.trim = function () {
|
||||
return this.replace(/^\s+|\s+$/g, '');
|
||||
};
|
||||
|
||||
String.prototype.repeat = function (n) {
|
||||
return new Array(1 + n).join(this);
|
||||
};
|
||||
};
|
||||
base.init();
|
||||
|
||||
var selX = /([^\s\;\{\}][^\;\{\}]*)\{/g;
|
||||
var endX = /\}/g;
|
||||
var lineX = /([^\;\{\}]*)\;/g;
|
||||
var commentX = /\/\*[\s\S]*?\*\//g;
|
||||
var lineAttrX = /([^\:]+):([^\;]*);/;
|
||||
|
||||
// This is used, a concatenation of all above. We use alternation to
|
||||
// capture.
|
||||
var altX = /(\/\*[\s\S]*?\*\/)|([^\s\;\{\}][^\;\{\}]*(?=\{))|(\})|([^\;\{\}]+\;(?!\s*\*\/))/gmi;
|
||||
|
||||
// Capture groups
|
||||
var capComment = 1;
|
||||
var capSelector = 2;
|
||||
var capEnd = 3;
|
||||
var capAttr = 4;
|
||||
|
||||
var isEmpty = function (x) {
|
||||
return typeof x == 'undefined' || x.length == 0 || x == null;
|
||||
};
|
||||
|
||||
var isCssJson = function (node) {
|
||||
return !isEmpty(node) ? (node.attributes && node.children) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input is css string and current pos, returns JSON object
|
||||
*
|
||||
* @param cssString
|
||||
* The CSS string.
|
||||
* @param args
|
||||
* An optional argument object. ordered: Whether order of
|
||||
* comments and other nodes should be kept in the output. This
|
||||
* will return an object where all the keys are numbers and the
|
||||
* values are objects containing "name" and "value" keys for each
|
||||
* node. comments: Whether to capture comments. split: Whether to
|
||||
* split each comma separated list of selectors.
|
||||
*/
|
||||
base.toJSON = function (cssString, args) {
|
||||
var node = {
|
||||
children: {},
|
||||
attributes: {}
|
||||
};
|
||||
var match = null;
|
||||
var count = 0;
|
||||
|
||||
if (typeof args == 'undefined') {
|
||||
var args = {
|
||||
ordered: false,
|
||||
comments: false,
|
||||
stripComments: false,
|
||||
split: false
|
||||
};
|
||||
}
|
||||
if (args.stripComments) {
|
||||
args.comments = false;
|
||||
cssString = cssString.replace(commentX, '');
|
||||
}
|
||||
|
||||
while ((match = altX.exec(cssString)) != null) {
|
||||
if (!isEmpty(match[capComment]) && args.comments) {
|
||||
// Comment
|
||||
var add = match[capComment].trim();
|
||||
node[count++] = add;
|
||||
} else if (!isEmpty(match[capSelector])) {
|
||||
// New node, we recurse
|
||||
var name = match[capSelector].trim();
|
||||
// This will return when we encounter a closing brace
|
||||
var newNode = base.toJSON(cssString, args);
|
||||
if (args.ordered) {
|
||||
var obj = {};
|
||||
obj['name'] = name;
|
||||
obj['value'] = newNode;
|
||||
// Since we must use key as index to keep order and not
|
||||
// name, this will differentiate between a Rule Node and an
|
||||
// Attribute, since both contain a name and value pair.
|
||||
obj['type'] = 'rule';
|
||||
node[count++] = obj;
|
||||
} else {
|
||||
if (args.split) {
|
||||
var bits = name.split(',');
|
||||
} else {
|
||||
var bits = [name];
|
||||
}
|
||||
for (i in bits) {
|
||||
var sel = bits[i].trim();
|
||||
if (sel in node.children) {
|
||||
for (var att in newNode.attributes) {
|
||||
node.children[sel].attributes[att] = newNode.attributes[att];
|
||||
}
|
||||
} else {
|
||||
node.children[sel] = newNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!isEmpty(match[capEnd])) {
|
||||
// Node has finished
|
||||
return node;
|
||||
} else if (!isEmpty(match[capAttr])) {
|
||||
var line = match[capAttr].trim();
|
||||
var attr = lineAttrX.exec(line);
|
||||
if (attr) {
|
||||
// Attribute
|
||||
var name = attr[1].trim();
|
||||
var value = attr[2].trim();
|
||||
if (args.ordered) {
|
||||
var obj = {};
|
||||
obj['name'] = name;
|
||||
obj['value'] = value;
|
||||
obj['type'] = 'attr';
|
||||
node[count++] = obj;
|
||||
} else {
|
||||
if (name in node.attributes) {
|
||||
var currVal = node.attributes[name];
|
||||
if (!(currVal instanceof Array)) {
|
||||
node.attributes[name] = [currVal];
|
||||
}
|
||||
node.attributes[name].push(value);
|
||||
} else {
|
||||
node.attributes[name] = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Semicolon terminated line
|
||||
node[count++] = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param node
|
||||
* A JSON node.
|
||||
* @param depth
|
||||
* The depth of the current node; used for indentation and
|
||||
* optional.
|
||||
* @param breaks
|
||||
* Whether to add line breaks in the output.
|
||||
*/
|
||||
base.toCSS = function (node, depth, breaks) {
|
||||
var cssString = '';
|
||||
if (typeof depth == 'undefined') {
|
||||
depth = 0;
|
||||
}
|
||||
if (typeof breaks == 'undefined') {
|
||||
breaks = false;
|
||||
}
|
||||
if (node.attributes) {
|
||||
for (i in node.attributes) {
|
||||
var att = node.attributes[i];
|
||||
if (att instanceof Array) {
|
||||
for (var j = 0; j < att.length; j++) {
|
||||
cssString += strAttr(i, att[j], depth);
|
||||
}
|
||||
} else {
|
||||
cssString += strAttr(i, att, depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.children) {
|
||||
var first = true;
|
||||
for (i in node.children) {
|
||||
if (breaks && !first) {
|
||||
cssString += '\n';
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
cssString += strNode(i, node.children[i], depth);
|
||||
}
|
||||
}
|
||||
return cssString;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param data
|
||||
* You can pass css string or the CSSJS.toJSON return value.
|
||||
* @param id (Optional)
|
||||
* To identify and easy removable of the style element
|
||||
* @param replace (Optional. defaults to TRUE)
|
||||
* Whether to remove or simply do nothing
|
||||
* @return HTMLLinkElement
|
||||
*/
|
||||
base.toHEAD = function (data, id, replace) {
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
var xnode = document.getElementById(id);
|
||||
var _xnodeTest = (xnode !== null && xnode instanceof HTMLStyleElement);
|
||||
|
||||
if (isEmpty(data) || !(head instanceof HTMLHeadElement)) return;
|
||||
if (_xnodeTest) {
|
||||
if (replace === true || isEmpty(replace)) {
|
||||
xnode.removeAttribute('id');
|
||||
} else return;
|
||||
}
|
||||
if (isCssJson(data)) {
|
||||
data = base.toCSS(data);
|
||||
}
|
||||
|
||||
var node = document.createElement('style');
|
||||
node.type = 'text/css';
|
||||
|
||||
if (!isEmpty(id)) {
|
||||
node.id = id;
|
||||
} else {
|
||||
node.id = 'cssjson_' + timestamp();
|
||||
}
|
||||
if (node.styleSheet) {
|
||||
node.styleSheet.cssText = data;
|
||||
} else {
|
||||
node.appendChild(document.createTextNode(data));
|
||||
}
|
||||
|
||||
head.appendChild(node);
|
||||
|
||||
if (isValidStyleNode(node)) {
|
||||
if (_xnodeTest) {
|
||||
xnode.parentNode.removeChild(xnode);
|
||||
}
|
||||
} else {
|
||||
node.parentNode.removeChild(node);
|
||||
if (_xnodeTest) {
|
||||
xnode.setAttribute('id', id);
|
||||
node = xnode;
|
||||
} else return;
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
// Alias
|
||||
|
||||
if (typeof window != 'undefined') {
|
||||
window.createCSS = base.toHEAD;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
var isValidStyleNode = function (node) {
|
||||
return (node instanceof HTMLStyleElement) && node.sheet.cssRules.length > 0;
|
||||
}
|
||||
|
||||
var timestamp = function () {
|
||||
return Date.now() || +new Date();
|
||||
};
|
||||
|
||||
var strAttr = function (name, value, depth) {
|
||||
return '\t'.repeat(depth) + name + ': ' + value + ';\n';
|
||||
};
|
||||
|
||||
var strNode = function (name, value, depth) {
|
||||
var cssString = '\t'.repeat(depth) + name + ' {\n';
|
||||
cssString += base.toCSS(value, depth + 1);
|
||||
cssString += '\t'.repeat(depth) + '}\n';
|
||||
return cssString;
|
||||
};
|
||||
|
||||
};
|
|
@ -12,6 +12,8 @@ var allowedTags = [
|
|||
'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'msgroup', 'mlongdiv', 'mscarries',
|
||||
'mscarry', 'mstack'
|
||||
];
|
||||
var cssClassesToTmpIds = {};
|
||||
var tmpIdsToNewCss = {};
|
||||
|
||||
//////
|
||||
|
||||
|
@ -188,9 +190,16 @@ function sanitize(rawContentString) {
|
|||
});
|
||||
lastFragment = tattrs.length === 0 ? '<a>' : '<a href="' + tattrs[0] + '">';
|
||||
} else {
|
||||
lastFragment = '<' + tag + '>';
|
||||
}
|
||||
|
||||
// TODO ???
|
||||
tattrs = attrs.filter(function(attr) {
|
||||
return attr.name === 'data-class';
|
||||
}).map(function(attr) {
|
||||
return attr.value;
|
||||
});
|
||||
lastFragment = '<' + tag + ' class="' + tattrs[0] + '"' + '>';
|
||||
|
||||
results += lastFragment;
|
||||
lastFragment = '';
|
||||
},
|
||||
|
@ -263,6 +272,74 @@ function getSelectedNodes() {
|
|||
|
||||
/////
|
||||
|
||||
function jsonToCss(jsonObj) {
|
||||
var keys = Object.keys(jsonObj);
|
||||
var result = '';
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var tmpJsonObj = jsonObj[keys[i]];
|
||||
var tmpKeys = Object.keys(tmpJsonObj);
|
||||
result += '.' + keys[i] + ' {';
|
||||
for (var j = 0; j < tmpKeys.length; j++) {
|
||||
result += tmpKeys[j] + ':' + tmpJsonObj[tmpKeys[j]] + ';';
|
||||
}
|
||||
result += '} ';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function extractCss(callback) {
|
||||
$('body').find('*').each(function (i, pre) {
|
||||
if (!$(pre).is(':visible')) {
|
||||
$(pre).replaceWith('');
|
||||
} else {
|
||||
var classNames = pre.getAttribute('class');
|
||||
var tmpName = cssClassesToTmpIds[classNames];
|
||||
var tmpNewCss = tmpIdsToNewCss[tmpName];
|
||||
if (!tmpName) {
|
||||
tmpName = 'class-' + Math.floor(Math.random()*100000);
|
||||
cssClassesToTmpIds[classNames] = tmpName;
|
||||
tmpIdsToNewCss[tmpName] = {};
|
||||
}
|
||||
if (!tmpNewCss) {
|
||||
var style = window.getComputedStyle(pre);
|
||||
tmpNewCss = {};
|
||||
tmpNewCss['font-size'] = style['font-size'];
|
||||
tmpNewCss['font-weight'] = style['font-weight'];
|
||||
tmpNewCss['color'] = style['color'];
|
||||
tmpNewCss['background-color'] = style['background-color'];
|
||||
tmpIdsToNewCss[tmpName] = tmpNewCss;
|
||||
}
|
||||
pre.setAttribute('data-class', tmpName);
|
||||
}
|
||||
});
|
||||
getCurrentStyle(function (currentStyle) {
|
||||
var styleText = currentStyle.style;
|
||||
var json = CSSJSON.toJSON(styleText);
|
||||
var keys = Object.keys(json.children);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (json.children[keys[i]].children['display'] && json.children[keys[i]].children['display'] === 'none') {
|
||||
continue;
|
||||
}
|
||||
var cEls = document.querySelectorAll(keys[i]);
|
||||
for (var j = 0; j < cEls.length; j++) {
|
||||
var style = window.getComputedStyle(cEls[j]);
|
||||
tmpNewCss = {};
|
||||
tmpNewCss['font-size'] = style['font-size'];
|
||||
tmpNewCss['font-weight'] = style['font-weight'];
|
||||
tmpNewCss['color'] = style['color'];
|
||||
tmpNewCss['background-color'] = style['background-color'];
|
||||
tmpName = 'class-' + Math.floor(Math.random()*100000);
|
||||
tmpIdsToNewCss[tmpName] = tmpNewCss;
|
||||
var oldClass = cEls[j].getAttribute('data-class');
|
||||
cEls[j].setAttribute('data-class', oldClass + ' ' + tmpName);
|
||||
}
|
||||
}
|
||||
callback(jsonToCss(tmpIdsToNewCss));
|
||||
});
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
function deferredAddZip(url, filename) {
|
||||
var deferred = $.Deferred();
|
||||
JSZipUtils.getBinaryContent(url, function(err, data) {
|
||||
|
@ -286,7 +363,8 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
var result = {};
|
||||
var pageSrc = '';
|
||||
var tmpContent = '';
|
||||
|
||||
// var styleFile =
|
||||
extractCss(function (styleFile) {
|
||||
if (request.type === 'extract-page') {
|
||||
pageSrc = document.getElementsByTagName('body')[0];
|
||||
tmpContent = getContent(pageSrc);
|
||||
|
@ -317,6 +395,8 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
url: getPageUrl(tmpTitle),
|
||||
title: tmpTitle,
|
||||
baseUrl: getCurrentUrl(),
|
||||
styleFileContent: styleFile,
|
||||
styleFileName: 'style'+Math.floor(Math.random()*100000)+'.css',
|
||||
images: extractedImages,
|
||||
content: tmpContent
|
||||
};
|
||||
|
@ -324,6 +404,7 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
}).fail(function(e) {
|
||||
console.log('Error:', e);
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"content_scripts": [{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["jquery.js", "jszip.js", "jszip-utils.js", "utils.js", "pure-parser.js", "extractHtml.js"]
|
||||
"js": ["jquery.js", "jszip.js", "jszip-utils.js", "utils.js", "pure-parser.js", "cssjson.js", "extractHtml.js"]
|
||||
}],
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
|
|
|
@ -74,6 +74,17 @@
|
|||
<body>
|
||||
<div class="menu-holder">
|
||||
<div id="menuTitle">Save as eBook:</div>
|
||||
<hr/>
|
||||
<div><label for="">Custom Styles:</label></div>
|
||||
<div>
|
||||
<select id="allStylesList" class="" name="" style="display:inline-block;width:100%;">
|
||||
</select>
|
||||
</div>
|
||||
<div class="">
|
||||
<button id="applyStyle" type="button" name="button" style="display:inline-block;width:47%;border-right:solid 1px black;">Apply</button>
|
||||
<button id="editStyles" type="button" name="button" style="display:inline-block;width:47%;">Edit Styles ...</button>
|
||||
</div>
|
||||
<hr/>
|
||||
<button id="savePage" type="button" name="button">Save Page</button>
|
||||
<button id="saveSelection" type="button" name="button">Save Selection</button>
|
||||
<hr/>
|
||||
|
|
|
@ -1,3 +1,93 @@
|
|||
var allStyles = [];
|
||||
var currentStyle = null;
|
||||
|
||||
getStyles(createStyleList);
|
||||
|
||||
function createStyleList(styles) {
|
||||
allStyles = styles;
|
||||
chrome.tabs.query({'active': true}, function (tabs) {
|
||||
var currentUrl = tabs[0].url;
|
||||
|
||||
if (!styles || styles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var existingStyles = document.getElementById('allStylesList');
|
||||
var foundMatchingUrl = false;
|
||||
|
||||
while (existingStyles.hasChildNodes() && existingStyles.childElementCount > 1) {
|
||||
existingStyles.removeChild(existingStyles.lastChild);
|
||||
}
|
||||
|
||||
for (var i = 0; i < styles.length; i++) {
|
||||
var listItem = document.createElement('option');
|
||||
listItem.id = 'option_' + i;
|
||||
listItem.className = 'cssEditor-chapter-item';
|
||||
listItem.value = 'option_' + i;
|
||||
listItem.innerText = styles[i].title;
|
||||
|
||||
if (!foundMatchingUrl) {
|
||||
currentUrl = currentUrl.replace(/(http[s]?:\/\/|www\.)/gi, '').toLowerCase();
|
||||
var styleUrl = styles[i].url.replace(/(http[s]?:\/\/|www\.)/gi, '').toLowerCase();
|
||||
|
||||
if (currentUrl.startsWith(styleUrl)) {
|
||||
listItem.selected = 'selected';
|
||||
foundMatchingUrl = true;
|
||||
currentStyle = styles[i];
|
||||
setCurrentStyle(currentStyle);
|
||||
}
|
||||
}
|
||||
|
||||
existingStyles.appendChild(listItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('allStylesList').onchange = function () {
|
||||
var newValue = this.value;
|
||||
newValue = newValue.replace('option_', '');
|
||||
newValue = parseInt(newValue);
|
||||
currentStyle = allStyles[newValue];
|
||||
setCurrentStyle(currentStyle);
|
||||
}
|
||||
|
||||
document.getElementById("applyStyle").onclick = function() {
|
||||
chrome.tabs.query({
|
||||
currentWindow: true,
|
||||
active: true
|
||||
}, function(tab) {
|
||||
chrome.tabs.insertCSS(tab[0].id, {code: currentStyle.style});
|
||||
// window.close(); TODO ?
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("editStyles").onclick = function() {
|
||||
|
||||
if (document.getElementById('cssEditor-Modal')) {
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.tabs.query({
|
||||
currentWindow: true,
|
||||
active: true
|
||||
}, function(tab) {
|
||||
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/jquery.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/utils.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/filesaver.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/saveEbook.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/jquery-sortable.js'});
|
||||
chrome.tabs.insertCSS(tab[0].id, {file: '/cssEditor.css'});
|
||||
|
||||
chrome.tabs.executeScript(tab[0].id, {
|
||||
file: '/cssEditor.js'
|
||||
});
|
||||
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById("editChapters").onclick = function() {
|
||||
|
||||
|
@ -46,6 +136,7 @@ function dispatch(action, justAddToBuffer) {
|
|||
chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/pure-parser.js'});
|
||||
chrome.tabs.executeScript(tab[0].id, {file: '/cssjson.js'});
|
||||
|
||||
chrome.tabs.executeScript(tab[0].id, {
|
||||
file: 'extractHtml.js'
|
||||
|
|
|
@ -98,7 +98,11 @@ function _buildEbook(allPages) {
|
|||
'</ncx>'
|
||||
);
|
||||
|
||||
oebps.file(cssFileName, '');
|
||||
oebps.file(cssFileName, ''); //TODO
|
||||
var styleFolder = oebps.folder('style');
|
||||
allPages.forEach(function(page) {
|
||||
styleFolder.file(page.styleFileName, page.styleFileContent);
|
||||
});
|
||||
|
||||
var pagesFolder = oebps.folder('pages');
|
||||
allPages.forEach(function(page) {
|
||||
|
@ -107,7 +111,7 @@ function _buildEbook(allPages) {
|
|||
'<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" />' +
|
||||
'<link href="../style/' + page.styleFileName + '" rel="stylesheet" type="text/css" />' +
|
||||
'</head><body>' +
|
||||
page.content +
|
||||
'</body></html>'
|
||||
|
|
|
@ -1,3 +1,36 @@
|
|||
|
||||
function setCurrentStyle(currentStyle) {
|
||||
chrome.runtime.sendMessage({
|
||||
type: "set current style",
|
||||
currentStyle: currentStyle
|
||||
}, function(response) {
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentStyle(callback) {
|
||||
chrome.runtime.sendMessage({
|
||||
type: "get current style"
|
||||
}, function(response) {
|
||||
callback(response.currentStyle);
|
||||
});
|
||||
}
|
||||
|
||||
function getStyles(callback) {
|
||||
chrome.runtime.sendMessage({
|
||||
type: "get styles"
|
||||
}, function(response) {
|
||||
callback(response.styles);
|
||||
});
|
||||
}
|
||||
|
||||
function setStyles(styles) {
|
||||
chrome.runtime.sendMessage({
|
||||
type: "set styles",
|
||||
styles: styles
|
||||
}, function(response) {
|
||||
});
|
||||
}
|
||||
|
||||
function getEbookTitle(callback) {
|
||||
chrome.runtime.sendMessage({
|
||||
type: "get title"
|
||||
|
@ -119,6 +152,7 @@ function getAbsoluteUrl(urlStr) {
|
|||
} else if (urlStr.indexOf('http') !== 0) {
|
||||
absoluteUrl = currentUrl + '/' + urlStr;
|
||||
}
|
||||
absoluteUrl = absoluteUrl.replace(/&/ig, '&'); //TODO ?
|
||||
return absoluteUrl;
|
||||
} catch (e) {
|
||||
console.log('Error:', e);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue