您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Filter for anime trackers [planning for many]
// ==UserScript== // @name Anime Lighter // @namespace horc.net // @version 3.7.1 // @description Filter for anime trackers [planning for many] // @author RandomClown @ HoRC // @copyright © 2015 // @homepage http://git.horc.net // @icon https://bitbucket.org/horc/anime-lighter/raw/master/img/Logo.png // @grant GM_xmlhttpRequest // @grant GM_log // @require http://code.jquery.com/jquery-2.1.3.min.js // @require https://code.jquery.com/ui/1.11.4/jquery-ui.min.js // @match http://www.nyaa.se/* // @match http://horriblesubs.info // ==/UserScript== var greasyfork = true; // // // Source code available: https://bitbucket.org/horc/anime-lighter/ // // /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> /// <reference path="Class AnimeFilter.js" /> /// <reference path="Class LighterUI.js" /> /// <reference path="Class Run.js" /> /// <reference path="Class StoreUpdater.js" /> /// <reference path="Standard.js" /> var HORC_NAME = 'Anime Lighter Beta'; // Wait for ready new function () { var runonce = 0; function complete() { if (document.readyState === 'complete') { document.removeEventListener('DOMContentLoaded', complete, false); window.removeEventListener('load', complete, false); if (runonce++) return; setTimeout(ready); } else { document.addEventListener('DOMContentLoaded', complete, false); window.addEventListener('load', complete, false); } } complete(); } // Now ready function ready() { new StoreUpdater('horc-animes', '1.0'); // <-- refrain from updating this one new StoreUpdater('horc-position', '1.1'); new StoreUpdater('horc-style', '1.0'); if (document.horc_updated) { location.reload(true); return; } run_animelighter(); } // Run main module function run_animelighter() { // Load site mod var mod = document.mod; delete document.readymods; console.log(' Host: ' + window.location.host); mod.createpositions(); new MainUI(mod.style); new AnimeFilter(mod.filteroptions); mod.run(); } /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> // Get Anime - Generic // // Parse an episode text for the anime name // Works for most things function default_getanime(jqe) { var str = jqe.text(); return smartget(str); } //////////////////////////////////////// //// Useful Functions function smartget(str) { // Make it sane var str = trimws(str.replace(/_/g, ' ').replace(/\s\s+/g, ' ')); if (!str.length) throw 'Expected a string'; // Get rid of leading subber if (-1 < str.charAt(0).search(/[\[\(\{]/)) str = trimws(consumetag(str)); // Get rid of next tag & beyond str = trimws(str.substr(0, str.search(/[\[\(\{]/))); // Get rid of episode number[s] of format str = trimws(consumenumber(str)); if (!str.length) throw 'Couldn\'t get the name'; return str; } // Consume Tag // // Gets rid of 1 tag: [subber], [1080p], [etc] function consumetag(str) { var mode = false, done = false; var count = 0; var s = 0, e = 0; for (var i = 0; i < str.length; ++i) { if (done) break; switch (str.charAt(i)) { case '[': case '(': case '{': mode = true; count++; break; case ']': case ')': case '}': count--; if (mode && !count) done = true; break; default: break; } if (mode) e++; else s++, e++; } return str.substr(0, s) + str.substr(e); } // Consume Number // // Gets rid of the episode number function consumenumber(str) { // Consumes numbers with a tack before function cnum_tack(str) { for (var s = 0; s < str.length ; ++s) { var hitnum = false; if (str.charAt(s) === '-' || str.charAt(s) === '‒') { var n = s + 1; // Consume ws for (; n < str.length; ++n) if (!isws(str.charAt(n))) break; // Consume number for (; n < str.length; ++n) { if (-1 < str.charAt(n).search(/\d/)) hitnum = true; break; } if (hitnum) { return str.substr(0, s); } } } return str; } // Consumes numbers without a tack before function cnum_none(str) { var digits = 0; var i = 0; for (i = str.length - 1; 0 <= i; --i) { if (-1 < str.charAt(i).search(/\d/)) digits++; if (str.charAt(i).search(/\d/) === -1) break; } if (i === 0) return str; // reached the end if (1 < digits && isws(str.charAt(i))) return str.substr(0, i); // lone number & at least 2 digits return str; // some mixed number like: S2 } str = cnum_tack(str); str = cnum_none(str); return str; } // Trim Whitespace // // Gets rid of beginning & ending whitespace function trimws(str) { var mode = false, done = false; var count = 0; var s = 0, e = 0; for (s = 0; s < str.length; ++s) { if (str.charAt(s) === ' ') continue; if (str.charAt(s) === '\t') continue; if (str.charAt(s) === '\r') continue; if (str.charAt(s) === '\n') continue; break; } for (e = str.length - 1; 0 <= e; --e) { if (str.charAt(e) === ' ') continue; if (str.charAt(e) === '\t') continue; if (str.charAt(e) === '\r') continue; if (str.charAt(e) === '\n') continue; break; } return str.substr(s, e - s + 1); } function isws(str) { for (var i = 0; i < str.length; ++i) if (!(str.charAt(i) === ' ' || str.charAt(i) === '\t' || str.charAt(i) === '\n' || str.charAt(i) === '\r' || str.charAt(i) === ' ')) return false; return true; } /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> /// <reference path="_ Host All.js" /> if ('horriblesubs.info' === window.location.host) { var options = {}; options.style = "/* Configuration for horriblesubs.info */\n\n.horc-sidebar {\n\tfont-size: 0.75em;\n\twidth: 10em;\n}\n\n\t.horc-sidebar:hover {\n\t\twidth: 44em;\n\t}\n\n\t.horc-sidebar #horc-mainui {\n\t\twidth: 40em;\n\t}\n\n.horc-ep-droppanel {\n\tfont-size: 1.5em;\n}\n\n\n\n/* Original episode list */\n\n.horc-episode-orig.watch {\n\tbackground-color: rgba(0, 255, 0, 0.25);\n}\n\n.horc-episode-orig.drop {\n\tcolor: rgba(0, 0, 0, 0.15);\n}\n\n.horc-episode-orig.hide {\n\tdisplay: none;\n\tvisibility: hidden;\n}\n\n.horc-episode-orig.dragging {\n\tfont-weight: bolder;\n}\n\n\n\n/* Main UI */\n\n#horc-statusbar {\n\tfont-size: 1.2em;\n\ttext-align: center;\n\tbackground-color: rgba(200,255,200, 0.9);\n\twidth: 100%;\n\tposition: fixed;\n\tpadding: 1em 0 1em 0;\n\tmargin: 0;\n\tleft: 0;\n\tbottom: 0;\n\tborder-radius: 1em;\n}\n\n\t#horc-statusbar > div {\n\t\tmargin: 0 1em 0 1em;\n\t}\n\n.horc-slot {\n\ttext-align: center;\n\twidth: 100%;\n}\n\n#horc-mainui {\n\ttext-align: center;\n\tbackground-color: rgba(230, 230, 230, 0.96);\n\tuser-select: none;\n}\n\n\t#horc-mainui > div {\n\t\tmargin: 1em 0;\n\t\tcursor: initial;\n\t\tuser-select: initial;\n\t}\n\n\t#horc-mainui h2 {\n\t\tcolor: #000000;\n\t\tpadding: 0.2em;\n\t\tmargin: 0;\n\t}\n\n.horc-sidebar {\n\tposition: absolute;\n\tright: 0;\n\toverflow: hidden;\n\tz-index: 1;\n\ttransition: all 0.4s;\n\topacity: 0.25;\n\t-webkit-filter: blur(2px);\n\t-moz-filter: blur(2px);\n\tfilter: blur(2px);\n}\n\n\t.horc-sidebar:hover {\n\t\topacity: 1;\n\t\t-webkit-filter: blur(0px);\n\t\t-moz-filter: blur(0px);\n\t\tfilter: blur(0px);\n\t}\n\n\t.horc-sidebar #horc-mainui {\n\t\tpadding: 2em;\n\t\tpadding-bottom: 0.5em;\n\t\tborder-radius: 2em;\n\t}\n\n\n\n/* Anime Filter */\n\n.horc-listhead {\n\tcursor: default;\n\tuser-select: none;\n}\n\n\t.horc-listhead.horc-listhead-hide {\n\t\tcolor: #888888 !important;\n\t}\n\n.horc-listcounter {\n\tfont-family: Arial;\n\tfont-weight: initial;\n\tmargin-right: -100%;\n\tfloat: left;\n}\n\n#horc-watchlist {\n\tbackground-color: rgba(0, 255, 0, 0.05);\n}\n\n\t#horc-watchlist > .horc-content {\n\t\tbackground-color: rgba(0, 255, 0, 0.1);\n\t}\n\n#horc-droplist {\n\tbackground-color: rgba(0, 0, 0, 0.05);\n}\n\n\t#horc-droplist > .horc-content {\n\t\tbackground-color: rgba(0, 0, 0, 0.1);\n\t}\n\n#horc-hidelist {\n\tbackground-color: rgba(255, 0, 0, 0.05);\n}\n\n\t#horc-hidelist > .horc-content {\n\t\tbackground-color: rgba(255, 0, 0, 0.1);\n\t}\n\n#horc-mainui .horc-episode-filter {\n}\n\n\t#horc-mainui .horc-episode-filter:not(:last-child) {\n\t\tborder-bottom: 0.2em solid rgba(255, 255, 255, 0.8);\n\t}\n\n\n\n/* Position Drag UI */\n\n.horc-posdrag-icon {\n\twidth: 4em;\n\theight: 6em;\n\tmargin-left: -1em;\n\tmargin-top: -1em;\n\tbackground-color: rgb(230, 230, 230);\n\tborder: 0.25em solid #ffffff;\n\tposition: fixed;\n\tz-index: 10;\n\tpointer-events: none;\n}\n\n.horc-slot .horc-ui-droppanel {\n}\n\n\t.horc-slot .horc-ui-droppanel .horc-circle {\n\t\tposition: absolute;\n\t\tdisplay: inline-block;\n\t\tz-index: 8;\n\t\twidth: 8em;\n\t\theight: 8em;\n\t\tmargin: -4em;\n\t\tpadding: 0;\n\t\tbackground: radial-gradient( rgba(100,100,255, 1.0), rgba(100,100,255, 0.8), rgba(100,100,255, 0.8), rgba(100,100,255, 0.8) );\n\t\tborder-radius: 50%;\n\t}\n\n\n\n/* Episode Drag UI */\n\n.horc-episode-icon {\n\tfont-family: Arial, Helvetica, sans-serif;\n\tcolor: #000000;\n\tfont-size: 1.2em;\n\tfont-weight: bold;\n\ttext-align: center;\n\tmax-width: 24em;\n\tmargin-left: -1em;\n\tmargin-top: -3em;\n\tpadding: 1em;\n\tbackground-color: rgb(230, 230, 255);\n\tborder: 0.25em solid #ffffff;\n\tborder-radius: 1em;\n\tposition: fixed;\n\tz-index: 10;\n\tpointer-events: none;\n}\n\n.horc-ep-droppanel {\n\tposition: absolute;\n\tmargin: -8em;\n\twidth: calc(16em);\n\theight: calc(16em);\n\tbackground: radial-gradient( rgba(200,200,255, 0.8), rgba(200,200,255, 0.4), rgba(200,200,255, 0.4), rgba(200,200,255, 0.8) );\n\tborder-radius: 50%;\n\tz-index: 3;\n}\n\n\t.horc-ep-droppanel .horc-circle {\n\t\tcolor: #000000;\n\t\ttext-align: center;\n\t\tpointer-events: initial;\n\t\tposition: absolute;\n\t\tdisplay: table;\n\t\tmargin: calc(4.5em * -0.5);\n\t\twidth: calc(4.5em);\n\t\theight: calc(4.5em);\n\t\tborder-radius: 50%;\n\t}\n\n\t\t.horc-ep-droppanel .horc-circle div {\n\t\t\tdisplay: table-cell;\n\t\t\tvertical-align: middle;\n\t\t\tuser-select: none;\n\t\t}\n\n#watchcircle {\n\tleft: 25%;\n\ttop: 25%;\n\tbackground-color: rgba(140,255,120, 0.75);\n}\n\n\t#watchcircle:hover {\n\t\tbackground-color: rgba(140,255,140, 1.0);\n\t}\n\n#dropcircle {\n\tright: 25%;\n\ttop: 25%;\n\tbackground-color: rgba(140,140,120, 0.75);\n}\n\n\t#dropcircle:hover {\n\t\tbackground-color: rgba(140,140,140, 1.0);\n\t}\n\n#hidecircle {\n\tleft: 25%;\n\tbottom: 25%;\n\tbackground-color: rgba(255,140,120, 0.75);\n}\n\n\t#hidecircle:hover {\n\t\tbackground-color: rgba(255,140,140, 1.0);\n\t}\n\n#clearcircle {\n\tright: 25%;\n\tbottom: 25%;\n\tbackground-color: rgba(255,255,255, 0.75);\n}\n\n\t#clearcircle:hover {\n\t\tbackground-color: rgba(255,255,255, 1.0);\n\t}\n\n\n\n/* Both Drag UI */\n\n.openhand {\n\tcursor: url(http://www.google.com/intl/en_ALL/mapfiles/openhand.cur) 8 4, move;\n}\n\n.closedhand {\n\tcursor: url(http://www.google.com/intl/en_ALL/mapfiles/closedhand.cur) 8 4, move;\n}\n\n.draggable a, .draggable button {\n\tcursor: default;\n}\n\n.noselect {\n\t-webkit-touch-callout: none;\n\t-webkit-user-select: none;\n\t-khtml-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n"; options.createpositions = function () { // Create possible UI positions MainUI.create_sidebar($('<div>').insertBefore($('h2:contains("Releases")')), undefined, ['position', 'absolute', 'right', '4em', ]); MainUI.create_embed($('<div>').insertBefore($('h2:contains("Releases")'))); MainUI.create_embed($('<div>').insertAfter($('.episodecontainer'))); MainUI.create_embed($('<div>').prependTo($('#sidebar'))); MainUI.create_embed($('<div class="horc-default">').insertAfter($('#text-16'))); MainUI.create_embed($('<div>').insertAfter($('#text-8'))); MainUI.create_embed($('<div>').appendTo($('#sidebar'))); }; options.filteroptions = { // What contains the episode listing? epcontainer: '.episodecontainer', // How to get an episode element? epselector: '.episodecontainer .episode', // How to get anime name from an episode element? getanimename: default_getanime, // How to grep episode container for an anime? epsearch: function (animename) { return $('.episodecontainer .episode').filter(':contains("' + animename + '")'); }, }; options.run = function () { // Find every way to update content function refresh(e) { var lastcount = 0; var timeout = 8000; var _check = setInterval(function () { var episodes = $('.episodecontainer .episode:not(.draggable)'); if (lastcount != episodes.length) { lastcount = episodes.length; document.refreshfilters(); timeout = 1000; return; } else if (0 < timeout) { timeout -= 50; return; } clearInterval(_check); }, 50); } function refresh_enter(e) { if (e.which == 13) refresh(); } function refresh_click(e) { refresh(); } $('.searchbar').on('keyup', refresh_enter); $('.refreshbutton').on('keyup', refresh_enter); $('.refreshbutton').on('mouseup', refresh_click); $('.morebox').on('keyup', '.morebutton, .searchmorebutton', refresh_enter); $('.morebox').on('mouseup', '.morebutton, .searchmorebutton', refresh_click); }; document.mod = options; } /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> /// <reference path="_ Host All.js" /> if ('www.nyaa.se' === window.location.host) { var options = {}; options.style = "/* Configuration for www.nyaa.se */\n\n.horc-sidebar {\n\twidth: 100px;\n\ttop: 280px;\n}\n\n\t.horc-sidebar:hover {\n\t\twidth: 44em;\n\t}\n\n\t.horc-sidebar #horc-mainui {\n\t\twidth: 40em;\n\t}\n\n#ddpanel {\n\tfont-size: 1.5em;\n}\n\n.horc-episode-orig:not(.watch):not(.drop):not(.hide) * {\n\tfont-weight: normal !important;\n}\n\n.horc-episode-orig.watch .tlistname {\n\tfont-weight: 800 !important;\n}\n\n.horc-episode-orig.drop {\n\topacity: 0.30;\n}\n\n\n\n/* Original episode list */\n\n.horc-episode-orig.watch {\n\tbackground-color: rgba(0, 255, 0, 0.25);\n}\n\n.horc-episode-orig.drop {\n\tcolor: rgba(0, 0, 0, 0.15);\n}\n\n.horc-episode-orig.hide {\n\tdisplay: none;\n\tvisibility: hidden;\n}\n\n.horc-episode-orig.dragging {\n\tfont-weight: bolder;\n}\n\n\n\n/* Main UI */\n\n#horc-statusbar {\n\tfont-size: 1.2em;\n\ttext-align: center;\n\tbackground-color: rgba(200,255,200, 0.9);\n\twidth: 100%;\n\tposition: fixed;\n\tpadding: 1em 0 1em 0;\n\tmargin: 0;\n\tleft: 0;\n\tbottom: 0;\n\tborder-radius: 1em;\n}\n\n\t#horc-statusbar > div {\n\t\tmargin: 0 1em 0 1em;\n\t}\n\n.horc-slot {\n\ttext-align: center;\n\twidth: 100%;\n}\n\n#horc-mainui {\n\ttext-align: center;\n\tbackground-color: rgba(230, 230, 230, 0.96);\n\tuser-select: none;\n}\n\n\t#horc-mainui > div {\n\t\tmargin: 1em 0;\n\t\tcursor: initial;\n\t\tuser-select: initial;\n\t}\n\n\t#horc-mainui h2 {\n\t\tcolor: #000000;\n\t\tpadding: 0.2em;\n\t\tmargin: 0;\n\t}\n\n.horc-sidebar {\n\tposition: absolute;\n\tright: 0;\n\toverflow: hidden;\n\tz-index: 1;\n\ttransition: all 0.4s;\n\topacity: 0.25;\n\t-webkit-filter: blur(2px);\n\t-moz-filter: blur(2px);\n\tfilter: blur(2px);\n}\n\n\t.horc-sidebar:hover {\n\t\topacity: 1;\n\t\t-webkit-filter: blur(0px);\n\t\t-moz-filter: blur(0px);\n\t\tfilter: blur(0px);\n\t}\n\n\t.horc-sidebar #horc-mainui {\n\t\tpadding: 2em;\n\t\tpadding-bottom: 0.5em;\n\t\tborder-radius: 2em;\n\t}\n\n\n\n/* Anime Filter */\n\n.horc-listhead {\n\tcursor: default;\n\tuser-select: none;\n}\n\n\t.horc-listhead.horc-listhead-hide {\n\t\tcolor: #888888 !important;\n\t}\n\n.horc-listcounter {\n\tfont-family: Arial;\n\tfont-weight: initial;\n\tmargin-right: -100%;\n\tfloat: left;\n}\n\n#horc-watchlist {\n\tbackground-color: rgba(0, 255, 0, 0.05);\n}\n\n\t#horc-watchlist > .horc-content {\n\t\tbackground-color: rgba(0, 255, 0, 0.1);\n\t}\n\n#horc-droplist {\n\tbackground-color: rgba(0, 0, 0, 0.05);\n}\n\n\t#horc-droplist > .horc-content {\n\t\tbackground-color: rgba(0, 0, 0, 0.1);\n\t}\n\n#horc-hidelist {\n\tbackground-color: rgba(255, 0, 0, 0.05);\n}\n\n\t#horc-hidelist > .horc-content {\n\t\tbackground-color: rgba(255, 0, 0, 0.1);\n\t}\n\n#horc-mainui .horc-episode-filter {\n}\n\n\t#horc-mainui .horc-episode-filter:not(:last-child) {\n\t\tborder-bottom: 0.2em solid rgba(255, 255, 255, 0.8);\n\t}\n\n\n\n/* Position Drag UI */\n\n.horc-posdrag-icon {\n\twidth: 4em;\n\theight: 6em;\n\tmargin-left: -1em;\n\tmargin-top: -1em;\n\tbackground-color: rgb(230, 230, 230);\n\tborder: 0.25em solid #ffffff;\n\tposition: fixed;\n\tz-index: 10;\n\tpointer-events: none;\n}\n\n.horc-slot .horc-ui-droppanel {\n}\n\n\t.horc-slot .horc-ui-droppanel .horc-circle {\n\t\tposition: absolute;\n\t\tdisplay: inline-block;\n\t\tz-index: 8;\n\t\twidth: 8em;\n\t\theight: 8em;\n\t\tmargin: -4em;\n\t\tpadding: 0;\n\t\tbackground: radial-gradient( rgba(100,100,255, 1.0), rgba(100,100,255, 0.8), rgba(100,100,255, 0.8), rgba(100,100,255, 0.8) );\n\t\tborder-radius: 50%;\n\t}\n\n\n\n/* Episode Drag UI */\n\n.horc-episode-icon {\n\tfont-family: Arial, Helvetica, sans-serif;\n\tcolor: #000000;\n\tfont-size: 1.2em;\n\tfont-weight: bold;\n\ttext-align: center;\n\tmax-width: 24em;\n\tmargin-left: -1em;\n\tmargin-top: -3em;\n\tpadding: 1em;\n\tbackground-color: rgb(230, 230, 255);\n\tborder: 0.25em solid #ffffff;\n\tborder-radius: 1em;\n\tposition: fixed;\n\tz-index: 10;\n\tpointer-events: none;\n}\n\n.horc-ep-droppanel {\n\tposition: absolute;\n\tmargin: -8em;\n\twidth: calc(16em);\n\theight: calc(16em);\n\tbackground: radial-gradient( rgba(200,200,255, 0.8), rgba(200,200,255, 0.4), rgba(200,200,255, 0.4), rgba(200,200,255, 0.8) );\n\tborder-radius: 50%;\n\tz-index: 3;\n}\n\n\t.horc-ep-droppanel .horc-circle {\n\t\tcolor: #000000;\n\t\ttext-align: center;\n\t\tpointer-events: initial;\n\t\tposition: absolute;\n\t\tdisplay: table;\n\t\tmargin: calc(4.5em * -0.5);\n\t\twidth: calc(4.5em);\n\t\theight: calc(4.5em);\n\t\tborder-radius: 50%;\n\t}\n\n\t\t.horc-ep-droppanel .horc-circle div {\n\t\t\tdisplay: table-cell;\n\t\t\tvertical-align: middle;\n\t\t\tuser-select: none;\n\t\t}\n\n#watchcircle {\n\tleft: 25%;\n\ttop: 25%;\n\tbackground-color: rgba(140,255,120, 0.75);\n}\n\n\t#watchcircle:hover {\n\t\tbackground-color: rgba(140,255,140, 1.0);\n\t}\n\n#dropcircle {\n\tright: 25%;\n\ttop: 25%;\n\tbackground-color: rgba(140,140,120, 0.75);\n}\n\n\t#dropcircle:hover {\n\t\tbackground-color: rgba(140,140,140, 1.0);\n\t}\n\n#hidecircle {\n\tleft: 25%;\n\tbottom: 25%;\n\tbackground-color: rgba(255,140,120, 0.75);\n}\n\n\t#hidecircle:hover {\n\t\tbackground-color: rgba(255,140,140, 1.0);\n\t}\n\n#clearcircle {\n\tright: 25%;\n\tbottom: 25%;\n\tbackground-color: rgba(255,255,255, 0.75);\n}\n\n\t#clearcircle:hover {\n\t\tbackground-color: rgba(255,255,255, 1.0);\n\t}\n\n\n\n/* Both Drag UI */\n\n.openhand {\n\tcursor: url(http://www.google.com/intl/en_ALL/mapfiles/openhand.cur) 8 4, move;\n}\n\n.closedhand {\n\tcursor: url(http://www.google.com/intl/en_ALL/mapfiles/closedhand.cur) 8 4, move;\n}\n\n.draggable a, .draggable button {\n\tcursor: default;\n}\n\n.noselect {\n\t-webkit-touch-callout: none;\n\t-webkit-user-select: none;\n\t-khtml-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n"; options.createpositions = function () { // Create possible UI positions MainUI.create_sidebar($('<div>').insertBefore($('#main')), undefined, ['position', 'absolute', 'right', '4em', 'top', '20em']); MainUI.create_embed($('<div>').insertBefore($('#main .tlistsortorder')), ['width', '100%', 'text-align', 'center']); MainUI.create_embed($('<div class="horc-default">').insertAfter($('#main .torrentsubcatlist')), ['width', '100%', 'text-align', 'center']); }; options.filteroptions = { // What contains the episode listing? epcontainer: '.tlist', // How to get an episode element? epselector: '.tlist .tlistrow', // How to get anime name from an episode element? getanimename: default_getanime, // How to grep episode container for an anime? epsearch: function (animename) { return $('.tlist .tlistrow').has('.tlistname:contains("' + animename + '")'); }, }; options.run = function () { }; document.mod = options; } // String Compare Case-Sensitive function strcmp(lhs, rhs) { for (var i = 0; i < lhs.length && i < rhs.length; ++i) { if (lhs[i] === rhs[i]) continue; return lhs[i] < rhs[i] ? -1 : 1; } if (lhs.length === rhs.length) return 0; return lhs.length < rhs.length ? -1 : 1; } // String Compare Case-Insensitive function strcmpi(lhs, rhs) { for (var i = 0; i < lhs.length && i < rhs.length; ++i) { if (lhs[i].toLowerCase() === rhs[i].toLowerCase()) continue; return lhs[i].toLowerCase() < rhs[i].toLowerCase() ? -1 : 1; } if (lhs.length === rhs.length) return 0; return lhs.length < rhs.length ? -1 : 1; } // Binary Search Template // // In the options, method_options below, you can override the methods: // // int compare(element_to_find, element_in_container) // Expected return: -1, 0, 1 // Determines if the element is less or greater than // Behavior should match this: http://www.cplusplus.com/reference/cstring/strcmp/ // // int length(container) // Expected return: int // Redefines how to check the size of the container [default container.length] // // ElementType get(i, container) // Expected return: a type matching the parameters of compare(e0, e1) // Redefines how to access the container by some index // // * found(i, container) // Expected return: anything you require // Redefines what to return // // * notfound(i, container) // Expected return: anything you require // Redefines what to return when not found [default null] // This function is useful to ask "where to insert", since // "i" represent the position to insert the new element, using splice: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice // // Usage [2]: int, null binsearch(string_to_find, array_of_strings) // Usage [3]: custom binsearch(element_to_find, container_object, method_options) function binsearch(element, container, options) { if (options === undefined) options = { compare: undefined, length: undefined, get: undefined, found: undefined, notfound: undefined, }; var compare = options.compare; var length = options.length; var get = options.get; var found = options.found; var notfound = options.notfound; if (compare === undefined) compare = strcmpi; function default_len(container) { return container.length; } if (length === undefined) length = default_len; function default_arrget(i, container) { return container[i]; } if (get === undefined) get = default_arrget; function default_arrret(i, container) { return i; } if (found === undefined) found = default_arrret; function default_arrnotret(i, container) { return null; } if (notfound === undefined) notfound = default_arrnotret; if (!length(container)) return notfound(0, container); var begin = 0; var end = length(container) - 1; var mid = begin + (end - begin) / 2 | 0; while (true) { if (begin === end) { switch (compare(element, get(mid, container))) { case -1: return notfound(mid, container); case 0: return found(mid, container); case 1: return notfound(mid + 1, container); } } switch (compare(element, get(mid, container))) { case -1: end = mid; mid = begin + (end - begin) / 2 | 0; break; case 0: return found(mid, container); case 1: begin = mid + 1; mid = begin + (end - begin) / 2 | 0; break; } } } /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> /// <reference path="Algorithms.js" /> /// <reference path="Class Store.js" /> var FLAG_WATCH = 0; var FLAG_DROP = 1; var FLAG_HIDE = 2; var ANIME_NAME = 0; var ANIME_FLAG = 1; var ANIME_DATE = 2; var ANIME_EXPIRE = 3; // Anime Structure // // Holds information on an anime // // Member Data: // - name | Name of the anime according to the tracker // - flag | View flag // - date_y | Date added // - date_m | ^ // - date_d | ^ // - expire_y | Date this will expire on [-1 for never] // - expire_m | ^ // - expire_d | ^ // // Member Functions: // - isgood | Test if expired // - setexpirecours | Set the expiration date ## "anime network season" from today // - setexpireweeks | Set the expiration date ## weeks from today // - setexpiredays | Set the expiration date ## days from today // - compress | Compress the data for storage // - decompress | Copies data from a compressed data array // // Usage [0]: new Anime() // Usage [1]: new Anime(anime_dataset) var Anime = function (anime_dataset) { var thisanime = this; this.Anime = function (anime_dataset) { if (typeof anime_dataset === 'object') { thisanime.decompress(anime_dataset); } else { var d = new Date(); thisanime.date_y = d.getFullYear(); thisanime.date_m = d.getMonth() + 1; thisanime.date_d = d.getDate(); delete d; } }; this.isgood = function () { if (thisanime.expire_y === -1) return true; var diff = (new Date(thisanime.expire_y, thisanime.expire_m - 1, thisanime.expire_d - 1)) - (new Date(thisanime.date_y, thisanime.date_m - 1, thisanime.date_d)); if (-1 < diff) return true; return false; }; this.setexpiredays = function (amount) { var d = new Date(); var e = new Date(d.getFullYear(), d.getMonth(), d.getDate() + amount); thisanime.expire_y = e.getFullYear(); thisanime.expire_m = e.getMonth() + 1; thisanime.expire_d = e.getDate(); }; this.setexpireweeks = function (amount) { thisanime.setexpiredays(amount * 7); }; this.setexpirecours = function (amount) { thisanime.setexpiredays(amount * 7 * 13); }; this.compress = function () { var anime = []; anime[ANIME_NAME] = thisanime.name; // Name of anime anime[ANIME_FLAG] = thisanime.flag; // w d h anime[ANIME_DATE] = thisanime.date_y; anime[ANIME_DATE] = anime[ANIME_DATE] * 100 + thisanime.date_m; anime[ANIME_DATE] = anime[ANIME_DATE] * 100 + thisanime.date_d; if (thisanime.expire_y === -1) { anime[ANIME_EXPIRE] = -1; } else { anime[ANIME_EXPIRE] = thisanime.expire_y; anime[ANIME_EXPIRE] = anime[ANIME_EXPIRE] * 100 + thisanime.expire_m; anime[ANIME_EXPIRE] = anime[ANIME_EXPIRE] * 100 + thisanime.expire_d; } return anime; }; this.decompress = function (anime_dataset) { thisanime.name = anime_dataset[ANIME_NAME]; // Name of anime_dataset thisanime.flag = anime_dataset[ANIME_FLAG]; // w d h var value = anime_dataset[ANIME_DATE]; thisanime.date_d = Math.floor(value % 100); value /= 100; thisanime.date_m = Math.floor(value % 100); value /= 100; thisanime.date_y = Math.floor(value); var value = anime_dataset[ANIME_EXPIRE]; if (value === -1) { thisanime.expire_y = thisanime.expire_m = thisanime.expire_d = -1; } else { thisanime.expire_d = Math.floor(value % 100); value /= 100; thisanime.expire_m = Math.floor(value % 100); value /= 100; thisanime.expire_y = Math.floor(value); } return thisanime; }; this.name = ''; // Name of anime this.flag = -1; // w d h this.date_y = -1; this.date_m = -1; this.date_d = -1; this.expire_y = -1; this.expire_m = -1; this.expire_d = -1; this.Anime(anime_dataset); }; // Anime List Manager // // Holds information on an anime // // Public Methods // - get | Get an anime entry; If not found, return the insertion index // - add | Add an anime by Anime struct // - rm | Set the expiration date ## weeks from today // - save | Save data to storage // - load | Reload data from storage // // Usage [0]: AnimeList() var AnimeList = function () { var thisanimelist = this; var store = new function () { this.anime = new Store('horc-animes', [], 'object'); }; store.anime.list = []; this.AnimeList = function () { thisanimelist.load(); }; // Add Anime // // Inserts the anime to the list & storage // // Usage [1]: add(Anime_structure) this.add = function (anime) { var find = thisanimelist.get(anime.name); if (typeof find === 'number') { // Not found store.anime.list.splice(find, 0, anime); // insert } else { // Found store.anime.list.splice(find[0], 1, anime); // replace } thisanimelist.save(); }; // Remove Anime // // Remove the anime from the list & storage // // Usage [1]: rm(string_name) this.rm = function (name) { var find = thisanimelist.get(name); if (typeof find === 'number') { // Not found } else { // Found store.anime.list.splice(find[0], 1); // replace } thisanimelist.save(); }; // Get Anime // // Return: // If found, the array [ index, Anime structure ] // If not found, the index at which to insert the anime // // Usage [1]: get(string_name) this.get = function (name) { return binsearch(name, store.anime.list, { get: function (i, container) { return container[i].name; }, found: function (i, container) { return [i, container[i]]; }, notfound: function (i, container) { return i; }, }); }; // Save Anime List // // Save the anime list to storage // // Usage [0]: save() this.save = function () { var compressed = $(store.anime.list).map(function (i, e) { return [e.compress()]; }); store.anime.set(compressed); }; // Load Anime List // // Reload the anime list from storage // // Note: // This will also delete expired entries from storage. // // Usage [0]: load() this.load = function () { var deletions = false; store.anime.list = $(store.anime.get()).map(function (i, e) { var anime = new Anime(e); if (anime.isgood()) return anime; deletions = true; }); if (deletions) thisanimelist.save(); }; this.AnimeList(); }; /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> /// <reference path="Class Anime.js" /> /// <reference path="Class Store.js" /> /// <reference path="Class Touch.js" /> // AnimeFilter module // // Constructor takes the string selector for slots the UI may position itself in. // Events are listened to & fired in $(document) // // Events this will listen for: // - set-active | Make the program interactable // - clear-active | Make the program non-interactable // - set-ui-droppanel | Open the MainUI position drop panel // - clear-ui-droppanel | Close the MainUI position drop panel // - set-ep-droppanel | Open the episode list drop panel // - clear-ep-droppanel | Close the episode list drop panel // - update-animelist | Update the anime list count // // Events this will trigger: // - // // Options: // - // // Note: // You are required to allocate at least 1 position as empty elements before constructing this. var AnimeFilter = function (options) { var thisanimefilter = this; var animelist = null; this.AnimeFilter = function () { document.animelist = animelist = new AnimeList(); // Bind episodes now & add to document thisanimefilter.refreshfilters(); document.refreshfilters = thisanimefilter.refreshfilters; firstbindtouch(); $(document).on('update-animelist', updatelist); $(document).trigger('update-animelist'); }; function firstbindtouch() { // Search an element, then it's parents for a selector function treehas(element, selector) { // Check this var jqe = $(element).filter(selector); if (jqe.length) return $(element); // Check parents var jqe = $(element).parents(selector); if (jqe.length) return jqe; return null; } function typeofelement(jqe) { if (jqe.hasClass('horc-episode-filter')) return 'fi'; // Episode Filter if (jqe.hasClass('horc-episode-orig')) return 'ep'; // Episode Original if (jqe.hasClass('horc-circle')) return 'ep'; // Episode Original if (jqe.hasClass('horc-slot')) return 'ui'; // UI if (jqe.hasClass('horc-ui')) return 'ui'; // UI return ''; } function shouldreject(jqe) { return !!treehas(jqe, 'a,button,datalist,input,keygen,output,select,textarea'); } var touch = new Touch(); var container = $('body'); container.on('mousedown', touch.mousedown); container.on('mousemove', touch.mousemove); container.on('mouseup', touch.mouseup); container.on('touchstart', touch.touchstart); container.on('touchmove', touch.touchmove); container.on('touchend', touch.touchend); touch.onstart = function (ids, changes, e) { }; touch.onend = function (ids, changes, e) { }; touch.ondragstart = function (ids, changes, e) { // If requires control & pressing control don't match, cancel if (e.ctrlKey ^ document.store.requirectrl.get()) return; $(changes).map(function (i, changed) { if (id < -1) return; // ignore middle & right click var changed = e.originalEvent.changedTouches[i]; var id = changed.identifier; if (shouldreject(changed.target)) return; // Check if drag exists var jqedrag = treehas(changed.target, '.draggable'); if (!jqedrag) return; // Check type of drag & drop var typeofdrag = typeofelement(jqedrag); switch (typeofdrag) { case 'ep': var panel = $('.horc-ep-droppanel'); if (!panel.is(':visible')) { panel.css('left', changed.pageX); panel.css('top', changed.pageY); } $(document).trigger('set-ep-droppanel') try { addepicon(id, options.getanimename(jqedrag)); } catch (err) { setstatus('<br>Could\'t get the anime\'s name, please report this:<br>\n<span style="color: #800000;">' + jqedrag.text() + '</span>'); } break; case 'fi': var panel = $('.horc-ep-droppanel'); if (!panel.is(':visible')) { panel.css('left', changed.pageX); panel.css('top', changed.pageY); } $(document).trigger('set-ep-droppanel') addepicon(id, jqedrag.text()); break; case 'ui': $(document).trigger('set-ui-droppanel') $('#horc-mainui').hide(); adduiicon(id); break; } e.preventDefault(); }); }; touch.ondragend = function (ids, changes, e) { $(changes).map(function (i, changed) { if (id < -1) return; // ignore middle & right click var changed = e.originalEvent.changedTouches[i]; var id = changed.identifier; if (shouldreject(changed.target)) return; rmepicon(id); rmuiicon(id); // Check if drag & drop both exists var jqedrag = treehas(changed.target, '.draggable'); if (!jqedrag) return; var jqedrop = treehas(changed.targetnow, '.droppable'); if (!jqedrop) $('#horc-mainui').show(); if (!jqedrop) return; // Check type of drag & drop var typeofdrag = typeofelement(jqedrag); var typeofdrop = typeofelement(jqedrop); switch (typeofdrag) { case 'ep': switch (typeofdrop) { case 'ep': try { filteranime(options.getanimename(jqedrag), jqedrop); } catch (err) { setstatus('<br>Could\'t get the anime\'s name, please report this:<br>\n<span style="color: #800000;">' + jqedrag.text() + '</span>'); } break; case 'ui': break; } break; case 'fi': switch (typeofdrop) { case 'ep': filteranime(jqedrag.text(), jqedrop); break; case 'ui': break; } break; case 'ui': switch (typeofdrop) { case 'ep': break; case 'ui': $('.horc-slot').map(function (i, element) { if (jqedrop[0] === element) document.store.position.set(i); }); $('#horc-mainui').appendTo(jqedrop.children('.horc-content')).show(); break; } break; } e.preventDefault(); }); }; touch.onmove = function (ids, changes, e) { $(changes).map(function (i, changed) { if (id < -1) return; // ignore middle & right click var id = changed.identifier; var icon = $('#horc-ep' + id + ',#horc-pos' + id); if (icon.length) { icon.css('left', changed.clientX); icon.css('top', changed.clientY); e.preventDefault(); } }); }; touch.onfirst = function (ids, changes, e) { if (e.ctrlKey ^ document.store.requirectrl.get()) return; $('#horc-mainui,' + options.epcontainer).addClass('noselect'); }; touch.onlast = function (ids, changes, e) { $(document).trigger('clear-ep-droppanel'); $(document).trigger('clear-ui-droppanel'); $('#horc-mainui,' + options.epcontainer).removeClass('noselect'); }; } function updatelist() { $('#horc-mainui .horc-episode-filter').remove(); var eps = $('.horc-episode-orig'); eps.map(function (i, element) { var ep = $(element); var animename = ''; try { animename = options.getanimename(ep); } catch (err) { return; } // Check which filter this episode falls under var flag = -1; if (ep.hasClass('watch')) flag = FLAG_WATCH; else if (ep.hasClass('drop')) flag = FLAG_DROP; else if (ep.hasClass('hide')) flag = FLAG_HIDE; if (flag == -1) return; // Construct the list selector var whichselector = ''; switch (flag) { case FLAG_WATCH: whichselector = 'watch'; break; case FLAG_DROP: whichselector = 'drop'; break; case FLAG_HIDE: whichselector = 'hide'; break; } // Populate the filter lists, since they were cleared before this map // Check if the list has the anime var list = $('#horc-' + whichselector + 'list .horc-content'); var inlist = !!list.find('.horc-episode-filter').map(function (i, element) { var filter = $(element); var filtername = filter.text(); if (filtername === animename) return filtername; }).length; // Add it maybe if (!inlist) { var filter = $('<div class="horc-episode-filter draggable">'); filter.text(animename); list.append(filter); } }); } // Rebind Episodes // // Rebinds the drag event to new episode elements // // Note: // You will need to call this anytime episode lists are populated dynamically. this.refreshfilters = function () { $(options.epselector).filter(':not(.horc-episode-orig)').map(function (i, element) { var jqe = $(element); jqe.addClass('horc-episode-orig draggable'); var animename = ''; try { animename = options.getanimename(jqe); } catch (err) { return; } var search = animelist.get(animename); if (typeof search === 'number') { // Not yet marked return; // Dont highlight } var flag = search[1].flag; var whichselector = ''; switch (flag) { case FLAG_WATCH: whichselector = 'watch'; break; case FLAG_DROP: whichselector = 'drop'; break; case FLAG_HIDE: whichselector = 'hide'; break; } jqe.removeClass('watch drop hide'); jqe.addClass(whichselector); }); $(document).trigger('update-animelist'); }; function filteranime(animename, jqedrop) { // Check which filter this episode falls under var flag = 0; if (jqedrop.filter('#clearcircle').length) flag = -1; else if (jqedrop.filter('#watchcircle').length) flag = FLAG_WATCH; else if (jqedrop.filter('#dropcircle').length) flag = FLAG_DROP; else if (jqedrop.filter('#hidecircle').length) flag = FLAG_HIDE; // Construct the list selector var whichselector = ''; switch (flag) { case FLAG_WATCH: whichselector = 'watch'; break; case FLAG_DROP: whichselector = 'drop'; break; case FLAG_HIDE: whichselector = 'hide'; break; case -1: break; } if (flag === -1) { // Clear // Remove highlight var eps = options.epsearch(animename); eps.removeClass('watch drop hide'); // Remove from list animelist.rm(animename); $(document).trigger('update-animelist'); return; } /////////////////////// // Watch/Drop/Hide // // Re-highlight var eps = options.epsearch(animename); eps.removeClass('watch drop hide'); eps.addClass(whichselector); var search = animelist.get(animename); if (typeof search === 'number') { // Not yet added var newanime = new Anime(); // Update newanime.name = animename; newanime.flag = flag; newanime.setexpirecours(2); // Save animelist.add(newanime); animelist.save(); } else { // Already there; update it if (search[1].flag !== flag) { // update the flag // Update search[1].flag = flag; animelist.save(); } // Else do nothing } $(document).trigger('update-animelist'); } function addepicon(id, name) { rmepicon(id); // Drag icon var icon = $('<div id="horc-ep' + id + '" class="horc-episode-icon">' + name + '</div>'); $('body').append(icon); } function adduiicon(id) { rmuiicon(id); // Drag icon var icon = $('<div id="horc-pos' + id + '" class="horc-posdrag-icon">'); $('body').append(icon); } function rmepicon(id) { $('#horc-ep' + id).remove(); } function rmuiicon(id) { $('#horc-pos' + id).remove(); } var timer_statusbar = null; function setstatus(msg) { var statusbar = $('#horc-statusbar'); var msgbox = statusbar.children(); if (timer_statusbar === null) { msgbox.empty().append(msg); statusbar.show({ direction: 'down' }, 250); timer_statusbar = setTimeout(function () { statusbar.hide({ direction: 'down' }, 250); }, 10000); } else { clearTimeout(timer_statusbar); timer_statusbar = null; setstatus(msg); } } this.AnimeFilter(); }; /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> /// <reference path="Class Store.js" /> // MainUI module // // Constructor takes the string selector for slots the UI may position itself in. // Events are listened to & fired in $(document) // // Events this will listen for: // - set-active | Make the program interactable // - clear-active | Make the program non-interactable // - set-ui-droppanel | Open the MainUI position drop panel // - clear-ui-droppanel | Close the MainUI position drop panel // - set-ep-droppanel | Open the episode list drop panel // - clear-ep-droppanel | Close the episode list drop panel // - update-animelist | Update the anime list count // // Static methods to know of: // - MainUI.create_embed | Creates an embedded position // - MainUI.create_sidebar | Creates a position for a sidebar // // Note: // You are required to allocate at least 1 position as empty elements before constructing this. // Pass the string selector to your allocated positions. var MainUI = function (default_css) { var thismainui = this; if (document.store == undefined) document.store = {}; document.store.css = new Store('horc-style', default_css, 'string'); document.store.handedness = new Store('horc-handedness', 1, 'number'); document.store.position = new Store('horc-position', -1, 'number'); document.store.requirectrl = new Store('horc-requirectrl', false, 'boolean'); document.store.settings = new Store('horc-settings', false, 'boolean'); document.store.updater = new Store('horc-updater', true, 'boolean'); document.store.showwatch = new Store('horc-show-watch', true, 'boolean'); document.store.showdrop = new Store('horc-show-drop', false, 'boolean'); document.store.showhide = new Store('horc-show-hide', false, 'boolean'); this.MainUI = function () { var slots = $('.horc-slot'); if (slots.length === 0) throw 'No positions for the UI to take'; // Load CSS $('<style id="horc-css">').text(document.store.css.get()).appendTo($('head')); create_mainui(); create_statusbar(); create_epdragui(); create_events(); }; // Get Position Value [+1 overloads] // // Return [0]: // Returns the position of the current selection // The range is from including -1 to number_of_slots // // Return [1]: // Returns the position value passed in, but corrected for out of bounds // The range is from including -1 to number_of_slots // // Note: // Use if you need to calculate a new position that might be out of bounds. // // Usage [0]: getpositionvalue() // Usage [1]: getpositionvalue(position_value_to_test) function getpositionvalue(pos) { var len = $('.horc-slot').length; if (pos === undefined) pos = document.store.position.get(); if (pos < -1) pos = -1; if (len <= pos) pos = len - 1; return pos; }; // Get Currently Selected Position // // Return: // Returns the elements that is chosen to hold the UI // // Usage [0]: getposition() function getposition() { var slots = $('.horc-slot > .horc-content'); var pos = getpositionvalue(); if (pos === -1) { // Find default slot var defaultslot = slots.parent('.horc-default').children('.horc-content'); if (defaultslot.length) { return $(defaultslot[0]); } else { return $(slots[0]); } } return $(slots[pos]); }; // Create Main UI function create_mainui() { if (0 < $('#horc-mainui').length) throw 'Stopped UserScript from loading again'; // when UserScript loads twice, cancel load // Create main ui area var mainui = $('<div id="horc-mainui" class="horc-ui draggable">'); getposition().append(mainui); // Add title & logo var title = $('<h1>' + HORC_NAME + '</h1>'); var logo = $('<img>'); title.prepend(logo); logo.css('width', '2em'); logo.css('height', '2em'); logo.css('margin-right', '0.5em'); logo.css('vertical-align', 'middle'); logo.on('load', function () { setTimeout(function () { title.slideUp(250, function () { title.remove(); }); }, 5000); }); logo.prop('src', 'https://bitbucket.org/horc/anime-lighter/raw/master/img/Logo.png'); mainui.append(title); // Updater var updater = $('<div>'); updater.hide().appendTo(mainui); var url = $('<span>[<a href="https://openuserjs.org/scripts/RandomClown/Anime_Lighter">OpenUserJS</a>]</span>'); function nothing(newversion) { console.log('New version available: v' + newversion); } function versionchecker(newversion) { updater.slideDown(250); setTimeout(function () { updater.slideUp(250, function () { updater.remove(); }); }, 10000); } if (greasyfork) { updater.text('May be out-dated. '); updater.append(url); document.versionchecker = nothing; versionchecker('0.0'); } else { updater.text('New version available! '); updater.append(url); document.versionchecker = document.store.updater.get() ? versionchecker : nothing; } // Create individual filter lists var watchlist = $('<div id="horc-watchlist">'); var droplist = $('<div id="horc-droplist">'); var hidelist = $('<div id="horc-hidelist">'); mainui.append(watchlist); mainui.append(droplist); mainui.append(hidelist); // List headers var watchhead = $('<h2 class="horc-listhead">Watch List</h2>'); var drophead = $('<h2 class="horc-listhead">Drop List</h2>'); var hidehead = $('<h2 class="horc-listhead">Hide List</h2>'); watchlist.append(watchhead); droplist.append(drophead); hidelist.append(hidehead); if (!document.store.showwatch.get()) watchhead.addClass('horc-listhead-hide'); if (!document.store.showdrop.get()) drophead.addClass('horc-listhead-hide'); if (!document.store.showhide.get()) hidehead.addClass('horc-listhead-hide'); var watchcontent = $('<div class="horc-content">'); var dropcontent = $('<div class="horc-content">'); var hidecontent = $('<div class="horc-content">'); watchlist.append(watchcontent); droplist.append(dropcontent); hidelist.append(hidecontent); if (!document.store.showwatch.get()) watchcontent.hide(); if (!document.store.showdrop.get()) dropcontent.hide(); if (!document.store.showhide.get()) hidecontent.hide(); watchhead.click(function () { if ($(this).prop('disabled')) return; watchcontent.slideToggle(250) watchhead.toggleClass('horc-listhead-hide'); document.store.showwatch.set(!document.store.showwatch.get()); }); drophead.click(function () { if ($(this).prop('disabled')) return; dropcontent.slideToggle(250) drophead.toggleClass('horc-listhead-hide'); document.store.showdrop.set(!document.store.showdrop.get()); }); hidehead.click(function () { if ($(this).prop('disabled')) return; hidecontent.slideToggle(250) hidehead.toggleClass('horc-listhead-hide'); document.store.showhide.set(!document.store.showhide.get()); }); var watchcount = $('<span class="horc-listcounter">0</span>') var dropcount = $('<span class="horc-listcounter">0</span>') var hidecount = $('<span class="horc-listcounter">0</span>') watchhead.append(watchcount); drophead.append(dropcount); hidehead.append(hidecount); var delay = null; $('#horc-mainui').on('anime-update', function () { if (!delay) delay = setTimeout(function () { delay = null; watchcount.text(watchlist.find('.episode').length); dropcount.text(droplist.find('.episode').length); hidecount.text(hidelist.find('.episode').length); }, 100); }); create_settings(); } // Create Settings // // This will only show if the "settings" flag in localStorage is set function create_settings() { var mainui = $('#horc-mainui'); // Create Settings button var settingsbutton = $('<button>Settings</button>'); settingsbutton.click(function () { var settings = document.store.settings.get(); document.store.settings.set(!settings); $('#horc-settings').slideToggle(200); }); mainui.append(settingsbutton); // Create settings area var settingui = $('<div id="horc-settings">'); mainui.append(settingui); settingui.css('text-align', 'center'); if (!document.store.settings.get()) $('#horc-settings').hide(); // Create ctrl mode button var ctrlmod = $('<input id="horc-ctrlmod" type="checkbox">') settingui.append(ctrlmod); settingui.append('Require <ctrl> modifier'); ctrlmod.prop('checked', document.store.requirectrl.get()) ctrlmod.on('change', function () { document.store.requirectrl.set($(ctrlmod).prop('checked')); }); // Create CSS Update button var cssupdate = $('<button>Update CSS</button>'); cssupdate.css('float', 'left'); cssupdate.click(function () { var css = $('#horc-cssbox').val(); var stringified = JSON.stringify(css); // update current CSS style document.store.css.set(css); $('#horc-css').text(css); try { StyleFix.styleElement($('#horc-css')[0]); } catch (err) { // Prefix Free doesnt exist } // update css dev box $('#stringify').val(stringified); }); // Create Clear Storage button var clearstorage = $('<button>Clear Storage</button>'); clearstorage.css('float', 'right'); clearstorage.css('background-color', 'rgba(255, 0, 0, 0.4)'); clearstorage.click(function () { localStorage.clear(); window.location.reload(true); }); // Create CSS box var cssbox = $('<textarea id="horc-cssbox">'); cssbox.css('width', '100%'); cssbox.css('height', '10em'); cssbox.val(document.store.css.get()); $(document).on('keydown', '#horc-cssbox', function (e) { // Detect save if ((e.which == '115' || e.which == '83') && (e.ctrlKey || e.metaKey)) { e.preventDefault(); cssupdate.trigger('click'); } }); // Create Stringified box var stringbox = $('<textarea id="stringify">'); stringbox.attr('disabled', 'true'); stringbox.css('width', '100%'); stringbox.css('height', '8em'); stringbox.val(JSON.stringify(document.store.css.get())); // Append CSS text editors settingui.append($('<br>')); settingui.append($('<br>')); settingui.append(clearstorage); settingui.append(cssupdate); settingui.append($('<div>Current CSS</div>')); settingui.append(cssbox); settingui.append($('<br>')); settingui.append($('<br>')); settingui.append($('<div>Stringified CSS</div>')); settingui.append(stringbox); } // Create status bar function create_statusbar() { var statusbar = $('<div id="horc-statusbar"><div></div></div>').hide(); $('body').append(statusbar); } // Create Episode Drag & Drop UI function create_epdragui() { // Find panel var ep_droppanel = $('<div class="horc-ep-droppanel">').hide(); $('body').append(ep_droppanel); ep_droppanel.css('left', '10em'); ep_droppanel.css('top', '30em'); // Create circles var watchcircle = $('<div id="watchcircle" class="horc-circle droppable"><div>Watch</div></div>'); var dropcircle = $('<div id="dropcircle" class="horc-circle droppable"><div>Drop</div></div>'); var hidecircle = $('<div id="hidecircle" class="horc-circle droppable"><div>Hide</div></div>'); var clearcircle = $('<div id="clearcircle" class="horc-circle droppable"><div>Clear</div></div>'); ep_droppanel.append(watchcircle); ep_droppanel.append(dropcircle); ep_droppanel.append(hidecircle); ep_droppanel.append(clearcircle); } // Create Events function create_events() { // Make the program interactable $(document).on('set-active', function (e) { $('#horc-mainui').find('.horc-listhead, .horc-filter-episode, button, input').prop('disabled', false); }); $(document).on('clear-active', function (e) { $('#horc-mainui').find('.horc-listhead, .horc-filter-episode, button, input').prop('disabled', true) }); // Make the main UI disappear into a small dragged window // Show the position drop panels $(document).on('set-ui-droppanel', function (e) { $('.horc-ui-droppanel').fadeIn(100); $('.horc-posdrag-icon').show(); //$('.horc-ui-droppanel').find('.horc-circle') }); $(document).on('clear-ui-droppanel', function (e) { $('.horc-ui-droppanel').fadeOut(100); $('.horc-posdrag-icon').hide(); }); // Make a small box displaying an episode entry // Show the anime drop panels $(document).on('set-ep-droppanel', function (e) { var panel = $('.horc-ep-droppanel'); panel.fadeIn(100); $('.horc-episode-icon').show(); //panel.css('left', document.prop.mousex + 'px'); //panel.css('top', document.prop.mousey + 'px'); }); $(document).on('clear-ep-droppanel', function (e) { $('.horc-ep-droppanel').fadeOut(100); $('.horc-episode-icon').hide(); }); // Update the counters on anime lists $(document).on('update-animelist', function (e) { // Clear mainui anime list $('#horc-watchlist, #horc-droplist, #horc-hidelist').find('.horc-content').empty(); // Repopulate with the episode listing $('.horc-episode').map(function (i, element) { var jqe = $(element); console.warn('Repopulate Incomplete'); }); // Count number of animes in list var eps = $('.horc-episode-orig'); var watchcount = eps.filter('.watch').length; var dropcount = eps.filter('.drop').length; var hidecount = eps.filter('.hide').length; $('#horc-watchlist').find('.horc-listcounter').text(watchcount); $('#horc-droplist').find('.horc-listcounter').text(dropcount); $('#horc-hidelist').find('.horc-listcounter').text(hidecount); }); } this.MainUI(); }; MainUI.create_embed = function (jqe, contentstyles, draguistyles) { jqe.empty().addClass('horc-slot droppable'); var dragui = $('<div class="horc-ui-droppanel">').hide(); dragui.append($('<div class="horc-circle">')); jqe.append(dragui); var content = $('<div class="horc-content horc-embed">'); jqe.append(content); if (contentstyles) { for (var i = 0; i < contentstyles.length; i += 2) { content.css(contentstyles[i], contentstyles[i + 1]);; } } if (draguistyles) { for (var i = 0; i < draguistyles.length; i += 2) { dragui.css(draguistyles[i], draguistyles[i + 1]); } } } MainUI.create_sidebar = function (jqe, contentstyles, draguistyles) { jqe.empty().addClass('horc-slot droppable'); var dragui = $('<div class="horc-ui-droppanel">').hide(); dragui.append($('<div class="horc-circle">')); jqe.append(dragui); var content = $('<div class="horc-content horc-sidebar">'); jqe.append(content); if (contentstyles) { for (var i = 0; i < contentstyles.length; i += 2) { content.css(contentstyles[i], contentstyles[i + 1]);; } } if (draguistyles) { for (var i = 0; i < draguistyles.length; i += 2) { dragui.css(draguistyles[i], draguistyles[i + 1]); } } } // Storage Management // // This will create a new persistant variable // // Note: // Constructor[3] has type checking. // // Usage [2]: new Store(keyname, value_default) // Usage [3]: new Store(keyname, value_default, expected_type) var Store = function (keyname, value_default, type) { var thisstore = this; this.Store = function (keyname, value_default, type) { var value = localStorage.getItem(thisstore._keyname); if (value === null) { thisstore._cache = JSON.parse(thisstore._default); } else { value = JSON.parse(value); if (type && typeof value !== type) { console.warn('Loaded data for "' + keyname + '" is not of type "' + type + '"; Using default value'); thisstore._cache = JSON.parse(thisstore._default); } else { thisstore._cache = value; } } }; this.get = function () { return thisstore._cache; }; this.set = function (value) { if (type && typeof value !== type) { thisstore._cache = JSON.parse(thisstore._default); } thisstore._cache = value; localStorage.setItem(thisstore._keyname, JSON.stringify(value)); if (thisstore.onset) thisstore.onset(); }; this.rm = function () { localStorage.removeItem(thisstore._keyname); }; this._keyname = keyname; this._default = JSON.stringify(value_default); this._type = type; this._cache = null; this.onset = null; this.Store(keyname, value_default, type); }; // Store Updater // // This will force storage to clear for a specific key. // // Note: // You should force refresh the page, in case your other modules loaded 1st. // // Usage [2]: new StoreUpdater(keyname, new_version) var StoreUpdater = function (keyname, newversion) { // Initialize position for the UI if (!document.getElementById('horc-updater')) { var updater = document.createElement('div'); document.body.insertBefore(updater, document.body.childNodes[0]); updater.id = 'horc-updater'; updater.style.position = 'fixed'; updater.style.left = '0'; updater.style.top = '0'; updater.style.zIndex = '100'; } if (document.horc_updated === undefined) document.horc_updated = false; var moduleversion = localStorage.getItem(keyname + '.version'); if (moduleversion !== newversion) { // Site theme changed since last time; Delete key & prepare to reboot document.horc_updated = true; localStorage.removeItem(keyname); localStorage.setItem(keyname + '.version', newversion); localStorage.removeItem(keyname + '.updated'); return; } if (null === localStorage.getItem(keyname + '.updated')) { // Tell the user it updated localStorage.setItem(keyname + '.updated', 'true'); var container = document.createElement('div'); container.style.color = '#000000'; container.style.backgroundColor = '#eeffee'; container.style.padding = '.5em 2em'; container.style.margin = '.5em'; container.style.marginLeft = '1em'; container.style.width = '16em'; container.style.borderRadius = '0.5em'; container.style.boxShadow = '0 0 1em #000000'; var icon = document.createElement('img'); icon.src = 'https://bitbucket.org/horc/anime-lighter/raw/master/img/Logo.png'; icon.style.width = '1.2em' icon.style.verticalAlign = 'middle' var msg = ' <b>' + keyname + '</b> was reset'; var counter = document.createElement('span'); counter.style.cssFloat = 'right'; var desc = document.createElement('span'); desc.innerHTML = msg; desc.appendChild(counter); container.appendChild(icon); container.appendChild(desc); document.getElementById('horc-updater').appendChild(container); var time = 10000; var _updater = setInterval(function () { if (time <= 0) { clearInterval(_updater); container.remove(); } else { var t = (time / 100 | 0) / 10; if (t % 1 === 0) t = t + '.0'; counter.innerHTML = t; time -= 100; } }, 100); container.onclick = function () { clearInterval(_updater); container.remove(); } } } /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> // Mouse & Touch module // // Makes mouse & touch interaction consistent // This guarantees that for every "start" event, there is a matching "end" event // Assign the event handler to the correct functions of these // // If you need to test against left/middle/right click, negate the number before testing // Real touch events will use positive numbers: 0, 1, 2, + // Mouse events will use negative numbers, -1, -2, -3 // // Important functions: // .mousedown | Event proxies that you must pass to an object when binding // .mouseup | ^ // .mousemove | ^ // .touchstart | ^ // .touchend | ^ // .touchmove | ^ // // Important Variables: // .onstart | Callback for mouse or touch start event // .onend | Callback for matching end event // .ondragstart | Callback for when a drag is detected // .ondragend | Callback for matching end event // .onmove | Callback for matching move event // .onfirst | Callback for 1st start event // .onlast | Callback for last end event // // Constructor: // Constructor takes 3 functions, listed below // Make sure each of those are defined with parameters(identifiers, changedtouches, event) // // Usage[0]: new Touch() var Touch = function () { var thistouch = this; var touchsupport = 'ontouchstart' in window; this.Touch = function () { }; this.mousedown = function (e) { if (e.which < 1) return; if (thistouch.ismdown) return; thistouch.ismdown = true; var isfirst = !thistouch.dragging; thistouch.mousetouch = new VirtualTouchEvent(e, -e.which); thistouch.target = e.originalEvent.target; recheck_mouse(e); $(document).on('mouseup mousemove mouseenter mouseleave', mousemove_correction); var changes = e.originalEvent.changedTouches; // Trigger first if (thistouch.onfirst && isfirst) thistouch.onfirst([-e.which], changes, e); // Trigger mouse start if (thistouch.onstart) thistouch.onstart([-e.which], changes, e); e.done = true; // Notify mouse correction handler }; this.mousemove = function (e) { if (e.which < 1) return; if (!thistouch.ismdown) return; recheck_mouse(e); var changes = e.originalEvent.changedTouches; // Trigger dragging if (!isdragging(-e.which)) { setdragging(-e.which); if (thistouch.ondragstart) thistouch.ondragstart([-e.which], changes, e); } // Trigger moving if (thistouch.onmove) thistouch.onmove([-e.which], changes, e); e.done = true; // Notify mouse correction handler }; this.mouseup = function (e) { if (e.which < 1) return; if (!thistouch.ismdown) return; thistouch.ismdown = false; e.originalEvent.target = thistouch.target; recheck_mouse(e); var changes = e.originalEvent.changedTouches; // Trigger dragging if (isdragging(-e.which)) { cleardragging(-e.which); if (thistouch.ondragend) thistouch.ondragend([-e.which], e.originalEvent.changedTouches, e); } // Trigger mouse end if (thistouch.onend) thistouch.onend([-e.which], changes, e); // Trigger last if (thistouch.onlast) thistouch.onlast([-e.which], changes, e); thistouch.mousetouch = null; // Delete last mouse e.done = true; // Notify mouse correction handler }; this.touchstart = function (e) { normalize(e); var isfirst = !thistouch.istdown; thistouch.istdown = true; // For all changes var changes = e.originalEvent.changedTouches; var ids = $(changes).map(function (i, changed) { return changed.identifier; }); // Trigger first if (isfirst && thistouch.onfirst) thistouch.onfirst(ids, changes, e); // Trigger touch start if (thistouch.onstart) thistouch.onstart(ids, changes, e); e.done = true; // Notify mouse correction handler }; this.touchmove = function (e) { try { // Unknown error possible where the user taps once extremely fast [takes a lot of tries to replicate] normalize(e); } catch (err) { return; // Just skip this move event } // For all changes var changes = e.originalEvent.changedTouches; var ids = $(changes).map(function (i, changed) { return changed.identifier; }); var changes_start = []; var ids_start = []; $(changes).map(function (i, changed) { var id = changed.identifier; if (!isdragging(id)) { setdragging(id); changes_start.push(changed); ids_start.push(id); } }); // Trigger dragging if (ids_start.length && thistouch.ondragstart) thistouch.ondragstart(ids_start, changes_start, e); // Trigger moving if (thistouch.onmove) thistouch.onmove(ids, changes, e); e.done = true; // Notify mouse correction handler }; this.touchend = function (e) { try { // Unknown error possible where the user taps once extremely fast [takes a lot of tries to replicate] normalize(e); } catch (err) { console.error('Error: Unknown issue where user performs 2 finger right click.\nThat breaks: normalize::importchanges::changes.push'); // Allow cleanup to happen, but alert developer console.warn('Attempting to recover from error') } // For all changes var changes = e.originalEvent.changedTouches; var ids = $(changes).map(function (i, changed) { return changed.identifier; }); var changes_end = []; var ids_end = []; $(changes).map(function (i, changed) { var id = changed.identifier; if (isdragging(id)) { cleardragging(id); changes_end.push(changed); ids_end.push(id); } }); // Trigger dragging if (ids_end.length && thistouch.ondragend) thistouch.ondragend(ids_end, changes_end, e); // Trigger touch end if (thistouch.onend) thistouch.onend(ids, changes, e); // Trigger last if (!e.originalEvent.touches.length) thistouch.istdown = false; var islast = !thistouch.istdown; if (thistouch.onlast && islast) thistouch.onlast(ids, changes, e); e.done = true; // Notify mouse correction handler }; // Unified handler for all this.onstart = null; this.onend = null; this.ondragstart = null; this.ondragend = null; this.onmove = null; this.onfirst = null; this.onlast = null; // Check Dragging State // // Used to keep track of whats being dragged // // Usage [1]: setdragging(id) function isdragging(id) { var shift = (0x1 << (id + 3)); return !!(thistouch.dragging & shift); } // Set Dragging State // // Used to keep track of whats being dragged // // Usage [1]: setdragging(id) // Usage [2]: setdragging(ids) function setdragging(ids) { if (ids.length === undefined) { var shift = (0x1 << (ids + 3)); thistouch.dragging |= shift; } else { $(ids).map(function (i, id) { setdragging(id); }); } } // Clear Dragging State // // Used to keep track of whats being dragged // // Usage [1]: cleardragging(id) // Usage [2]: cleardragging(ids) function cleardragging(ids) { if (ids.length === undefined) { var shift = (0x1 << (ids + 3)); thistouch.dragging &= ~shift; } else { $(ids).map(function (i, id) { cleardragging(id); }); } } // Normalize // // Combines .which, .client, .changedTouches, .touches, & .targetnow // // Usage [1]: normalize(event) function normalize(e) { if (e.done) return; if (e.originalEvent.changedTouches === undefined) e.originalEvent.changedTouches = []; if (e.originalEvent.touches === undefined) e.originalEvent.touches = []; for (var i = 0; i < e.originalEvent.changedTouches.length; i++) { var changed = e.originalEvent.changedTouches[i]; changed.target = thistouch.target; changed.targetnow = gettargetnow(e); } var changed = e.originalEvent.changedTouches; if (e.which && changed.length) { delete changed.target; // a bug requires original target to be deleted before reassignment changed.target = thistouch.target; } var changes = e.originalEvent.changedTouches; if (thistouch.mousetouch) changes.push(thistouch.mousetouch); var touches = e.originalEvent.touches; if (thistouch.mousetouch) touches.push(thistouch.mousetouch); } // Recheck Mouse // // For mouse events, this ensures consistency between mouse & touch. // // Note: // Only for mouse events; Don't call this from a touch event. // // Usage [1]: recheck_mouse(event) function recheck_mouse(e) { normalize(e); if (thistouch.mousetouch) { thistouch.mousetouch.copyfrom(e); } if (e.which !== thistouch.lastmouse) { if (thistouch.lastmouse) { var nowwhich = e.which; e.which = thistouch.lastmouse; thistouch.mouseup(e); e.which = nowwhich; } thistouch.lastmouse = e.which; } } // Mouse Move Correction // // Bound when a mousedown happens // This ensures that a mouse up will happen // // Note: // Only for mouse events; Don't call this from a touch event. // // Usage [1]: recheck_mouse(event) function mousemove_correction(e) { if (e.done) return; recheck_mouse(e); if (!thistouch.mousetouch) { $(document).off('mouseup mousemove mouseenter mouseleave', mousemove_correction); thistouch.lastmouse = 0; thistouch.mouseup(e); } else { thistouch.mousemove(e); } } // Get Cursor Position // // Note: // Requires at least 1 touch or mouse point to exist. // // Usage [1]: getcursor(event) function getcursor(e) { var changed = e.originalEvent.changedTouches; if (changed && 0 < changed.length) { var touch = e.originalEvent.changedTouches[0]; return [touch.clientX, touch.clientY]; } else if (e.clientX !== undefined) { return [e.clientX, e.clientY]; } return null; } // Get Target Now // // Note: // Requires at least 1 touch or mouse point to exist. // // Usage [1]: gettargetnow(event) function gettargetnow(e) { var pos = getcursor(e); if (pos) return document.elementFromPoint(pos[0], pos[1]); return null; } // Virtual Touch Event // // Note: // Constructor Takes the mouse event & which mouse. // It has a copyfrom() method to copy new XY values. // copyfrom() will not override the id or element target. var VirtualTouchEvent = function (e, id) { this.clientX = e.originalEvent.clientX; this.clientY = e.originalEvent.clientY; this.force = 0; // Pressure this.identifier = id; // ID of this touch event this.pageX = e.originalEvent.pageX; this.pageY = e.originalEvent.pageY; this.radiusX = 25; // Default to 25 for mouse this.radiusY = 25; // ^ this.screenX = e.originalEvent.screenX; this.screenY = e.originalEvent.screenY; this.target = e.originalEvent.target; // Element that triggered the event this.targetnow = gettargetnow(e); // Element the mouse is focused on this.copyfrom = function (e) { this.clientX = e.originalEvent.clientX; this.clientY = e.originalEvent.clientY; this.pageX = e.originalEvent.pageX; this.pageY = e.originalEvent.pageY; this.screenX = e.originalEvent.screenX; this.screenY = e.originalEvent.screenY; this.targetnow = gettargetnow(e); // Element the mouse is focused on }; } this.mousetouch = null; this.dragging = 0x0; // dragging flag this.target = null; this.lastmouse = 0; this.ismdown = false; this.istdown = false; this.Touch(); }; /// Dependencies: /// <reference path="http://code.jquery.com/jquery-2.1.3.js" /> /// <reference path="https://code.jquery.com/ui/1.11.4/jquery-ui.js" /> // Get URL Search +1 overloads // // This will read a URL for the search query // // Examples of accepted URLs: // http://horc.bitbucket.org/ // http://horc.bitbucket.org? // http://horc.bitbucket.org/?path=projects // ?path=projects/test/ // path=projects/test& // // Return: // Object with the key & string values // // Usage [0]: geturlsearch() // Usage [1]: geturlsearch(string_url) function geturlsearch(url) { if (typeof url !== 'string') { if (url === undefined) url = location.search; } function cleanurlsearch(url) { console.warn('\t' + url); if (-1 < url.search(/:\/\//)) { // This is a url var p = url.search(/\?/); if (p === -1) url = ''; else url = url.substr(p + 1); } if (url.substr(0, 1) === '?') url = url.substr(1); return url; } url = cleanurlsearch(url); var params = {}; var split = url.split('&'); for (var i = 0; i < split.length; i++) { var u = split[i]; if (u.length) { var p = u.search('='); if (-1 < p) { var lhs = u.substr(0, p); var rhs = u.substr(p + 1); if (!lhs.length) continue; params[lhs] = rhs; } else { var lhs = u; params[lhs] = ''; } } } return params; } // Load Script +3 overloads // // This will sequentially load 1 script at a time. // Run multiple times to load in parallel // // Usage [1]: loadscript(string_url) // Usage [2]: loadscript(string_url, oncomplete_function) // Usage [1]: loadscript(array_of_urls) // Usage [2]: loadscript(array_of_urls, oncomplete_function) function loadscript(urls, oncomplete) { if (typeof urls === 'object') { if (!urls.length) { if (oncomplete) return oncomplete(); return; } loadscript(urls[0], function () { loadscript(urls.slice(1), oncomplete); }); } else if (typeof urls === 'string') { var url = urls; var scripts = document.getElementsByTagName('script'); for (var i = 0; i < scripts.length; ++i) { if (scripts[i].src === url) { console.log(' Script already loaded:\n' + url); if (oncomplete) oncomplete(); return; } } var e = document.createElement('script'); e.src = url; e.onload = function () { console.log(' Injected script:\n' + url); if (oncomplete) oncomplete(); } e.onerror = function () { e.remove(); console.warn(' Failed to injected script:\n' + url); if (oncomplete) oncomplete(); } document.head.appendChild(e); } else { } } // Load Style +3 overloads // // This will sequentially load 1 style at a time. // Run multiple times to load in parallel // // Usage [1]: loadstyle(string_url) // Usage [2]: loadstyle(string_url, oncomplete_function) // Usage [1]: loadstyle(array_of_urls) // Usage [2]: loadstyle(array_of_urls, oncomplete_function) function loadstyle(urls, oncomplete) { if (typeof urls === 'object') { if (!urls.length) { if (oncomplete) return oncomplete(); return; } loadstyle(urls[0], function () { loadstyle(urls.slice(1), oncomplete); }); } else if (typeof urls === 'string') { var url = urls; var links = document.getElementsByTagName('link'); for (var i = 0; i < links.length; ++i) { if (links[i].href === url) { console.log(' Style already loaded:\n' + url); if (oncomplete) oncomplete(); return; } } var styles = document.getElementsByTagName('style'); for (var i = 0; i < styles.length; ++i) { if (styles[i].attributes && (url === styles[i].attributes.src.value)) { console.log(' Style already loaded:\n' + url); if (oncomplete) oncomplete(); return; } } var e = document.createElement('link'); e.rel = 'stylesheet'; e.type = 'text/css'; e.href = url; e.onload = function () { console.log(' Injected style:\n' + url); if (oncomplete) oncomplete(); } e.onerror = function () { e.remove(); console.warn(' Failed to injected style:\n' + url); if (oncomplete) oncomplete(); } document.head.appendChild(e); } else { } } function recheck_prefixfree() { var sleeptime = 200; var maxtime = 10000; var addtime = Date.now(); function helper() { var realtimemax = maxtime + addtime; if (realtimemax < Date.now()) return; var time = Date.now(); setTimeout(function () { try { StyleFix.link; // test for Prefix Free var links = document.getElementsByTagName('link'); if (links.length) { addtime = Date.now(); for (var i = 0; i < links.length; ++i) { StyleFix.link(links[i]); } } } catch (err) { } helper(); }, sleeptime); } helper(); } function benchmark(f) { s = Date.now(); for (var i = 0; i < 1000000; ++i) f(); s = Date.now() - s; return s / 1000; }