// ==UserScript==
// @name overtooning
// @namespace http://www.bumblebits.net
// @author doonge@oddsquad.org
// @version 1.1.7
// @description Load overlay from scanlation teams while browsing original webtoons.
// @match *://comic.naver.com/*
// @match *://m.comic.naver.com/*
// @match *://webtoon.daum.net/*
// @match *://cartoon.media.daum.net/*
// @match *://comico.toast.com/*
// @match *://www.comico.jp/*
// @match *://www.foxtoon.com/*
// @match *://page.kakao.com/*
// @match *://comics.nate.com/*
// @match *://webtoon.olleh.com/*
// @match *://ttale.com/*
// @match *://www.lezhin.com/*
// @match https://ex-ac.lezhin.com/*
// @match *://manhua.weibo.com/*
// @grant none
// ==/UserScript==
var OTOON_VERSION = '1.1.7';
var OTOON_MESSAGE = 'Mixed content fix (set to https).';
// -- MUTATION OBSERVER rough fallback for older browsers.
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
if(!MutationObserver) {
MutationObserver = function(callback) {
this.callback = callback;
var interval, textContent, node;
};
MutationObserver.prototype = {
observe: function (node, options) {
this.node = node;
this.textContent = node.textContent;
this.interval = window.setInterval(function() {
if(arguments[0].textContent != arguments[0].node.textContent) {
arguments[0].callback([{target: arguments[0].node}]);
}
},
1000, this);
},
takeRecords: function () { //Not used.
return true;
},
disconnect: function () {
window.clearInterval(this.interval);
}
};
}
// -- Banzai.
var overtooning = {
//lang: http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry ?
storage: { //default values (overwritten by localStorage if exists)
feed: [{url: 'https://otoon-api.bumblebits.net', name: 'Default feed', lang: ['en', 'es'], lastUpdate: 0}],
webtoon: [],
config: {
debug: 3, //verbosity of logs.
active: true, //stops the execution of this script early, if false (only display menu).
lang: [], //bypass the browser's user agent language, if needed.
memoryLimit: 0, //limit for the low memory devices (in megapixels).
lazyLoading: true, //images are to be loaded in order.
},
text: {
_SETTING: 'Setting',
_SETTING_LAZYLOAD: 'Ordered loading',
_SETTING_LAZYLOAD_I:'Force the original images to be loaded one by one (automatically set to yes if you set a memory limit below).',
_SETTING_MEMORY: 'Memory Limit (MPixel)',
_SETTING_MEMORY_I: 'Limit the maximum memory available for image processing (low power devices). In megapixels (width/1024 x height/1024).',
_SETTING_LANG: 'Force language preferences',
_SETTING_LANG_I: 'Only use if the autodetect language feature is not enough. Currently available values are: ',
_SETTING_DISABLE: 'Disable Overtooning on this website',
_SETTING_RESET: 'Reset Overtooning data',
_SETTING_RESET_I: 'If nothing works, maybe this will help.',
_FEED: 'Feed',
_FEED_TEMPLATE: 'Set this feed as layout translator.',
_FEED_REFRESH: 'Refresh the data served by this feed.',
_WEBTOON: 'Webtoon',
_WEBTOON_FEED: 'Set this feed as primary choice for this webtoon.',
_LOG: 'Log',
_TEMPLATE: 'Layout',
_TEMPLATE_I: 'Edit the layout (advanced users).',
_ROUGH: 'Rough',
_ROUGH_I: 'Placeholder for the Rough Translation functionality (not available yet).',
_YES: 'Yes',
_NO: 'No',
_APPLY: 'Apply'
},
template: [],
exec: function(func, property) {
if(property) {
if(!property.join) {
property = [property];
}
for(var i = property.length -1; i > -1; i--) {
this[func](property[i]);
}
} else {
this.exec(func, ['feed', 'webtoon', 'config', 'text', 'template']);
}
},
load: function(property) {
var data = localStorage.getItem('overtooning.' + property);
if(data && (data = JSON.parse(data))) {
this[property] = data;
}
},
save: function(property) {
if(this[property]) {
localStorage.setItem('overtooning.' + property, JSON.stringify(this[property]));
}
},
erase: function(property) {
localStorage.removeItem('overtooning.' + property);
}
},
jar: { //holds temporary shared vars for internal functions
node: { //store properties like webtoonId, webtoonTitle etc...
nodeList: {},
valueList: {},
routineList: {},
pathList: {},
addNode: function(property, node, path) {
if(!this.nodeList[property]) {
this.nodeList[property] = [];
}
if(!this.pathList[property]) {
this.pathList[property] = [];
}
var index = this.nodeList[property].indexOf(node);
if(index == -1) { // no duplicate node
var pathIndex = this.pathList[property].indexOf(path);
if(pathIndex == -1) {
this.nodeList[property].push(node);
return this.nodeList[property].length-1;
}
this.nodeList[property][pathIndex] = node;
return pathIndex;
}
return index;
},
value: function(property, value) {
if(typeof value == 'string') {
this.valueList[property] = value;
var nodeList = [];
for(var i = this.nodeList[property] ? this.nodeList[property].length -1 : -1; i > -1; i--) {
if(document.contains(this.nodeList[property][i]) || this.nodeList[property][i] instanceof Attr) { //what to do with Attr?
nodeList.push(this.nodeList[property][i]);
overtooning.value(this.nodeList[property][i], value);
}
}
this.nodeList[property] = nodeList;
}
if(typeof this.valueList[property] !== 'undefined') {
return this.valueList[property];
}
return false;
},
routine: function(property, func, args) {
if(typeof func == 'function') {
this.routineList[property] = {func: func, args: args};
var nodeList = [];
for(var i = this.nodeList[property] ? this.nodeList[property].length -1 : -1; i > -1; i--) { //empty orphans.
if(document.contains(this.nodeList[property][i])) {
nodeList.push(this.nodeList[property][i]);
}
}
this.nodeList[property] = nodeList;
}
for(var i = this.nodeList[property] ? this.nodeList[property].length -1 : -1; i > -1; i--) {
var node = this.nodeList[property][i],
unactiveNode = [],
order = (this.routineList[property].args && this.routineList[property].args.order),
active = false,
item = 0;
while(node) {
item++;
this.routineList[property].args.item = item;
active = this.routineList[property].func(node, this.routineList[property].args);
if(order) { //order subroutine
if(!active) {
unactiveNode.push(node);
} else {
if(unactiveNode.length) {
var saveNodePlace = node.nextSibling; //we should move them all
var switchNode = unactiveNode.shift(); // but will mess with the order because we do not care about non translated stuff
node.parentNode.replaceChild(node, switchNode);
node.parentNode.insertBefore(switchNode, saveNodePlace);
node = switchNode;
unactiveNode.push(node);
}
}
}
if(this.routineList[property].args && this.routineList[property].args.next) {
node = overtooning.next(node, this.routineList[property].args.next);
if(order && node.newList && this.routineList[property].args.multiple) {
unactiveNode = [];
}
if(node && node.node) {
node = node.node;
} else {
node = false;
}
} else {
node = false;
}
}
}
},
refresh: function(property) {
if(typeof property !== 'undefined') {
if(typeof this.valueList[property] == 'string') {
this.value(property, this.value(property));
} else if(typeof this.routineList[property].func == 'function') {
this.routine(property);
}
} else {
for(property in this.valueList) {
this.refresh(property);
}
}
}
},
log: [],
fetch: {node: [], name: []},
run: {},
menu: {}, //holds the menu node container.
query: [],
rawImage: new Image(),
overlay: new Image(),
splitOverlay: 0, //whether or not overlays are split (gives split height)
canvas: [],
pixel: 'data:image/svg+xml,',
pixelRatio: 0,
currentImage: 0,
busy: false,
timer: false,
interval: false,
maxDimension: 1024 * 1024, //set default expected image's size to 1 Megapixel
bufferSize: 0,
internalWebtoonId: -1,
observer: [],
forceRaw: false //Cheese-like compromize
},
menu: {
close: function() {
if(overtooning.jar.menu.container) {
overtooning.jar.menu.container.parentNode.removeChild(overtooning.jar.menu.container);
}
},
console: function(element, category) {
if(!overtooning.jar.menu.container) {
overtooning.jar.menu.container = overtooning.create('div', {id: 'otoon-console'},
overtooning.create('div', {className: 'otoon-row otoon-header', style: 'font-weight: bold; border-bottom: 1px solid black;'},
overtooning.create('a', {className: 'otoon-col otoon-button otoon-logo', textContent: 'Overtooning', href: 'http://overtooning.bumblebits.net', target: '_blank', style: 'text-decoration: none !important;'}, overtooning.create('small', {textContent: ' ' + OTOON_VERSION})),
overtooning.create('span', {className: 'otoon-col otoon-button otoon-close', textContent: 'X', onclick: overtooning.menu.close})
)
);
}
while(overtooning.jar.menu.container.firstChild.nextSibling) {
overtooning.jar.menu.container.removeChild(overtooning.jar.menu.container.firstChild.nextSibling);
}
overtooning.jar.menu.container.appendChild(overtooning.create('div', {className: 'otoon-row otoon-menu', style: 'position: relative; font-weight: bold; border-bottom: 1px solid black;'},
overtooning.create('span', {
className: 'otoon-col otoon-button otoon-log' + (category == 'log' ? ' otoon-active':''),
textContent: overtooning.storage.text._LOG, onclick: overtooning.menu.log}),
overtooning.create('span', {
className: 'otoon-col otoon-button otoon-general' + (category == 'general' ? ' otoon-active':''),
textContent: overtooning.storage.text._SETTING, onclick: overtooning.menu.setting}),
overtooning.create('span', {
className: 'otoon-col otoon-button otoon-feed' + (category == 'feed' ? ' otoon-active':''),
textContent: overtooning.storage.text._FEED, onclick: overtooning.menu.feed}),
overtooning.create('span', {
className: 'otoon-col otoon-button otoon-webtoon' + (category == 'webtoon' ? ' otoon-active':''),
textContent: overtooning.storage.text._WEBTOON, onclick: overtooning.menu.webtoon}),
overtooning.create('span', {
className: 'otoon-col otoon-button otoon-template' + (category == 'template' ? ' otoon-active':''),
textContent: overtooning.storage.text._TEMPLATE, onclick: overtooning.menu.template}),
overtooning.create('span', {
className: 'otoon-col otoon-button otoon-rough' + (category == 'rough' ? ' otoon-active':''),
textContent: overtooning.storage.text._ROUGH, onclick: overtooning.menu.rough})
));
if(element) {
element.className = 'otoon-col';
overtooning.jar.menu.container.appendChild(overtooning.create('div', {className: 'otoon-row otoon-body'}, element));
}
if(!overtooning.jar.menu.container.parentNode) {
document.body.appendChild(overtooning.jar.menu.container);
}
},
log: function() {
var logs = overtooning.create('div', {});
if(!overtooning.jar.log.length) {
logs.appendChild(overtooning.create('Empty Log'))
}
for(var i = 0; i < overtooning.jar.log.length; i++) {
logs.appendChild(overtooning.create('p', {textContent: overtooning.jar.log[i], style: 'padding: 0 1em;'}));
}
overtooning.menu.console(logs, 'log');
},
setting: function() {
var settings = overtooning.create('div', {});
settings.appendChild(overtooning.create('div', {className: 'otoon-row'},
overtooning.create('span', {className: 'otoon-col otoon-option-name', textContent: overtooning.storage.text._SETTING_LAZYLOAD}),
overtooning.create('span', {className: 'otoon-col otoon-option-value'},
overtooning.create('input', {className: 'otoon-toggler otoon-hidden', id: 'otoon-lazyload-y', name: 'lazyload', type: 'radio', value: '1',
onclick: function() {overtooning.storage.config.lazyLoading = true; overtooning.storage.save('config');}}),
overtooning.create('label', {className: 'otoon-option otoon-yes', for: 'otoon-lazyload-y', textContent: overtooning.storage.text._YES}),
overtooning.create('input', {className: 'otoon-toggler otoon-hidden', id: 'otoon-lazyload-n', name: 'lazyload', type: 'radio', value: '0',
onclick: function() {overtooning.storage.config.lazyLoading = false; overtooning.storage.save('config');}}),
overtooning.create('label', {className: 'otoon-option otoon-no', for: 'otoon-lazyload-n', textContent: overtooning.storage.text._NO})
),
overtooning.create('span', {className: 'otoon-col otoon-info', textContent: overtooning.storage.text._SETTING_LAZYLOAD_I})
));
settings.appendChild(overtooning.create('div', {className: 'otoon-row'},
overtooning.create('span', {className: 'otoon-col otoon-option-name', textContent: overtooning.storage.text._SETTING_MEMORY}),
overtooning.create('span', {className: 'otoon-col otoon-option-value'},
overtooning.create('input', {type: 'text', id: 'otoon-memorylimit', style: 'width: 40%;', value: Math.round(overtooning.storage.config.memoryLimit / 1024 / 1024)}),
overtooning.create('span', {className: 'otoon-option otoon-yes', textContent: overtooning.storage.text._APPLY,
onclick: function() {
var memoryLimit = Math.min(Math.max(document.getElementById('otoon-memorylimit').value, 0), 100);
overtooning.storage.config.memoryLimit = memoryLimit * 1024 * 1024;
overtooning.storage.save('config');
}})
),
overtooning.create('span', {className: 'otoon-col otoon-info', textContent: overtooning.storage.text._SETTING_MEMORY_I})
));
var lang = overtooning.lang();
settings.appendChild(overtooning.create('div', {className: 'otoon-row'},
overtooning.create('span', {className: 'otoon-col otoon-option-name', textContent: overtooning.storage.text._SETTING_LANG}),
overtooning.create('span', {className: 'otoon-col otoon-option-value'},
overtooning.create('input', {type: 'text', id: 'otoon-lang', style: 'width: 40%;', value: overtooning.storage.config.lang ? overtooning.storage.config.lang.join(', ') : ''}),
overtooning.create('span', {className: 'otoon-option otoon-yes', textContent: overtooning.storage.text._APPLY,
onclick: function() {
var lang = overtooning.lang(),
result = [],
input = document.getElementById('otoon-lang');
var inputValue = input.value.split(',');
for(var i = inputValue.length-1; i > -1; i--){
if(lang.indexOf(inputValue[i].trim()) != -1) {
result.unshift(inputValue[i].trim());
}
}
if(overtooning.storage.config.lang != result) {
overtooning.storage.config.lang = result;
overtooning.storage.save('config');
//ping new template?
overtooning.cors(
overtooning.storage.feed[0].url + '/' + window.location.hostname + '/0/0.json?t=0&lang='+result.join(','), 'GET', '', '',
function(data) {overtooning.jar.query.unshift({feedId: 0, ping: true}); overtooning.query(data); overtooning.menu.setting();},
function(error) {overtooning.addLog('[overtooning.run] No template can be loaded'); overtooning.menu.log();}
);
}
input.value = result.join(', ');
}})
),
overtooning.create('span', {className: 'otoon-col otoon-info', textContent: overtooning.storage.text._SETTING_LANG_I + lang.join(', ')})
));
settings.appendChild(overtooning.create('div', {className: 'otoon-row'},
overtooning.create('span', {className: 'otoon-col otoon-option-name', textContent: overtooning.storage.text._SETTING_DISABLE}),
overtooning.create('span', {className: 'otoon-col otoon-option-value'},
overtooning.create('input', {className: 'otoon-toggler otoon-hidden', id: 'otoon-disable-y', name: 'disable', type: 'radio', value: '1',
onclick: function() {overtooning.storage.config.disable = true; overtooning.storage.save('config');}}),
overtooning.create('label', {className: 'otoon-option otoon-no', for: 'otoon-disable-y', textContent: overtooning.storage.text._YES}),
overtooning.create('input', {className: 'otoon-toggler otoon-hidden', id: 'otoon-disable-n', name: 'disable', type: 'radio', value: '0',
onclick: function() {overtooning.storage.config.disable = false; overtooning.storage.save('config');}}),
overtooning.create('label', {className: 'otoon-option otoon-yes', for: 'otoon-disable-n', textContent: overtooning.storage.text._NO})
)
));
settings.appendChild(overtooning.create('div', {className: 'otoon-row'},
overtooning.create('span', {className: 'otoon-col otoon-option-name', textContent: overtooning.storage.text._SETTING_RESET}),
overtooning.create('span', {className: 'otoon-col otoon-option-value'},
overtooning.create('span', {className: 'otoon-option otoon-no', textContent: overtooning.storage.text._APPLY,
onclick: function() {
overtooning.storage.exec('erase');
window.setTimeout(function() {document.location.reload()}, 3000);
}})
),
overtooning.create('span', {className: 'otoon-col otoon-info', textContent: overtooning.storage.text._SETTING_RESET_I})
));
overtooning.menu.console(settings, 'general');
document.getElementById(overtooning.storage.config.lazyLoading ? 'otoon-lazyload-y' : 'otoon-lazyload-n').checked = true;
document.getElementById(overtooning.storage.config.disable ? 'otoon-disable-y' : 'otoon-disable-n').checked = true;
},
feed: function() {
var feeds = overtooning.create('div', {});
if(!overtooning.storage.feed.length) {
feeds.appendChild(overtooning.create('no feed'))
}
for(var i = 0; i < overtooning.storage.feed.length; i++) {
feeds.appendChild(overtooning.create('p', {textContent: overtooning.storage.feed[i].url}));
}
overtooning.menu.console(feeds, 'feed');
},
webtoon: function() {
var webtoons = overtooning.create('div', {});
if(!overtooning.storage.webtoon.length) {
webtoons.appendChild(overtooning.create('no webtoon'))
}
for(var i = 0; i < overtooning.storage.webtoon.length; i++) {
webtoons.appendChild(overtooning.create('p', {textContent: overtooning.storage.webtoon[i].wT}));
}
overtooning.menu.console(webtoons, 'webtoon');
},
template: function() {
var template = overtooning.create('div', {});
overtooning.storage.load('template');
template.appendChild(overtooning.create('textarea', {style: 'width: 100%; height: 50em;', textContent: JSON.stringify(overtooning.storage.template, null, ' ')}));
overtooning.storage.template = null;
overtooning.menu.console(template, 'template');
},
rough: function() {
var rough = overtooning.create('div', {});
rough.appendChild(overtooning.create('p', {textContent: 'not yet'}));
overtooning.menu.console(rough, 'rough');
}
},
lang: function() {
var lang = [];
for(var i = overtooning.storage.feed.length -1; i > -1; i--) {
if(overtooning.storage.feed[i].lang && overtooning.storage.feed[i].lang.length) {
for(var j = overtooning.storage.feed[i].lang.length -1; j > -1; j--) {
if(lang.indexOf(overtooning.storage.feed[i].lang[j]) == -1) {
lang.unshift(overtooning.storage.feed[i].lang[j]);
}
}
}
}
return lang;
},
mutation: function(MutationRecord) {
var observerId = -1, targetElement = MutationRecord[0].target;
for(var i = overtooning.jar.observer.length -1; i > -1; i--) {
if(targetElement == overtooning.jar.observer[i].target) {
observerId = i;
break;
}
}
if(observerId != -1) {
overtooning.jar.observer[observerId].observer.disconnect();
overtooning.addLog('[overtooning.observe] Mutation observed ' + overtooning.jar.observer[observerId].path);
overtooning.jar.fetch = {node: [], name: []}; //desactivate save node function (maybe remove it ?).
overtooning.runTemplate(overtooning.jar.observer[observerId].template);
overtooning.jar.observer[observerId].observer.observe(overtooning.jar.observer[observerId].target, overtooning.jar.observer[observerId].options);
}
},
run: function() {
if (window.top !== window.self || window.frameElement) {
return;
}
overtooning.storage.exec('load');
if(!overtooning.jar.run.stylesheet) {
overtooning.jar.run.stylesheet = overtooning.create('style', {});
overtooning.jar.run.stylesheet.textContent = overtooning.getBaseCss();
delete overtooning.getBaseCss;
}
if(!overtooning.jar.run.stylesheet.parentNode) {
document.body.appendChild(overtooning.jar.run.stylesheet);
}
if(!overtooning.storage.template || !overtooning.storage.template.length) {
//legacy localStorage variables
localStorage.removeItem('doongeFeeds');
localStorage.removeItem('overloaderData');
// end of legacy
if(!overtooning.storage.feed || !overtooning.storage.feed.length) {
overtooning.showFeed();
} else {
overtooning.jar.query.push({feedId: 0, ping: true});
overtooning.cors(
overtooning.storage.feed[0].url + '/' + window.location.hostname + '/0/0.json?t=0'+
(overtooning.storage.config.lang ? overtooning.storage.config.lang.join(',') : ''), 'GET', '', '',
function(data) {overtooning.query(data); if(overtooning.storage.template.length) overtooning.run();},
function(error) {overtooning.addLog('[overtooning.run] No template can be loaded'); overtooning.menu.log();}
);
}
return false;
}
if(!overtooning.jar.run.template) {
overtooning.jar.run.template = [];
var priorityTemplate = [];
//select proper routes and populate main queue (overtooning.jar.run.template)
for(var index = overtooning.storage.template.length -1; index > -1; index--) {
if( new RegExp('^' + overtooning.storage.template[index].route.replace(/\\/g, '').replace(/\./g, '\\.').replace(/\//g, '\/')).test(window.location.pathname/* + window.location.hash*/) ) {
overtooning.addLog('[overtooning.run] pathname: ' + overtooning.storage.template[index].route);
if(overtooning.storage.template[index].html) {
for(var path = overtooning.storage.template[index].html.length -1; path > -1; path--) {
if(overtooning.storage.template[index].html[path].assign && /^overtooning|webtoonId|chapterId$/.test(overtooning.storage.template[index].html[path].assign)) {
priorityTemplate.push(overtooning.storage.template[index].html[path]);
} else {
overtooning.jar.run.template.push(overtooning.storage.template[index].html[path]);
}
}
}
if(overtooning.storage.template[index].css && !overtooning.storage.config.disable) {
overtooning.jar.run.stylesheet.textContent += overtooning.storage.template[index].css;
}
}
}
overtooning.storage.template = [];
if(priorityTemplate.length) {
overtooning.runTemplate(priorityTemplate);
priorityTemplate = [];
}
if(!overtooning.jar.node.value('overtooning')) {
overtooning.addLog('[overtooning.run] Could not bootstrap - correct template?');
overtooning.menu.log();
}
if(overtooning.storage.config.disable) {
overtooning.addLog('[overtooning.run] User has disabled Overtooning on this website.');
return false;
}
var webtoonId = false;
if(webtoonId = overtooning.jar.node.value('webtoonId')) {
for(var i = overtooning.storage.webtoon.length -1; i > -1; i--) {
if(overtooning.storage.webtoon[i].wI == webtoonId) {
overtooning.jar.internalWebtoonId = i;
overtooning.jar.node.value('webtoonTitle', overtooning.storage.webtoon[i].wT);
overtooning.jar.node.value('webtoonAuthor', overtooning.storage.webtoon[i].wA);
overtooning.jar.node.value('webtoonBlurb', overtooning.storage.webtoon[i].wB);
}
}
}
//*// Cheese in the Trap exception
var forceWebtoonId = window.location.hash.match(new RegExp('^#otoon=([0-9a-zA-Z_-]+)$'));
if(forceWebtoonId) {
overtooning.jar.node.value('chapterId', forceWebtoonId[1]);
overtooning.jar.forceRaw = true;
overtooning.jar.saveAssignImageList = overtooning.assign.imageList;
overtooning.assign.imageList = function(data) { //do nothing
overtooning.jar.node.routineList.imageList = {args: data};
};
}
//*/
overtooning.jar.run.template.sort(function(a, b) {return ( ( a.path == b.path ) ? 0 : ( ( a.path > b.path ) ? 1 : -1 ) );});
//populate wait and observers queues (overtooning.jar.run.wait and overtooning.jar.run.observe).
overtooning.jar.run.wait = [];
overtooning.jar.run.observe = [];
for(var index = 0; index < overtooning.jar.run.template.length; index++) { //wait and observers setup
if(overtooning.jar.run.template[index].wait) {
overtooning.jar.run.wait.push(overtooning.jar.run.template[index]);
}
if(overtooning.jar.run.template[index].observe) {
var targetElement = overtooning.fetch(overtooning.jar.run.template[index].observe);
if(targetElement) {
var observerId = -1;
for(var i = overtooning.jar.observer.length -1; i > -1; i--) {
if(targetElement == overtooning.jar.observer[i].target) {
observerId = i;
break;
}
}
if(observerId == -1) {
overtooning.addLog('[overtooning.run.observe] Attaching observer to ' + overtooning.jar.run.template[index].observe);
overtooning.jar.observer.push({
target: targetElement,
path: overtooning.jar.run.template[index].observe,
options: overtooning.jar.run.template[index].options || {childList: true},
template: []
});
observerId = overtooning.jar.observer.length -1;
overtooning.jar.observer[observerId].observer = new MutationObserver(overtooning.mutation);
} else {
overtooning.jar.observer[observerId].observer.disconnect();
overtooning.addLog('[overtooning.run.observe] Overloading ' + overtooning.jar.run.template[index].observe);
for(var property in overtooning.jar.run.template[index].options) {
if (overtooning.jar.run.template[index].options.hasOwnProperty(property) && !overtooning.jar.observer[observerId].options[property]) {
overtooning.jar.observer[observerId].options[property] = true;
}
}
}
delete overtooning.jar.run.template[index].observe;
delete overtooning.jar.run.template[index].options;
overtooning.jar.observer[observerId].template.push(overtooning.jar.run.template[index]);
overtooning.jar.observer[observerId].observer.observe(overtooning.jar.observer[observerId].target, overtooning.jar.observer[observerId].options);
}
}
}
//if overtooning.jar.run.wait isn't empty, plan it.
if(overtooning.jar.run.wait.length) {
overtooning.jar.run.timer = window.setInterval(function() {
if(document.readyState == "complete") {
window.clearInterval(overtooning.jar.run.timer);
overtooning.runTemplate(overtooning.jar.run.wait);
delete overtooning.jar.run.wait;
}
}, 1000);
}
}
//once every image has been described
if(!overtooning.jar.pixelRatio) {
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
overtooning.jar.pixelRatio = dpr / bsr;
if(overtooning.jar.pixelRatio != 1) {
overtooning.addLog('[overtooning.run] Pixel Ratio: ' + overtooning.jar.pixelRatio);
}
}
overtooning.jar.rawImage.onload = overtooning.rawImageOnLoad;
overtooning.jar.rawImage.onerror = overtooning.rawImageOnError;
overtooning.jar.overlay.onload = overtooning.overlayOnLoad;
overtooning.jar.overlay.onerror = overtooning.overlayOnError;
overtooning.runTemplate(overtooning.jar.run.template);
overtooning.addLog('[overtooning.template] End.');
var MTime = overtooning.MTime(),
priorityFeed = [];
if(overtooning.jar.internalWebtoonId != -1) {
priorityFeed = overtooning.storage.webtoon[overtooning.jar.internalWebtoonId].fL;
for(var j = 0; j < priorityFeed.length; j++) {
overtooning.jar.query.push({feedId: priorityFeed[j]});
}
}
for(var j = 0; j < overtooning.storage.feed.length; j++) {
if(priorityFeed.indexOf(j) == -1 && overtooning.storage.feed[j].lastUpdate + 60 * 24 * 7 < MTime) {
overtooning.addLog('[overtooning.run] Ping feed: ' + overtooning.storage.feed[j].url + ' (' + j + ')');
overtooning.jar.query.push({feedId: j, ping: true});
}
}
overtooning.addLog('[overtooning.run] Queries to be made: ' + overtooning.jar.query.length);
if(overtooning.jar.query) {
overtooning.query();
}
overtooning.addLog('[overtooning.run] End.');
//overtooning.menu.console();
},
assign: {
generic: function(data) {
overtooning.jar.node.addNode(data.assign, data.node, data.path);
if(overtooning.jar.node.value(data.assign)) {
overtooning.jar.node.value(data.assign, overtooning.jar.node.value(data.assign), data.assign == 'webtoonBlurb' ? true : false);
}
},
webtoonId: function(data) { // no need to add node.
if(!overtooning.jar.node.value('webtoonId')) {
overtooning.jar.node.value('webtoonId', overtooning.value(data.node));
}
},
chapterId: function(data) { // no need to add node.
if(!overtooning.jar.node.value('chapterId')) {
overtooning.jar.node.value('chapterId', overtooning.value(data.node));
}
},
overtooning: function(data) { // simple setup
overtooning.jar.node.value('overtooning', '1');
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute('viewBox', '0 0 64 64');
svg.setAttribute('style', 'width: 100%; height: 100%; overflow: visible;');
svg.setAttribute('title', 'overTooning');
svg.onclick = function() {
//allow select and copy/paste (disallowed from naver).
document.oncontextmenu = null;
document.onselectstart = null;
//end;
overtooning.menu.log();
};
var path = document.createElementNS(svg.namespaceURI, 'path');
path.setAttribute('d', 'M32 4c18 0 32 12 32 26s-14 26-32 26c-2 0-3 0-5 0-7 7-15 8-23 8v-2c4-2 8-6 8-10 0-1 0-1 0-2-7-5-12-12-12-20 0-14 14-26 32-26zM48 25l-11-2-5-10-5 10-11 2 8 8-2 11 10-5 10 5-2-11 8-8z');
svg.appendChild(path);
data.node.appendChild(svg);
},
webtoonList: function(data) {
overtooning.jar.node.addNode(data.assign, data.node, data.path);
delete data.node;
data.order = true;
overtooning.jar.node.routine(data.assign, overtooning.webtoonList, data);
},
chapterList: function(data) {
overtooning.jar.node.addNode(data.assign, data.node, data.path);
delete data.node;
overtooning.jar.node.routine(data.assign, overtooning.chapterList, data);
},
imageList: function(data) {
//overtooning.addLog('[overtooning.assign] Call to imageList');
if(overtooning.jar.busy) { //nope, but prioritize this function over loadImage() timer.
if(overtooning.jar.timer) {
window.clearTimeout(overtooning.jar.timer);
overtooning.jar.timer = false;
}
if(!overtooning.jar.interval) {
overtooning.jar.interval = window.setInterval(overtooning.assign.imageList, 1000);
}
overtooning.addLog('[overtooning.canvas] Cancelled: already busy loading an image');
return false;
}
if(!overtooning.storage.config.memoryLimit) { //no need to keep interval if no memory limit.
window.clearInterval(overtooning.jar.interval);
overtooning.jar.interval = false;
} else if(!overtooning.jar.interval){
overtooning.jar.interval = window.setInterval(overtooning.assign.imageList, 1000);
}
//console.log('[imageList] running');
//clean canvas list.
var replaceCanvas = [];
for(var i = overtooning.jar.canvas.length -1; i > -1; i--) {
if(document.contains(overtooning.jar.canvas[i].ref)) {
replaceCanvas.push(overtooning.jar.canvas[i]);
} else {
overtooning.jar.canvas[i].ref.src = overtooning.jar.pixel;
overtooning.jar.canvas[i].ref = null;
if(overtooning.jar.canvas[i].node) {
overtooning.jar.canvas[i].node.textContent = '';
overtooning.jar.canvas[i].node = null;
}
overtooning.jar.canvas[i].activeNode = null;
}
}
overtooning.jar.canvas = replaceCanvas;
replaceCanvas = [];
//launch analysis routine which populates overtooning.jar.canvas.
if(data) {
overtooning.jar.node.addNode(data.assign, data.node, data.path);
overtooning.jar.node.routine(data.assign, overtooning.imageList, data);
overtooning.jar.node.nodeList.imageList = []; //ugly but we don't need it.
//data.node = first node, lastNode = last node. (we have "next").
overtooning.jar.node.routineList.imageList.args.lastNode = overtooning.jar.canvas[overtooning.jar.canvas.length-1].ref;
} else {
var test = overtooning.fetch(overtooning.jar.node.routineList.imageList.args.path);
if(test != overtooning.jar.node.routineList.imageList.args.node) { //different first or last node!
overtooning.jar.node.routineList.imageList.args.item = 1;
overtooning.jar.node.routine('imageList', overtooning.imageList, overtooning.jar.node.routineList.imageList.args);
overtooning.addLog('[overtooning.canvas] Image list updated (new starting node).');
} else if(test = overtooning.next(overtooning.jar.node.routineList.imageList.args.lastNode, overtooning.jar.node.routineList.imageList.args.next)) {
while(test) {
test = test.node;
overtooning.jar.node.routineList.imageList.args.item++;
overtooning.jar.node.routineList.imageList.args.lastNode = test;
overtooning.imageList(test, overtooning.jar.node.routineList.imageList.args);
test = overtooning.next(test, overtooning.jar.node.routineList.imageList.args.next);
}
overtooning.addLog('[overtooning.canvas] Image list updated (new ending node).');
}
}
var position = {top: 0, left: 0, width: 0, height: 0},
comparer = function(a, b) {
if(a.distance == -1) return 1;
if(b.distance == -1) return -1;
if(a.distance < b.distance) return -1;
if(a.distance > b.distance) return 1;
return 0;
};
//Orders images in current viewing order, relative to top-left scroll value.
for(var i = overtooning.jar.canvas.length-1; i > -1; i--) {
if(overtooning.jar.canvas[i].activeNode.style.display == 'none') {
overtooning.jar.canvas[i].distance = -1;
} else {
position = overtooning.jar.canvas[i].activeNode.getBoundingClientRect();
overtooning.jar.canvas[i].distance = Math.pow(position.top, 2) + Math.pow(position.left, 2);
}
overtooning.jar.canvas[i].top = position.top;
overtooning.jar.canvas[i].left = position.left;
replaceCanvas.splice(overtooning.pivot(overtooning.jar.canvas[i], replaceCanvas, comparer) +1, 0, overtooning.jar.canvas[i]);
}
overtooning.jar.canvas = replaceCanvas;
replaceCanvas = [];
//overtooning.addLog('yeah '+ JSON.stringify(overtooning.jar.canvas));//do something.
/*//var string = '';
for(var i= 0; i < 6; i++) {
string += ' ' + overtooning.jar.canvas[i].item + '('+overtooning.jar.canvas[i].loaded+')';
}
//console.log('[imageList] order ' + string);
//*/
overtooning.loadImage();
}
},
webtoonList: function(node, args) {
if(args && args.innerPath && args.innerPath.webtoonId) {
var webtoonId = overtooning.fetch(args.innerPath.webtoonId, node, true),
webtoonInfo = false;
webtoonId = webtoonId ? overtooning.value(webtoonId) : false;
for(var i = overtooning.storage.webtoon.length -1; i > -1; i--) {
if(overtooning.storage.webtoon[i].wI == webtoonId) {
webtoonInfo = overtooning.storage.webtoon[i];
break;
}
}
if(webtoonInfo) {
shortProperty = {webtoonTitle: 'wT', webtoonAuthor: 'wA', webtoonBlurb: 'wB'};
for(property in shortProperty) {
if(args.innerPath[property]) {
var subNode = overtooning.fetch(args.innerPath[property], node, true);
if(subNode) {
overtooning.value(subNode, webtoonInfo[shortProperty[property]]);
}
}
}
return true;
}
}
return false;
},
chapterList: function(node, args) {
if(args && args.innerPath && args.innerPath.chapterId && overtooning.jar.chapterList) { // args chapterList is to be added on CORS request
var chapterId = overtooning.fetch(args.innerPath.chapterId, node, true),
chapterInfo = false;
chapterId = chapterId ? overtooning.value(chapterId) : false;
for(var i = overtooning.jar.chapterList.length -1; i > -1; i--) {
if(overtooning.jar.chapterList[i].id == chapterId) {
chapterInfo = overtooning.jar.chapterList[i];
break;
}
}
if(chapterInfo) {
var subNode = overtooning.fetch(args.innerPath.chapterTitle, node, true);
if(subNode) {
overtooning.value(subNode, chapterInfo.title);
}
return true;
}
}
return false;
},
imageList: function(node, args) {
var index = -1,
src = false;
for(var i = overtooning.jar.canvas.length -1; i > -1; i--) {
if(overtooning.jar.canvas[i].ref == node) {
index = i;
break;
}
}
if(index == -1) {
overtooning.jar.canvas.push({ref: node});
index = overtooning.jar.canvas.length -1;
}
if(!overtooning.jar.canvas[index].src) {
if(args.innerPath && args.innerPath.src && node.getAttribute(args.innerPath.src)) {
overtooning.jar.canvas[index].src = node.getAttribute(args.innerPath.src);
overtooning.jar.canvas[index].attribute = args.innerPath.src;
} else {
overtooning.jar.canvas[index].src = node.src;
}
if(overtooning.storage.config.lazyLoading && node.src == overtooning.jar.canvas[index].src) {
node.src = overtooning.jar.pixel;
}
}
if(!overtooning.jar.canvas[index].activeNode) {
overtooning.jar.canvas[index].activeNode = overtooning.jar.canvas[index].ref
}
overtooning.jar.canvas[index].style = args.innerPath && args.innerPath.style ? args.innerPath.style : '';
//more intricate way to calculate imageNumber (distord or depending on page node) here.
overtooning.jar.canvas[index].item = args.item;
},
loadImage: function() {
if(overtooning.jar.timer) {
window.clearTimeout(overtooning.jar.timer);
overtooning.jar.timer = false;
}
if(overtooning.jar.busy) {
return false;
}
var workToDo = false, fullyLoaded = true;
for(var i = 0; i < overtooning.jar.canvas.length; i++) {
if(!overtooning.jar.canvas[i].loaded) {
if( overtooning.jar.canvas[i].ref.src == overtooning.jar.pixel ||
overtooning.jar.canvas[i].ref.src == ('data:image/svg+xml,') || overtooning.jar.canvas[i].ref.src == overtooning.jar.canvas[i].src) {
overtooning.jar.currentImage = i;
workToDo = true;
break;
} else {
fullyLoaded = false;
}
}
}
if(!workToDo) {
if(!fullyLoaded) {
overtooning.jar.timer = window.setTimeout(overtooning.loadImage, 1000);
} else {
overtooning.addLog('[overtooning.canvas] All images have been loaded. No lazyloading.');
}
return false;
}
//free memory
if(overtooning.storage.config.memoryLimit) {
var megapixel = overtooning.jar.maxDimension,
cursor = overtooning.jar.canvas.length -1;
if(overtooning.jar.canvas[overtooning.jar.currentImage].width) {
megapixel = overtooning.jar.canvas[overtooning.jar.currentImage].width * overtooning.jar.canvas[overtooning.jar.currentImage].height;
}
while(cursor > overtooning.jar.currentImage && overtooning.jar.bufferSize + megapixel > overtooning.storage.config.memoryLimit) {
var rect = overtooning.jar.canvas[cursor].activeNode.getBoundingClientRect();
var isVisible = (
rect.top < (window.innerHeight || document.documentElement.clientHeight) &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
rect.bottom > 0 &&
rect.right > 0
);
if(overtooning.jar.canvas[cursor].loaded && !isVisible) {
overtooning.addLog('[overtooning.canvas.memoryLimit] Unloading image ' + overtooning.jar.canvas[cursor].item);
overtooning.jar.canvas[cursor].loaded = false;
overtooning.jar.canvas[cursor].ref.src = 'data:image/svg+xml,';
if(overtooning.jar.canvas[cursor].node) {
overtooning.jar.canvas[cursor].node.textContent = '';
}
overtooning.jar.canvas[cursor].node.className = 'otoon-overlay hidden';
overtooning.jar.bufferSize -= overtooning.jar.canvas[cursor].width * overtooning.jar.canvas[cursor].height;
}
cursor--;
}
if(overtooning.jar.bufferSize && overtooning.jar.bufferSize + megapixel > overtooning.storage.config.memoryLimit) {
return false;
}
}
overtooning.jar.busy = true;
overtooning.jar.rawImage.src = overtooning.jar.canvas[overtooning.jar.currentImage].src;
},
rawImageOnLoad: function() {
var pointer = overtooning.jar.canvas[overtooning.jar.currentImage];
if(pointer.ref.src != overtooning.jar.rawImage.src) { //our lazyloading or their
overtooning.addLog('[overtooning.canvas] Loading image ' + pointer.item);
pointer.ref.src = overtooning.jar.rawImage.src;
/*window.setTimeout(overtooning.jar.rawImage.onload, 5000);
return false;*/
}
if(document.readyState != "complete") {
overtooning.addLog('[overtooning.canvas] still loading...');
window.setTimeout(overtooning.jar.rawImage.onload, 100);
return;
}
pointer.loaded = true;
if(pointer.attribute) { // their lazyloading, let's break it.
pointer.ref.removeAttribute(pointer.attribute);
delete pointer.attribute;
}
pointer.width = overtooning.jar.rawImage.naturalWidth;
pointer.height = overtooning.jar.rawImage.naturalHeight;
pointer.ref.width = overtooning.jar.rawImage.naturalWidth;
pointer.ref.height = overtooning.jar.rawImage.naturalHeight;
var mgpxl = overtooning.jar.rawImage.naturalWidth * overtooning.jar.rawImage.naturalHeight;
if (mgpxl > overtooning.jar.maxDimension) {
overtooning.jar.maxDimension = mgpxl;
}
overtooning.jar.bufferSize += mgpxl;
//pointer.clientWidth = pointer.activeNode.clientWidth;
//pointer.clientHeight = pointer.activeNode.clientHeight;
if(overtooning.jar.generalUrl) {
window.setTimeout(function() {overtooning.jar.overlay.src = overtooning.jar.generalUrl.replace('{imgNumber}', overtooning.digit(pointer.item, 2));}, 200);
return true;
}
overtooning.jar.busy = false;
if(!overtooning.storage.config.memoryLimit || (overtooning.jar.bufferSize + overtooning.jar.maxDimension <= overtooning.storage.config.memoryLimit)) {
window.setTimeout(overtooning.assign.imageList, 200);
}
},
rawImageOnError: function() {
overtooning.addLog('[overtooning.canvas] Error while loading image ' + overtooning.jar.canvas[overtooning.jar.currentImage].item);
overtooning.jar.canvas[overtooning.jar.currentImage].ref.src = this.src;
overtooning.jar.canvas[overtooning.jar.currentImage].loaded = true;
overtooning.jar.busy = false;
if(!overtooning.storage.config.memoryLimit || (overtooning.jar.bufferSize + overtooning.jar.maxDimension <= overtooning.storage.config.memoryLimit)) {
window.setTimeout(overtooning.assign.imageList, 200);
}
},
overlayOnLoad: function() {
var pointer = overtooning.jar.canvas[overtooning.jar.currentImage];
overtooning.addLog('[overtooning.canvas] Loading overlay ' + pointer.item);
if(!pointer.node) {
pointer.node = overtooning.create('div', {
className: 'otoon-overlay'
/*,
style: 'position: relative;'+
' width: ' + pointer.activeNode.clientWidth + 'px;'+
' height: ' + pointer.activeNode.clientHeight + 'px;'+
pointer.style
*/
}
);
} else {
pointer.node.className = 'otoon-overlay';
}
var mode = pointer.height >= pointer.width ? 'height' : 'width',
start = 0,
stop = pointer[mode],
naturalCanvas,
increment = Math.floor(1024 * 1024 / (mode == 'height' ? pointer.width : pointer.height)), //max 1 megapixel per canvas.
dim = {height: pointer.height, width: pointer.width},
cursor = {height: 0, width: 0};
// possible improvement for the memory management mode:
// instead of filling to to bottom or left to right, check if the opposite is better each time
while(start != stop) {
/*if(start > 0) {
start -= Math.floor(Math.random()*10 + 5);
}*/
if(start + increment > stop) {
increment = stop - start;
}
dim[mode] = increment;
cursor[mode] = start;
naturalCanvas = overtooning.create('canvas', {
width: dim.width, height: dim.height/*,
style: 'position: absolute; top: ' + cursor.height + 'px; left: '+ cursor.width +'px;'*/
});
naturalCanvas.getContext('2d').drawImage(
overtooning.jar.rawImage,
cursor.width, cursor.height,
dim.width, dim.height, 0, 0, dim.width, dim.height
);
naturalCanvas.getContext('2d').drawImage(
overtooning.jar.overlay,
cursor.width, cursor.height,
dim.width, dim.height, 0, 0, dim.width, dim.height
);
if(/*pointer.activeNode.clientHeight != pointer.height ||*/ overtooning.jar.pixelRatio != 1) {
/*var heightMod = pointer.activeNode.clientHeight / pointer.height;
var widthMod = pointer.activeNode.clientWidth / pointer.width;*/
/*if(start == 0 && pointer.activeNode.clientHeight != pointer.height) {
overtooning.addLog('[overtooning.canvas] Adjusting size for item '+pointer.item+': Width '+ pointer.activeNode.clientWidth +' / '+ pointer.width +' - Height '+ pointer.activeNode.clientHeight +' / '+ pointer.height);
}*/
var copyCanvas = overtooning.create('canvas', {
width: Math.round(naturalCanvas.width * overtooning.jar.pixelRatio),
height: Math.round(naturalCanvas.height * overtooning.jar.pixelRatio),
/*style: 'position: absolute; top: ' + (cursor.height ? Math.round(cursor.height * heightMod) : 0) + 'px;'+
' left: ' + (cursor.width ? Math.round(cursor.width * widthMod) : 0) + 'px;'*/}
);
//copyCanvas.style.width = mode == 'height' ? pointer.activeNode.clientWidth +'px' : Math.round(increment * widthMod) +'px';
//copyCanvas.style.height = mode == 'height' ? Math.round(increment * heightMod) +'px' : pointer.activeNode.clientHeight +'px';
copyCanvas.getContext('2d').setTransform(overtooning.jar.pixelRatio, 0, 0, overtooning.jar.pixelRatio, 0, 0);
copyCanvas.getContext('2d').drawImage(naturalCanvas, 0, 0);
naturalCanvas = copyCanvas;
copyCanvas = null;
}
pointer.node.appendChild(naturalCanvas);
overtooning.addLog('[overtooning.canvas] canvas added ' + dim.width + 'x' + dim.height +' (' + overtooning.jar.pixelRatio +')');
start += increment;
}
pointer.node.appendChild(overtooning.create('div', {style: 'position: absolute; top:0; left:0; width: 100%; height: 100%;'}));
//free some memory maybe.
pointer.ref.src = 'data:image/svg+xml,';
//*//
overtooning.jar.onloadSave = overtooning.jar.rawImage.onload;
overtooning.jar.rawImage.onload = function() {overtooning.jar.rawImage.onload = overtooning.jar.onloadSave; overtooning.jar.onloadSave = null;};
overtooning.jar.rawImage.src = overtooning.jar.pixel;
//*/
if(pointer.ref.previousSibling != pointer.node) {
pointer.ref.parentNode.insertBefore(pointer.node, pointer.ref);
}
pointer.activeNode = pointer.node;
overtooning.jar.busy = false;
if(!overtooning.storage.config.memoryLimit || (overtooning.jar.bufferSize + overtooning.jar.maxDimension <= overtooning.storage.config.memoryLimit)) {
window.setTimeout(overtooning.assign.imageList, 200);
}
},
overlayOnError: function() {
overtooning.addLog('[overtooning.canvas] Error while loading overlay ' + overtooning.jar.canvas[overtooning.jar.currentImage].item);
overtooning.jar.busy = false;
if(!overtooning.storage.config.memoryLimit || (overtooning.jar.bufferSize + overtooning.jar.maxDimension <= overtooning.storage.config.memoryLimit)) {
window.setTimeout(overtooning.assign.imageList, 200);
}
},
runTemplate: function(template) {
if(!template || !template.length) {
return false;
}
for(var index = 0; index < template.length; index++) {
//console.log(template[index].path);
var node = overtooning.fetch(template[index].path);
if(node) {
node = overtooning.runCommand(node, template[index]); //not possible to call by reference node.
if(template[index].next && !template[index].assign) { // assign nexts are managed through their own way.
var item = 1;
while(next = overtooning.next(node, template[index].next)) {
template[index].item = item;
node = overtooning.runCommand(next.node, template[index]); //we don't care about newList, so we don't really care about the whole next object.
item++;
}
}
}
}
template = [];
},
next: function(node, nextArray) {
if(node && nextArray && nextArray[0]) {
var saveNode = node;
var currentNode = overtooning.fetch(nextArray[0], saveNode, true);
for(var cursor = 1; !currentNode && cursor < nextArray.length; cursor++) {
currentNode = overtooning.fetch(nextArray[cursor], saveNode, true);
}
if(currentNode) {
return {node: currentNode, newList: (cursor > 1 && cursor == nextArray.length)?true:false};
}
}
return false;
},
pivot: function(element, array, comparer, start, end) {
if(array.length === 0)
return -1;
start = start || 0;
end = end || array.length;
var pivot = (start + end) >> 1,
c = comparer(element, array[pivot]);
if(end - start <= 1) return c == -1 ? pivot -1 : pivot;
switch(c) {
case -1: return overtooning.pivot(element, array, comparer, start, pivot);
case 0: return pivot;
case 1: return overtooning.pivot(element, array, comparer, pivot, end);
}
},
runCommand: function (node, command) {
if(command.assign && command.assign == 'overtooning') {
node.parentNode.insertBefore(overtooning.create('div', {}),
node.nextSibling);
node = node.nextSibling;
} else if(command.new && command.tagName) {
node.parentNode.insertBefore(overtooning.create(command.tagName, {}),
node.nextSibling);
node = node.nextSibling;
}
if(command.tagName && node.nodeName != command.tagName) {
var copyNode = overtooning.create(command.tagName, {innerHTML: node.innerHTML ? node.innerHTML : ' '}); //insecure?
if(node.className) {
copyNode.className = node.className;
}
if(node.id) {
copyNode.id = node.id;
}
node.parentNode.insertBefore(copyNode, node);
node = node.previousSibling;
node.parentNode.removeChild(node.nextSibling);
}
if(command.className && node.className != command.className) {
if(!node.saveClass) {
node.saveClass = node.className;
}
node.className = command.className;
}
if(command.style) {
/*if(!node.saveStyle) {
node.saveStyle = node.getAttribute('style') || '';
}*/
node.setAttribute('style', (node.getAttribute('style') || '') + command.style);
}
if(command.assign) {
var property = overtooning.assign[command.assign] ? command.assign : 'generic';
overtooning.assign[property]({
node: node,
assign: command.assign,
path: command.path,
innerPath: command.innerPath ? command.innerPath : false,
next: command.next ? command.next : false,
multiple: command.multiple ? command.multiple : false,
});
} else if(command.translate) {
overtooning.translate(node, {
translate: command.translate,
into: command.into ? command.into : false,
item: command.item ? command.item : 0
});
}
return node;
},
translate: function(node, data) {
if(!node || !data.translate) {
return false;
}
if(!data.translate.join) {
data.translate = [data.translate];
}
if(data.into && !data.into.join) {
data.into = [data.into];
}
//into = check and replace all words, no into = replace into translate[item]
if(data.into) {
var nodeValue = overtooning.value(node).split("\n");
for(var i = data.translate.length -1; i > -1; i--) {
for(var j = nodeValue.length -1; j > -1; j--) {
nodeValue[j] = nodeValue[j].replace(data.translate[i], data.into[i] ? data.into[i] : data.translate[i]);
}
console.log(nodeValue);
}
console.log(nodeValue.join("\n"));
overtooning.value(node, nodeValue.join("\n"));
} else {
if(!data.item) {
data.item = 0;
}
if(data.translate[data.item]) {
overtooning.value(node, data.translate[data.item]);
}
}
},
query: function(data) {
if(data) {
var query = overtooning.jar.query.shift();
var feedId = query.feedId;
overtooning.addLog('[overtooning.cors] Analyzing response.');
try {data = JSON.parse(data);}
catch(e) {
overtooning.addLog('[overtooning.json] Parsing failed.');
data = {};
}
if(!data.overtooning) {
overtooning.addLog('[overtooning.cors] Data not intented for overtooning.');
data = {};
}
delete data.overtooning;
overtooning.storage.feed[feedId].lastUpdate = overtooning.MTime();
//fix local dictionary
if(data.text && feedId == 0) { //instead of checking against 0, add a feedId template option?
for(var key in data.text) {
if(overtooning.storage.text[key]) {
overtooning.storage.text[key] = data.text[key];
}
}
overtooning.storage.save('text');
}
if(data.template && feedId == 0) {
for(var index = 0; index < data.template.length; index++) {
if(data.template[index].html) {
for(var path = 0; path < data.template[index].html.length; path++) {
//fix simple next value
if(data.template[index].html[path].next && !data.template[index].html[path].next.join) {
data.template[index].html[path].next = [data.template[index].html[path].next];
}
//fix short hand brackets in path and innerPath.
var newPath = overtooning.openBrackets(data.template[index].html[path].path, data.template[index].html[path].next);
if(newPath.next) {
if(data.template[index].html[path].next) {
for(var i = 0; i < newPath.next.length; i++) {
data.template[index].html[path].next.push(newPath.next[i]);
}
} else {
data.template[index].html[path].next = newPath.next;
}
}
data.template[index].html[path].path = newPath.path;
if(data.template[index].html[path].innerPath) {
for(var property in data.template[index].html[path].innerPath) {
if(/^(webtoon|chapter)/.test(property)) {
newPath = overtooning.openBrackets(data.template[index].html[path].innerPath[property]);
data.template[index].html[path].innerPath[property] = newPath.path;
}
}
}
//fix dictionary data into the template
if(data.text && data.template[index].html[path].translate) {
if(data.template[index].html[path].translate.join) {
for(var cursor = data.template[index].html[path].translate.length; cursor != 0; cursor--) {
var attribute = data.template[index].html[path].translate[cursor-1];
if(data.text[attribute]) {
data.template[index].html[path].translate[cursor-1] = data.text[attribute];
}
}
} else {
var attribute = data.template[index].html[path].translate;
if(data.text[attribute]) {
data.template[index].html[path].translate = data.text[attribute];
}
}
if(data.template[index].html[path].into) {
if(data.template[index].html[path].into.join) {
for(var cursor = data.template[index].html[path].into.length; cursor != 0; cursor--) {
var attribute = data.template[index].html[path].into[cursor-1];
if(data.text[attribute]) {
data.template[index].html[path].into[cursor-1] = data.text[attribute];
}
}
} else {
var attribute = data.template[index].html[path].into;
if(data.text[attribute]) {
data.template[index].html[path].join = data.text[attribute];
}
}
}
}
}
}
if(data.template[index].css && data.template[index].css.join) {
data.template[index].css = data.template[index].css.join('');
}
}
overtooning.storage.template = data.template;
overtooning.storage.save('template');
delete data.template;
delete data.text;
}
if(data.feedName && typeof data.feedName == 'string' && data.feedName.length < 50) {
overtooning.storage.feed[feedId].name = data.feedName;
delete data.feedName;
}
if(data.feedText && typeof data.feedText == 'string' && data.feedText.length < 256) {
overtooning.storage.feed[feedId].text = data.feedText;
delete data.feedText;
}
if(data.feedLang && data.feedLang[0]) {
overtooning.storage.feed[feedId].lang = [];
for(var i =0; i < data.feedLang.length; i++) {
if(typeof data.feedLang[i] == 'string' && data.feedLang[i].length < 20) {
overtooning.storage.feed[feedId].lang.push(data.feedLang[i]);
}
}
delete data.feedLang;
}
if(data.add) {
if(!data.add[0] || !data.add[0].id) {
data.add = [data.add];
}
for(var i = data.add.length -1; i > -1; i--) {
if( data.add[i].id && typeof data.add[i].id == 'string' && /^[0-9a-zA-Z_\-]+$/.test(data.add[i].id) &&
data.add[i].title && typeof data.add[i].title == 'string' && data.add[i].title.length < 100 &&
data.add[i].author && typeof data.add[i].author == 'string' && data.add[i].author.length < 100 &&
(data.add[i].blurb ? typeof data.add[i].blurb == 'string' && data.add[i].blurb.length < 500 : true)) {
var alreadyInserted = false;
for(var j = overtooning.storage.webtoon.length -1; j > -1; j--) {
if(overtooning.storage.webtoon[j].wI == data.add[i].id) {
alreadyInserted = j;
//ask if changes are to be committed!!!!!!!!!
if(overtooning.storage.webtoon[j].fL.indexOf(feedId) == -1) {
overtooning.storage.webtoon[j].fL.push(feedId);
}
break;
}
}
if(alreadyInserted === false) {
overtooning.storage.webtoon.push({
wI: data.add[i].id,
wT: data.add[i].title,
wA: data.add[i].author,
wB: data.add[i].blurb ? data.add[i].blurb : '',
fL: [feedId]
});
}
}
}
}
if(data.remove) {
if(typeof data.remove != 'object') {
data.remove = [data.remove];
}
for(var toRemove = data.remove.length-1; toRemove > -1; toRemove--) {
for(var i = overtooning.storage.webtoon.length -1; i > -1; i--) {
if(overtooning.storage.webtoon[i].wI == data.remove[toRemove]) {
var removeFeed = overtooning.storage.webtoon[i].fL.indexOf(feedId);
if(removeFeed != -1) {
overtooning.storage.webtoon[i].fL.slice(removeFeed,1);
}
if(overtooning.storage.webtoon[i].fL.length == 0) {
if(i == overtooning.jar.internalWebtoonId) {
overtooning.jar.internalWebtoonId = -1;
}
overtooning.storage.webtoon.slice(i,1);
}
break;
}
}
}
}
if(data.add || data.remove) {
delete data.add;
delete data.remove;
overtooning.storage.save('webtoon');
overtooning.jar.node.routine('webtoonList');
}
if(!query.ping && !overtooning.jar.scanlated) { //not just a ping, or no precedence found
if(data.chapterList) {
if(data.chapterList instanceof Array) {
if(!overtooning.jar.chapterList) {
overtooning.jar.chapterList = data.chapterList;
} else { //add only non existing chapters
for(var i = data.chapterList.length -1; i > -1; i--) {
var alreadyExisting = false;
for(var j = overtooning.jar.chapterList.length -1; j > -1; j--) {
if(overtooning.jar.chapterList[j].id == data.chapterList[i].id) {
alreadyExisting = true;
break;
}
}
if(!alreadyExisting) {
overtooning.jar.chapterList.push(data.chapterList[i]);
}
}
}
overtooning.jar.node.routine('chapterList');
}
delete data.chapterList;
}
if(data.raw) {
overtooning.forceRaw((data.raw.length && data.raw.length > 1) ? data.raw : null);
} else if(overtooning.jar.forceRaw) {
overtooning.forceRaw();
}
if(overtooning.jar.internalWebtoonId != -1 && feedId == overtooning.storage.webtoon[overtooning.jar.internalWebtoonId].fL[0]) {
var change = false;
if(data.webtoonTitle) {
if(data.webtoonTitle == overtooning.jar.node.value('webtoonTitle') || typeof data.webtoonTitle != 'string' || data.webtoonTitle.length > 100) {
delete data.webtoonTitle;
} else {
overtooning.storage.webtoon[overtooning.jar.internalWebtoonId].wT = data.webtoonTitle;
change = true;
}
}
if(data.webtoonAuthor) {
if(data.webtoonAuthor == overtooning.jar.node.value('webtoonAuthor') || typeof data.webtoonAuthor != 'string' || data.webtoonAuthor.length > 100) {
delete data.webtoonAuthor;
} else {
overtooning.storage.webtoon[overtooning.jar.internalWebtoonId].wA = data.webtoonAuthor;
change = true;
}
}
if(data.webtoonBlurb) {
if(data.webtoonBlurb == overtooning.jar.node.value('webtoonBlurb') || typeof data.webtoonBlurb != 'string' || data.webtoonBlurb.length > 500) {
delete data.webtoonBlurb;
} else {
overtooning.storage.webtoon[overtooning.jar.internalWebtoonId].wB = data.webtoonBlurb;
change = true;
}
}
if(change) {
overtooning.addLog('[overtooning.query] Webtoon data changed (id: '+ overtooning.storage.webtoon[overtooning.jar.internalWebtoonId].wI +').');
overtooning.storage.save('webtoon');
}
}
if(data.generalUrl) {
overtooning.jar.scanlated = true;
overtooning.jar.generalUrl = data.generalUrl;
if(overtooning.jar.canvas) {
for(var i = overtooning.jar.canvas.length -1; i > -1; i--) {
overtooning.jar.canvas[i].loaded = false;
}
}
overtooning.assign.imageList(); //hum...
delete data.generalUrl;
}
for(property in data) {
if(typeof data[property] == 'string' && overtooning.jar.node.nodeList[property] && overtooning.jar.node.nodeList[property].length) {
overtooning.jar.node.value(property, data[property], property == 'webtoonBlurb' ? true : false);
}
}
if(overtooning.scanlated) {
while(overtooning.jar.query.length && !overtooning.jar.query[0].ping) {
overtooning.query.shift();
}
}
}
overtooning.storage.save('feed');
}
if(overtooning.jar.query.length) {
var j = overtooning.jar.query[0].feedId;
var queryUrl = overtooning.storage.feed[j].url + '/' + window.location.hostname + '/' +
(overtooning.jar.node.value('webtoonId') || '0') + '/' +
(overtooning.jar.node.value('chapterId') || '0') + '.json?' +
't=' + (overtooning.storage.feed[j].lastUpdate ? Math.abs(overtooning.MTime() - overtooning.storage.feed[j].lastUpdate+1) : 0).toString() +
(overtooning.storage.config.lang.length ? '&lang='+overtooning.storage.config.lang.join(',') : '');
overtooning.cors(
queryUrl, 'GET', '', '',
function(data) {overtooning.query(data);},
function(error) {overtooning.addLog('[overtooning.cors] '+error.message); overtooning.jar.query.shift(); overtooning.query();}
);
}
},
forceRaw: function(raw) { //To be deprecated once webtoons.com finishes Cheese in the Trap S3.
// --------------------- DESKTOP NAVER ------------------------ //
if(window.location.hostname == 'comic.naver.com') {
// --------------------- chapter list ------------------------ //
if(new RegExp('list.nhn$').test(window.location.pathname)) {
overtooning.jar.run.stylesheet.textContent += '.otoon-virtual {cursor: pointer; display: inline-block; padding: 4px 6px 4px 5px; border: 1px solid #FFF; font: 13px Verdana; color: #999;} .otoon-current, .otoon-virtual:hover {border: 1px solid #e0e0e0; color: #00c73c; cursor: pointer} .otoon-current {font-weight: bold;}';
var navNode = overtooning.fetch('#content/div.paginate'),
insertChapters = 0,
chapterPerPage = 10;
navNode.textContent = '';
for(var i = overtooning.jar.chapterList.length -1; i > -1; i--) {
if(overtooning.jar.chapterList[i].thumbnail) {
insertChapters++;
}
}
var maxPages = Math.floor(insertChapters / chapterPerPage);
for(var i = maxPages; i > -1; i--) {
navNode.appendChild(overtooning.create('span', {className: 'otoon-virtual', onclick: overtooning.naverEvent.chapterList, textContent: maxPages - i+1}));
}
navNode.firstChild.click();
// --------------------- chapter ------------------------ //
} else if(new RegExp('detail.nhn$').test(window.location.pathname)) {
if(raw) {
var refNode = overtooning.fetch(overtooning.jar.node.routineList.imageList.args.path),
imgNode = false,
parentLength = overtooning.jar.node.routineList.imageList.args.next[0].split('/').length;
refNode.src = overtooning.jar.pixel + ' ';
while(imgNode = overtooning.next(refNode, overtooning.jar.node.routineList.imageList.args.next)) {
imgNode = imgNode.node;
var upOneLevel = parentLength;
while(upOneLevel > 1) {
upOneLevel--;
imgNode = imgNode.parentNode;
}
imgNode.parentNode.removeChild(imgNode);
overtooning.jar.fetch.node = [];
}
if(parentLength > 1) {
var parentNode = imgNode;
while(parentLength > 0) {
parentNode = parentNode.parentNode;
parentLength--;
}
parentNode.parentNode.insertBefore(imgNode, parentNode);
parentNode.parentNode.removeChild(parentNode);
}
overtooning.jar.node.routineList.imageList.args.next = ['+img'];
if(raw.length > 1) {
refNode.removeAttribute('width');
refNode.removeAttribute('height');
refNode.src = raw[1];
if(raw[0] != '' && raw[0] != 'create') {
refNode.setAttribute('style', raw[0]);
}
}
for(var i = 2; i < raw.length; i++) {
refNode.parentNode.insertBefore(
overtooning.create('img', {src: raw[i]}),
refNode.nextSibling);
refNode = refNode.nextSibling;
if(raw[0] != '') {
refNode.setAttribute('style', raw[0]);
}
}
}
overtooning.assign.imageList = overtooning.jar.saveAssignImageList;
overtooning.jar.saveAssignImageList = null;
overtooning.runTemplate([overtooning.jar.node.routineList.imageList.args]);
overtooning.jar.run.stylesheet.textContent += '#comic_before, #comic_after {display: none !important;} .comic_lst .inner_lst {width: auto; overflow: hidden; white-space: nowrap;} .comic_lst .item {float: none; position: static; display: inline-block;} .comic_lst .item a[href="#"] {visibility: hidden;}';
var comic_move = document.getElementById('comic_move');
comic_move.textContent = '';
comic_move.appendChild(overtooning.create('span', {style: 'display: inline-block; width: 0; height: 40px; vertical-align: top; margin-left: -364px;'}));
for(var i = 0; i < 13; i++) {
comic_move.appendChild(
overtooning.create('div', {className: 'item'},
overtooning.create('a', {href: '#', onclick: overtooning.naverEvent.loadHashLink},
overtooning.create('span', {className: 'thmb'},
overtooning.create('img', {width: 70, height: 42, src: overtooning.jar.pixel})
),
overtooning.create('span', {className: 'subj', textContent: ' '})
)
)
);
}
var comic_before = document.getElementById('comic_before');
elClone = comic_before.cloneNode(true);
elClone.setAttribute('id', 'otoon_before');
elClone.onclick = overtooning.naverEvent.comicMoveLeft;
comic_before.parentNode.insertBefore(elClone, comic_before);
var comic_after = document.getElementById('comic_after');
elClone = comic_after.cloneNode(true);
elClone.setAttribute('id', 'otoon_after');
elClone.onclick = overtooning.naverEvent.comicMoveRight;
comic_after.parentNode.insertBefore(elClone, comic_after);
var chapterId = parseInt(overtooning.jar.node.value('chapterId'));
overtooning.naverEvent.displayComicMove(chapterId);
var index = -1;
for(var i = 0; i < overtooning.jar.chapterList.length; i++) {
if(overtooning.jar.chapterList[i].id == chapterId) {
index = i;
break;
}
}
var navURL = {
first: '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[0].id,
last: '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[overtooning.jar.chapterList.length -1].id,
previous: index > 0 ? '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[index -1].id : false,
next: index < overtooning.jar.chapterList.length -1 ? '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[index +1].id : false
};
var navBarNode = overtooning.fetch('#content/div/div.tit_area/div/div'),
remoconNode = overtooning.fetch('#comicRemocon/div.remote_cont/div'),
quickNavNode = document.getElementById('comicSequence').parentNode;
navBarNode.textContent = '';
if(remoconNode) {
remoconNode.textContent = '';
}
remoconNode.textContent = '';
quickNavNode.textContent = '';
quickNavNode.appendChild(overtooning.create('input', {id: 'comicSequence', type: 'text', style: 'width: 31px;', value: index+1, onclick: function() {this.focus(); return false;}}));
quickNavNode.appendChild(overtooning.create('span', {textContent: ' / '}));
quickNavNode.appendChild(overtooning.create('span', {className: 'total', textContent: overtooning.jar.chapterList.length}));
quickNavNode.appendChild(overtooning.create('a', {className: 'btn_move', textContent: 'go!', onclick: function() {
var goTo = parseInt(document.getElementById('comicSequence').value) -1;
if(goTo > -1 && goTo < overtooning.jar.chapterList.length) {
window.location = '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[goTo].id;
window.location.reload();
}
}}));
if(navURL.previous) {
navBarNode.appendChild(
overtooning.create('span', {className: 'pre'},
overtooning.create('a', {textContent: 'Prev', href: navURL.previous, onclick: overtooning.naverEvent.loadHashLink})
)
);
if(remoconNode) {
remoconNode.appendChild(overtooning.create('a', {className: 'btn_prev_end', textContent: 'first', href: navURL.first, onclick: overtooning.naverEvent.loadHashLink}));
remoconNode.appendChild(overtooning.create('a', {className: 'btn_prev', textContent: 'prev', href: navURL.previous, onclick: overtooning.naverEvent.loadHashLink}));
}
} else {
if(remoconNode) {
remoconNode.appendChild(overtooning.create('span', {className: 'btn_prev_end dim', textContent: 'first'}));
remoconNode.appendChild(overtooning.create('span', {className: 'btn_prev dim', textContent: 'prev'}));
}
}
if(navURL.next) {
if(navURL.previous) {
navBarNode.appendChild( overtooning.create('span', {className: 'bar', textContent: '|'}));
}
navBarNode.appendChild(
overtooning.create('span', {className: 'next'},
overtooning.create('a', {textContent: 'Next', href: navURL.next, onclick: overtooning.naverEvent.loadHashLink})
)
);
if(remoconNode) {
remoconNode.appendChild(overtooning.create('a', {className: 'btn_next', textContent: 'next', href: navURL.next, onclick: overtooning.naverEvent.loadHashLink}));
remoconNode.appendChild(overtooning.create('a', {className: 'btn_next_end', textContent: 'last', href: navURL.last, onclick: overtooning.naverEvent.loadHashLink}));
}
} else {
if(remoconNode) {
remoconNode.appendChild(overtooning.create('span', {className: 'btn_next dim', textContent: 'next'}));
remoconNode.appendChild(overtooning.create('span', {className: 'btn_next_end dim', textContent: 'last'}));
}
}
}
} else if(window.location.hostname == 'm.comic.naver.com') {
// --------------------- chapter list ------------------------ //
if(new RegExp('list.nhn$').test(window.location.pathname)) {
var navNode = overtooning.fetch('#ct/ul.toon_lst'),
linkName = window.location.pathname.replace('list.nhn', 'detail.nhn?no=1&titleId='+overtooning.jar.node.value('webtoonId')+'#otoon=');
navNode.textContent = '';
for(var i = overtooning.jar.chapterList.length -1; i > -1; i--) {
navNode.appendChild(
overtooning.create('li', {},
overtooning.create('div', {className: 'lst'},
overtooning.create('a', {href: linkName + overtooning.jar.chapterList[i].id},
overtooning.create('span', {className: 'im_br'},
overtooning.create('span', {className: 'im_inbr'},
overtooning.create('img', {width: 71, height: 42, src: overtooning.jar.chapterList[i].thumbnail})
)
),
overtooning.create('div', {className: 'toon_info'},
overtooning.create('h4', {},
overtooning.create('span', {className: 'toon_name'},
overtooning.create('strong', {},
overtooning.create('span', {textContent: overtooning.jar.chapterList[i].title})
)
)
)
)
)
)
)
);
}
// --------------------- chapter ------------------------ //
} else if(new RegExp('detail.nhn$').test(window.location.pathname)) {
if(raw) {
var refNode = overtooning.fetch(overtooning.jar.node.routineList.imageList.args.path),
imgNode = false,
parentLength = overtooning.jar.node.routineList.imageList.args.next[0].split('/').length;
refNode.src = overtooning.jar.pixel + ' ';
while(imgNode = overtooning.next(refNode, overtooning.jar.node.routineList.imageList.args.next)) {
imgNode = imgNode.node;
var upOneLevel = parentLength;
while(upOneLevel > 2) {
upOneLevel--;
imgNode = imgNode.parentNode;
}
imgNode.parentNode.removeChild(imgNode);
overtooning.jar.fetch.node = [];
}
if(parentLength > 2) {
var parentNode = refNode;
while(parentLength > 1) {
parentNode = parentNode.parentNode;
parentLength--;
}
parentNode.parentNode.insertBefore(refNode, parentNode);
parentNode.parentNode.removeChild(parentNode);
}
overtooning.jar.node.routineList.imageList.args.next = ['+img'];
if(raw.length > 2) {
refNode.removeAttribute('width');
refNode.removeAttribute('height');
refNode.setAttribute('data-original', raw[1]);
refNode.src = raw[1];
if(raw[0] != '' && raw[0] != 'create') {
refNode.setAttribute('style', raw[0]);
}
}
for(var i = 2; i < raw.length; i++) {
refNode.parentNode.insertBefore(
overtooning.create('img', {src: raw[i]}),
refNode.nextSibling);
refNode = refNode.nextSibling;
if(raw[0] != '' && raw[0] != 'create') {
refNode.setAttribute('style', raw[0]);
}
}
}
overtooning.assign.imageList = overtooning.jar.saveAssignImageList;
overtooning.jar.saveAssignImageList = null;
overtooning.runTemplate([overtooning.jar.node.routineList.imageList.args]);
var navNode = overtooning.fetch('#ct/div.end_sub_cont/div.navi_area'),
chapterId = overtooning.jar.node.value('chapterId'),
chapterIndex = -1;
navNode.textContent = '';
for(var i = 0; i < overtooning.jar.chapterList.length; i++) {
if(overtooning.jar.chapterList[i].id == chapterId) {
chapterIndex = i;
break;
}
}
var navURL = {
previous: chapterIndex > 0 ? '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[chapterIndex -1].id : false,
next: chapterIndex < overtooning.jar.chapterList.length -1 ? '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[chapterIndex +1].id : false
};
navNode.appendChild(overtooning.create('a', {
className: 'link_nav link_prev'+(navURL.previous?'':' disabled'),
href: navURL.previous,
onclick: overtooning.naverEvent.loadHashLink},
overtooning.create('span', {className: 'ico_comic'}),
overtooning.create('Previous')
));
navNode.appendChild(overtooning.create('a', {
className: 'link_nav link_list',
href: 'list.nhn?titleId=' + overtooning.jar.node.value('webtoonId')},
overtooning.create('span', {className: 'ico_comic'}),
overtooning.create('List')
));
navNode.appendChild(overtooning.create('a', {
className: 'link_nav link_next'+(navURL.next?'':' disabled'),
href: navURL.next,
onclick: overtooning.naverEvent.loadHashLink},
overtooning.create('span', {className: 'ico_comic'}),
overtooning.create('Next')
));
}
}
},
naverEvent: { //To be deprecated once webtoons.com finishes Cheese in the Trap S3.
loadHashLink: function() {
window.location = this.href;
window.location.reload();
},
chapterList: function() {
var currentNode = overtooning.fetch('#content/div.paginate/span.otoon-current');
if(currentNode) {
currentNode.className = 'otoon-virtual';
}
this.className = 'otoon-virtual otoon-current';
var modifyHTML = overtooning.fetch('#content/table/tbody');
if(modifyHTML) {
modifyHTML.innerHTML = '';
var offset = (parseInt(this.textContent) -1) * 10,
i = overtooning.jar.chapterList.length -1;
while(offset > 0) {
if(overtooning.jar.chapterList[i].thumbnail) {
offset--;
}
i--;
}
offset = 10;
var linkName = window.location.pathname.replace('list.nhn', 'detail.nhn?no=1&titleId='+overtooning.jar.node.value('webtoonId')+'#otoon=');
while(offset > 0 && i > -1) {
if(overtooning.jar.chapterList[i].thumbnail) {
offset--;
modifyHTML.appendChild(overtooning.create('tr', {},
overtooning.create('td', {}, overtooning.create('img', {src: overtooning.jar.chapterList[i].thumbnail, alt: overtooning.jar.chapterList[i].title, title: overtooning.jar.chapterList[i].title, height: 41, width: 71})),
overtooning.create('td', {className: 'title'},
overtooning.create('a', {
href: linkName + overtooning.jar.chapterList[i].id,
textContent: overtooning.jar.chapterList[i].title})),
overtooning.create('td', {}),
overtooning.create('td', {})
));
}
i--;
}
}
},
displayComicMove: function(chapterId) {
if(overtooning.jar.chapterList) {
var index = -1;
for(var i = 0; i < overtooning.jar.chapterList.length; i++) {
if(overtooning.jar.chapterList[i].id == chapterId) {
index = i;
break;
}
}
if(index != -1) {
var node = overtooning.fetch('#comic_move/div/a');
for(var j = -6; j < 7; j++) {
if(index + j > -1 && index+ j < overtooning.jar.chapterList.length) {
node.href = '?titleId=' + overtooning.jar.node.value('webtoonId') + '&no=1#otoon=' + overtooning.jar.chapterList[index+j].id;
node.className = (overtooning.jar.chapterList[index+j].id == parseInt(overtooning.jar.node.value('chapterId'))) ? 'on' : '';
node.firstChild.firstChild.src = overtooning.jar.chapterList[index+j].thumbnail;
node.lastChild.textContent = overtooning.jar.chapterList[index+j].title;
} else {
node.href = '#';
}
node = overtooning.fetch('^1/+div/a', node, true)
}
document.getElementById('otoon_before').setAttribute('style', index -3 > 0 ? 'visibility: visible;' : 'visibility: hidden;');
document.getElementById('otoon_after').setAttribute('style', index +3 >= overtooning.jar.chapterList.length ? 'visibility: hidden;' : 'visibility: visible;');
}
}
},
comicMoveLeft: function() {
if(overtooning.jar.chapterList) {
var chapterId = parseInt(document.getElementById('comic_move').childNodes[7].firstChild.href.split('=').pop());
var index = -1;
for(var i = 0; i < overtooning.jar.chapterList.length; i++) {
if(overtooning.jar.chapterList[i].id == chapterId) {
index = i;
break;
}
}
console.log(index);
if(index != -1) {
var moveLeft = index - 3 > -1 ? 3 : index;
console.log(index-moveLeft);
overtooning.naverEvent.displayComicMove(overtooning.jar.chapterList[index-moveLeft].id);
}
}
return false;
},
comicMoveRight: function() {
if(overtooning.jar.chapterList) {
var chapterId = parseInt(document.getElementById('comic_move').childNodes[7].firstChild.href.split('=').pop());
var index = -1;
for(var i = 0; i < overtooning.jar.chapterList.length; i++) {
if(overtooning.jar.chapterList[i].id == chapterId) {
index = i;
break;
}
}
console.log(index);
if(index != -1) {
var moveRight = index + 3 < overtooning.jar.chapterList.length ? 3 : overtooning.jar.chapterList.length - index;
console.log(index+moveRight);
overtooning.naverEvent.displayComicMove(overtooning.jar.chapterList[index+moveRight].id);
}
}
return false;
},
},
openBrackets: function(path, next) {
var bracket;
while((bracket = path.lastIndexOf('[')) != -1) {
var length = path.length;
if(bracket < length -1) { //superfluous, but oh well.
if(path[bracket+1] != ']') {
var repeat = 0, pad = 0;
while(bracket + 1 + pad < length && path[bracket+1+pad].match(/^[0-9]$/)) {
repeat = repeat * 10 + parseInt(path[bracket+1+pad]);
pad++;
}
var element = path.substr(0, bracket);
element = element.substr(element.lastIndexOf('/') +1).replace(/#[a-zA-Z0-9_\-]+/, '');
var step = '+';
if(element[0].match(/^[~+^-]$/)){
switch(element[0]) {
case '~':
case '-':
step = '-';
default:
element = element.substr(1);
}
}
step = '/' + step + element;
// step.repeat(repeat-1) not compatible with safari and old version of browsers.
path = path.substr(0, bracket) + Array(repeat).join(step) + path.substr(bracket+2+pad); //replace [number]
} else {
if(!next) {
next = [];
}
path = path.substr(0, bracket) + path.substr(bracket+2); //delete []
var element = path.substr(path.lastIndexOf('/', bracket-1) +1).replace(/#[a-zA-Z0-9_\-]+/, '');
var parentNumber = element.match(/\/[^+\-]/g); //MISSING: discount ^ parents.
if(!parentNumber && element[0].match(/^[~+^-]$/)) {
element = element.substr(1);
}
next.push( (parentNumber ? '^' + parentNumber.length + '/' : '') + '+' + element);
}
}
}
return {path: path, next: next};
},
getBaseCss: function() {
return '#otoon-console {'+
'position: absolute;'+
'top: 0; left: 0;'+
'padding: 2em;'+
'box-sizing: border-box;'+
'background: rgba(0, 0, 0, 0.5);'+
'width: 100%; min-height: 100%;'+
'z-index: 99999;'+
'text-align: left;'+
'}' +
'#otoon-console .otoon-row {'+
'background: white;'+
'}' +
'.otoon-col {'+
'padding: 1.5em;'+
'}'+
'.otoon-row .otoon-row {'+
'margin: 0 -1.5em;'+
'}'+
'.otoon-button, #otoon-console a:hover, .otoon-option {'+
'cursor: pointer;'+
'color: white;'+
'text-shadow: 1px 1px 1px black, 1px -1px 1px black, -1px 1px 1px black, -1px -1px 1px black;'+
'}'+
'.otoon-overlay:not(.hidden) + img, .otoon-hidden {'+
'display: none !important;'+
'}'+
'.otoon-overlay {'+
'position: relative;'+
'}'+
'.otoon-overlay canvas {'+
'max-width: 100%; max-height: 100%; display: block; margin: 0 auto;'+
'}'+
'.otoon-col.otoon-info {'+
'padding: 0 1.5em 1em 1.5em;'+
'font-style: italic;'+
'}'+
'.otoon-button:not(:hover):not(.otoon-active), .otoon-toggler:not(:checked) + .otoon-option:not(:hover):not(.otoon-active) {'+
'color: black;'+
'background: white !important;'+
'text-shadow: 0 0 0 black;'+
'}'+
'.otoon-row .otoon-close {'+
'background: #F36858;'+
'padding: 0.5em;'+
'width: 10%;'+
'text-align: right;'+
'}'+
'.otoon-row .otoon-no {'+
'background: #F36858;'+
'}'+
'.otoon-yes, .otoon-no {'+
'display: inline-block; width: 50%; height: 100%; text-align: center; line-height: 2em;'+
'}'+
'.otoon-col.otoon-option-value {'+
'padding: 1.5em 1.5em 0.5em 1.5em;'+
'}'+
'.otoon-row .otoon-general {'+
'background: #51A2E5;'+
'}'+
'.otoon-row .otoon-feed {'+
'background: #01C685;'+
'}'+
'.otoon-row .otoon-webtoon, .otoon-row .otoon-yes {'+
'background: #8DD630;'+
'}'+
'.otoon-row .otoon-log {'+
'background: #9B8AEF;'+
'}'+
'.otoon-row .otoon-logo {'+
'background: #BEBFA4;'+
'width: 90%;'+
'padding: 0.5em;'+
'}'+
'.otoon-row .otoon-rough {'+
'background: #E68323;'+
'}'+
'.otoon-row .otoon-template {'+
'background: #F4C41F;'+
'}'+
'.otoon-menu {'+
'text-align: center;'+
'}'+
'@media screen and (min-width:20em) {'+
'.otoon-menu .otoon-col {width: 50%;}'+
'.otoon-col.otoon-option-name, .otoon-col.otoon-option-value {width: 50%;}'+
'}'+
'@media screen and (min-width:30em) {'+
'.otoon-menu .otoon-col {width: 33.332%;}'+
'}'+
'@media screen and (min-width:60em) {'+
'.otoon-menu .otoon-col {width: 16.666%;}'+
'}'+
'@media only screen {'+
'.otoon-col {'+
'display: inline-block;'+
'overflow: hidden;'+
'width: 100%;'+
'box-sizing: border-box;'+
'vertical-align:top;'+
'}'+
'}';
},
addLog: function(stringLog) {
if(console) console.log(stringLog);
this.jar.log.push(stringLog);
},
value: function(element, setValue, multiline) { //revamp: treewalker for textnodes?
if(!element) {
return false;
}
if(element instanceof Array) {
for(var i = 0; i < element.length; i++) {
this.value(element[i], setValue);
}
}
if(element instanceof Attr) {
if(setValue) {
element.value = setValue;
}
return element.value;
}
if(element.node) {
element = element.node;
}
if(setValue && !element.nodeValue) {
element.textContent = ' ';
}
if(!element.nodeValue && element.firstChild) {
element = element.firstChild;
}
if(!element.nodeValue) {
return false;
}
if(setValue) {
if(multiline) {
setValue = setValue.split("\n");
element.nodeValue = setValue[0];
for(var count = setValue.length -1; count > 0; count--) {
element.parentNode.insertBefore(this.create(setValue[count]), element.nextSibling);
element.parentNode.insertBefore(this.create('br', {}), element.nextSibling);
}
} else {
element.nodeValue = setValue;
}
}
return element.nodeValue;
},
digit: function (number, width, filler) {
filler = filler || '0';
number = number + '';
return number.length >= width ? number : new Array(width - number.length + 1).join(filler) + number;
},
fetch: function (path, node, nolog) {
if(!path) {
this.addLog('[overtooning.fetch] no path');
return false;
} else if (typeof path == 'string') { //first iteration of that path.
if(!node) {
if(path.substr(0, 5) == 'head/') { //AH!
node = document.head;
path = path.substr(5);
} else {
node = document.body;
}
}
path = {tag: path.substr(Math.max(0, path.indexOf('#'))).split('/'), current: 0};
if(this.jar.fetch.node[0] && this.jar.fetch.node[0] === node) { //same source node
while(this.jar.fetch.name[path.current+1] && path.tag[path.current] && this.jar.fetch.name[path.current+1] == path.tag[path.current]) {
path.current++;
node = this.jar.fetch.node[path.current];
}
if(path.current == path.tag.length) { //same node searched in the end.
return node;
}
this.jar.fetch.node = this.jar.fetch.node.slice(0, path.current+2);
this.jar.fetch.name = this.jar.fetch.name.slice(0, path.current+2);
} else {
this.jar.fetch = {node: [node], name: ['sourceNode']};
}
}
if(path.tag[path.current] == '') { //should not be necessary anymore
return node && node.firstChild ? node.firstChild : false;
} else {
// 1 is #nodeID or tagName, 2 is .className (or attributeName=value), 3 is attribute to return
var pathData = /^((?:#|~|\+|-|\^)?(?:[a-z0-9A-Z_-]*))?((?:\.[a-zA-Z0-9 _-]+(?:=[a-zA-Z0-9 _-]+)?)*)?(@[a-zA-Z0-9_-]+(?:\?(?:=[a-zA-Z_-]+|[pn]-?[0-9]+))?)?$/.exec(path.tag[path.current]);
//console.log(pathData.toString());
if(!pathData[1] && !pathData[2]) {
this.addLog('[overtooning.fetch] Malformed request ' + path.tag[path.current]);
return false
}
if(pathData[1] && pathData[1][0] == '#') {
if(pathData[1].length < 2) {
this.addLog('[overtooning.fetch] Illegal ID # at ' + path.tag[path.current]);
return false
}
node = document.getElementById(pathData[1].substr(1));
} else {
var movingProperty = 'nextSibling', compare = [];
if(pathData[1]) {
var slash = true;
switch(pathData[1][0]) {
case '~':
movingProperty = 'previousSibling';
node = node.lastChild;
break;
case '+':
node = node.nextSibling;
break;
case '-':
movingProperty = 'previousSibling';
node = node.previousSibling;
break;
case '^':
movingProperty = 'parentNode';
node = node.parentNode;
break;
default:
node = node.firstChild;
slash = false;
}
if(slash) {
pathData[1] = pathData[1].substr(1);
}
if(pathData[1].length > 0) { //nodeName
var attribute = 'nodeName';
if(pathData[1].match(/^[0-9]+$/)) {
attribute = 'increment';
pathData[1] = parseInt(pathData[1]) -1;
} else if(pathData[1] == '_text') {
pathData[2] = null;
pathData[3] = null;
pathData[1] = '#text';
}
compare.push({attribute: attribute, value: pathData[1]});
}
} else {
node = node.firstChild;
}
if(pathData[2]) { //attributes
pathData[2] = pathData[2].substr(1).split('.');
var tmp;
for(var cursor = pathData[2].length -1; cursor > -1; cursor--) {
tmp = pathData[2][cursor].split('=');
if(tmp[1]) {
compare.push({attribute: tmp[0], value: tmp[1]});
} else {
compare.push({attribute: 'className', value: tmp[0]});
}
}
}
if(compare.length == 0) {
this.addLog('[overtooning.fetch] No qualifier at ' + path.tag[path.current]);
return false
}
var found = false;
while(node && !found) {
found = true;
for(var cursor = compare.length -1; cursor > -1; cursor --) {
switch(compare[cursor].attribute) {
case 'increment':
found = compare[cursor].value-- <= 0;
break;
case 'nodeName':
found = node.nodeName.toLowerCase() == compare[cursor].value;
break;
case 'className':
found = node.className ? this.compareClassName(compare[cursor].value, node.className) : false;
break;
default:
found = node.getAttribute(compare[cursor].attribute) == compare[cursor].value;
}
if(!found) {
node = node[movingProperty];
break;
}
}
}
}
if(!node) {
if(!nolog) {
this.addLog('[overtooning.fetch] Node not found ' + path.tag.join('/') + '(' + path.current + ') : ' + path.tag[path.current]);
}
return false
}
path.current++;
this.jar.fetch.node[path.current] = node;
this.jar.fetch.name[path.current] = path.tag[path.current-1];
if(pathData[3]) {//force return attribute
var attribute = pathData[3].substr(1).split('?');
if(node.attributes && typeof node.attributes[attribute[0]] != 'undefined' && node.attributes[attribute[0]].nodeName == attribute[0]) {
node = node.attributes[attribute[0]];
} else {
return false;
}
//node = node.getAttributeNode(attribute[0]);
if(!attribute[1]) {
return node;
}
node = node.value;
//filter function
switch(attribute[1][0]) {
case '=': //query filter
node = node.match(new RegExp(attribute[1].substr(1) + "=([^&]+)", ''));
attribute[1] = '=1';
break;
case 'p': //path filter
node = node.split('/');
break;
case 'n': //number filter
node = node.match(/-?[0-9]+/g);
break;
}
if(!node) return false;
attribute[1] = attribute[1].substr(1);
var cursor = parseInt(attribute[1], 10) % node.length;
if(cursor < 0) {
cursor += node.length;
}
return overtooning.create(node[cursor]);
}
return (path.current < path.tag.length) ? this.fetch(path, node, nolog? true : false) : node;
}
},
create: function () {
switch(arguments.length) {
case 1:
var A = document.createTextNode(arguments[0]);
break;
default:
var A = document.createElement(arguments[0]),
B = arguments[1];
for (var b in B) {
if (b.indexOf("on") == 0) {
A.addEventListener ? A.addEventListener(b.substring(2), B[b], false) : A.attachEvent(b,B[b]);
} else if (",style,accesskey,id,name,src,href,for,value,".indexOf("," + b.toLowerCase()) != -1) {
A.setAttribute(b, B[b]);
} else {
A[b] = B[b];
}
}
for(var i = 2, len = arguments.length; i < len; ++i) {
A.appendChild(arguments[i]);
}
}
return A;
},
compareClassName: function(className, splitNames) {
if(splitNames.trim() == className.trim()) {
return true;
}
splitNames = splitNames.split(' ');
for(var index = 0; index < splitNames.length; index++) {
if(splitNames[index] == className) {
return true;
}
}
return false;
},
cors: function (url, method, type, data, callback, errback) {
var req;
if(XMLHttpRequest) {
req = new XMLHttpRequest();
overtooning.addLog('[overtooning.cors] ' + url);
if(req.withCredentials !== undefined) {
req.open(method, url, true);
req.responseType = type;
req.onerror = errback;
req.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status >= 200 && this.status < 400) {
callback(this.responseType == '' ? this.responseText : this.response);
} else {
overtooning.addLog('[overtooning.cors] Response returned with non-OK status');
if(errback) errback({message: '[overtooning.cors] Response returned with non-OK status'});
}
}
};
req.send(/*data*/);
}
} else {
overtooning.addLog('[overtooning.cors] XmlHTTPRequest 2 not fully supported');
if(errback) errback({message: '[overtooning.cors] XmlHTTPRequest 2 not fully supported'});
}
},
MTime: function() {
return Math.round(new Date().getTime() / 1000 / 60);
}
}
overtooning.run();