Greasy Fork is available in English.

DMZJ Subscriber 动漫之家订阅助手

提供动漫之家漫画的订阅批量导入导出功能

// ==UserScript==
// @name          DMZJ Subscriber 动漫之家订阅助手
// @namespace     https://github.com/DriverHub/dmzj-assistant
// @version       0.1.3
// @description   提供动漫之家漫画的订阅批量导入导出功能
// @match         http://i.dmzj.com/*
// @match         http://manhua.dmzj.com/*
// @copyright     2016, DriverHub
// @author        UnluckyNinja, pa001024

// @grant         unsafeWindow
// @grant         GM_xmlhttpRequest
// @connect       self
// @connect       user.dmzj.com
// @connect       s.acg.178.com
// ==/UserScript==

// userscript for https://chrome.google.com/webstore/detail/dhdgffkkebhmkfjojejmpbldmpobfkfo
(function($, window) {
    // loadCSS start
    var styleRes = function() {
        /*
            #da_import_button, .dy_content_li{cursor: pointer;}
            .da_button{width: 400px; display:block; float:right;}
            div.popup {
                padding: 6px;
                position: relative;
                margin-top: 5px;
                left: calc(-50px);
                display: block;
                background-color: white;
                color: black;
                width: 200px;
                height: 100px;
                border: 1px solid #e6e6e6;
            }
            div.popup.import {
                left: -120px;
                height: 135px;
            }
            div.popup.import i.popup {
                right: 33px;
            }
            textarea.popup {
                display: block;
                width: 100%;
                height: 100px;
                color: black;
                font-size: small;
                overflow: hidden;
            }
            i.popup {
                position: absolute;
                display: inline-block;
                width: initial;
                height: initial;
                margin: 0;
                border: 10px solid;
                top: -20px;
                right: calc(100px - 10px);
                border-color: transparent;
                border-bottom-color: #e6e6e6;
            }
            #da_import_button {
                margin-right: 6px;
                padding-left: 4px;
                float: right;
                background: url(img/border.gif) no-repeat scroll -283px -46px;
                color: #fd3600;
                height: 20px;
                margin: 0 1px 0 0;
                padding: 3px 0 0 3px;
            }
            #import_button {
                position:relative;
                width: 50px;
                height: 30px;
                margin: 5px 0 0 0;
                float: left;
                left: calc(50% - 25px);
                font-size: small;
            }
            
        */
    };
    var myCSS = $("#ad_style");
    if (!myCSS.length)
        myCSS = $("<style id='ad_style'>").insertAfter($(document.body).find(":last"));
    var stylestr = styleRes.toString().replace(/^[\s\S]*?\/\*\s*|\s*\*\/[\s\S]*?$/g, "");
    myCSS.html(stylestr);
    // loadCSS end

    window.DmzjAssistant = {
        version: '0.1.3',
        auto_expand_enable: true,
        teleport_enable: false,
        choosing_enable: false,
        is_loading: false,
        cur_page: 1,
        uid: 0,
        over_subscribed: false,
        itemOnClick: function(event) {
            var item = $(event.currentTarget);
            item.find('div.dy_img').toggleClass("comic_chosen");
            item.toggleClass("on_choosing");
            item.hasClass('on_choosing') ? item.fadeTo('fast', 1) : item.fadeTo('fast', 0.3);
        },

        hideNonChosenItem: function(event) {
            var all_item = $('div.dy_content_li');
            all_item.not('.on_choosing').fadeTo('slow', 0.3);
        },

        subscribeAll: function(obj, callback) {
            this.over_subscribed = false;
            var self = this;
            if (!self.uid) {
                return;
            }
            console.log('dmzj助手 - 开始批量订阅漫画');
            var count = Object.keys(obj).length;
            var success_count = 0;
            var fail_count = 0;
            var finished = 0;
            var failed = {};
            $.each(obj, function(comic_id, v) {
                self.subscribe(self.uid, comic_id, v, function(comic_id, subscribe_status) {
                    finished += 1;
                    callback(comic_id, subscribe_status);
                    if (subscribe_status === 0) {
                        fail_count += 1;
                        failed[comic_id] = v;
                        console.log(`dmzj助手 - 漫画 ${v.name} 订阅失败 (${success_count}:${fail_count}/${finished})`);
                    }
                    else if (subscribe_status === 1) {
                        success_count += 1;
                        console.log(`dmzj助手 - 漫画 ${v.name} 订阅成功 (${success_count}:${fail_count}/${finished})`);
                    }
                    else if (subscribe_status === 2) {
                        fail_count += 1;
                        failed[comic_id] = v;
                        if (!self.over_subscribed) {
                            self.over_subscribed = true;
                            console.log(`dmzj助手 - 漫画 ${v.name} 无法订阅,超出订阅数量限制 (${success_count}:${fail_count}/${finished})`);
                        }
                    }
                    else if (subscribe_status === 3) {
                        success_count += 1;
                        console.log(`dmzj助手 - 漫画 ${v.name} 已经订阅 (${success_count}:${fail_count}/${finished})`);
                    }
                    if (finished == count) {
                        var text = `dmzj助手 - 批量订阅完成,${success_count} 成功 ${fail_count} 失败`;
                        if (Object.keys(failed).length == 0) {
                            alert(text);
                        }
                        else {
                            alert(text+'\n订阅失败的部分请在浏览器控制台窗口复制,重新尝试批量订阅');
                            console.log(JSON.stringify(failed));
                        }
                    }
                    else {
                        console.log(`dmzj助手 - 订阅进度 ${success_count}:${fail_count} / No. ${finished} / Total ${count}`);
                    }
                });
            });
        },

        subscribe: function(uid, comic_id, value, callback) {
            if (this.over_subscribed) {
                callback(comic_id, 2);
                return;
            }
            var self = this;
            var proxy_url = "http://s.acg.178.com/sns/comic_test.php?jsonpcallback=?";
            console.log('dmzj助手 - 正在订阅漫画 ' + value.name);
            // 避免跨域错误
            GM_xmlhttpRequest({
                url: 'http://user.dmzj.com/subscribe/sure/' + uid + '/mh/' + comic_id,
                method: 'GET',
                headers: {
                    'Referer': location.href
                },
                // overrideMimeType: 'text/plain',
                onerror: function(response) {
                    self.over_subscribed = true;
                    callback(comic_id, 0);
                    console.log('dmzj助手 - 请求异常,终止订阅');
                },
                onload: function(response) {
                    // 订阅查询
                    var string = response.responseText;
                    var match = string.match(/\((.)\)/);
                    if (match && match.length == 2 && match[1] == 1) {
                        callback(comic_id, 3);
                        return;
                    }
                    else if (!match || match.length != 2) {
                        console.log('dmzj助手 - 订阅漫画' + value.name + '时服务器返回数据异常');
                        callback(comic_id, 0);
                        return;
                    }
                    var dataobj = {
                        'operation': 'subscribe',
                        'comic_id': comic_id,
                        'uid': uid
                    };
                    // 真正订阅,jsonp
                    $.getJSON(proxy_url, dataobj, function(jsondata) {
                        callback(comic_id, jsondata.subscribe_status);
                    }).fail(function(jsondata) {
                        self.over_subscribed = true;
                        console.log('dmzj助手 - 请求异常,终止订阅');
                        callback(comic_id, 0);
                    });
                }
            });
        },

        subscribeFetch: function(data, callback, fail) {
            var url = "/ajax/my/subscribe";
            /* data 格式
            var data = {
                page: 2,
                type_id: "2", //1国漫2日漫3动画4轻小说,页面标签
                letter_id: "0", //首字母,0 ALL
                read_id: "1" //1全部更新,2未读更新
            };
            */
            $.post(url, data, function(text, status, jqxhr) {
                callback(text);
                console.log('dmzj助手 - 成功获取第' + data.page + '页订阅列表');
            }).fail(fail);
        },

        subscribeLoad: function(event) {
            var self = this;
            var pagefoot = $('#page_id');
            var should_load = function() {
                return self.isElementInViewport(pagefoot);
            };
            // 避免重复加载
            if (this.auto_expand_enable && should_load() && !self.is_loading) {
                self.is_loading = true;
                var type_id = $('.optioned').attr('value');
                var letter_id = $('.cur').attr('value');
                var read_id = $('.oped').attr('value');
                var data = {
                    'page': self.cur_page + 1,
                    'type_id': type_id,
                    'letter_id': letter_id,
                    'read_id': read_id
                };

                self.subscribeFetch(data, function(text) {
                    //初始检测
                    var target = $('#my_subscribe_id_' + self.cur_page);
                    if (target.length < 1) {
                        target = $('#my_subscribe_id');
                    }
                    // 添加内容
                    var newList = $(text);
                    self.cur_page += 1;
                    var container = $(`<div class="dy_content autoHeight" id="my_subscribe_id_${self.cur_page}" style="border-top-width: 1px; border-top-style: solid; border-top-color: rgb(230, 230, 230);"></div>`);
                    container.insertAfter(target).append(newList);
                    $('#my_subscribe_id_' + self.cur_page).append(newList);

                    // 动画效果与事件响应
                    if (self.choosing_enable) {
                        self.hideNonChosenItem();
                        $('#my_subscribe_id_' + self.cur_page + ' div.dy_content_li').click(function(event) {
                            self.itemOnClick(event);
                        });
                    }
                    else {
                        $('#my_subscribe_id_' + self.cur_page + ' div.dy_content_li').click(function(event) {
                            self.choosing_enable = true;
                            var item = $(event.currentTarget);
                            item.find('div.dy_img').toggleClass("comic_chosen");
                            item.addClass('on_choosing');

                            self.hideNonChosenItem(event);

                            $('div.dy_content_li').off('click').click(function(event) {
                                self.itemOnClick(event);
                            });
                        });
                    }
                    self.is_loading = false;
                }, function() {
                    self.is_loading = false;
                });

            }

        },
        // source: http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
        isElementInViewport: function(element) {

            //special bonus for those using jQuery
            if (typeof jQuery === "function" && element instanceof jQuery) {
                element = element[0];
            }

            var rect = element.getBoundingClientRect();

            return (
                rect.top >= 0 &&
                rect.left >= 0 &&
                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
                rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
            );
        },

        exportSubscribe: function() {
            var self = this;
            var result = {};
            if (self.choosing_enable) {
                $('.on_choosing .dy_r').each(function(index, element) {

                    var info = $(element).find('h3 a');
                    var name = info.text();
                    var url = info.attr('href').trim();
                    var id = $(element).find('a.qx').attr('value');
                    result[id] = {
                        'name': name,
                        'url': url
                    };
                });
            }
            return JSON.stringify(result);
        },

        importSubscribe: function(text) {
            var self = this;
            var obj = {};
            try {
                obj = JSON.parse(text);
                var all_clear = true;
                $.each(obj, function(k, v) {
                    if(k.match(/^\d+$/).length < 1){
                        all_clear = false;
                    }
                });
                if(!all_clear || Object.keys(obj).length == 0){
                    alert("JSON数据有误,请检查分享代码格式!");
                    return;
                }
            }
            catch (e) {
                console.log(e);
                alert("解析JSON发生异常,请检查分享代码格式!");
            }
            self.subscribeAll(obj, function(id, jsondata) {
                    // nothing to do
            });
        },
        popupTextArea: function(element, content, select) {
            var popup = $(`<div class="popup"><i class="popup"></i><textarea id="popup_text" class="popup">${content}</textarea></div>`)
                .insertAfter(element);
            if (select) {
                popup.find('textarea').focus().select();
            }
            return popup;
        }
    };

    var _da = window._da = window.DmzjAssistant;

    function enable_subscribe() {
        // 好像然并卵
        /*$('div.dy_content_li').on("load change", function() {
            if (_da.choosing_enable) {
                _da.hideNonChosenItem();
            }
        });*/
        // 触发自动续屏,一颗赛艇
        $(window).scroll(function(event) {
            _da.subscribeLoad(event);
        });

        // 添加一键导出按钮
        var origin = $('.sub_potion').css('display', 'inline-block');
        var new_tab = $('<ul class="sub_potion da_button">' +
            '<li><a href="javascript:;" id="clear_choose" class="sub_potion_li sub_type_id" value="1">清除选择</a></li>' +
            '<li><a href="javascript:;" id="expand_off" class="sub_potion_li sub_type_id" value="1">关闭滚动</a></li>' +
            '<li><a href="javascript:;" id="da_export" class="sub_potion_li sub_type_id" value="1">导出订阅</a></li>' +
            '<li><a href="javascript:;" id="da_import" class="sub_potion_li sub_type_id" value="1">批量订阅</a></li>' +
            '</ul>').insertAfter(origin);
        new_tab.find('#clear_choose').click(function(event) {
            if (_da.choosing_enable) {
                _da.choosing_enable = false;
                $('.on_choosing').removeClass('on_choosing');
                // the same as above one
                $('div.dy_content_li').off('click').click(function(event) {
                    _da.choosing_enable = true;
                    var item = $(event.currentTarget);
                    item.find('div.dy_img').toggleClass("comic_chosen");
                    item.addClass('on_choosing');

                    _da.hideNonChosenItem(event);

                    $('div.dy_content_li').off('click').click(function(event) {
                        _da.itemOnClick(event);
                    });
                });
                $('div.dy_content_li').fadeTo('slow', 1.0);
            }
        });
        new_tab.find('#expand_off').click(function(event) {
            if (_da.auto_expand_enable) {
                _da.auto_expand_enable = false;
                $(event.target).text('开启滚动');
            }
            else {
                _da.auto_expand_enable = true;
                $(event.target).text('关闭滚动');
            }
        });
        // 导出按钮
        new_tab.find('#da_export').click(function(event) {
            if (_da.choosing_enable) {
                var popup = _da.popupTextArea(event.currentTarget, _da.exportSubscribe(), true);
                popup.find('textarea').attr('readonly', true);
                popup.focusout(function(event) {
                    $(event.currentTarget).remove();
                });
            }
            else {
                alert('请先选择漫画!');
            }
        });
        // 导入按钮
        new_tab.find('#da_import').click(function(event) {
            if($('div.popup.import').length > 0 ){
                return;
            }
            var popup = _da.popupTextArea(event.currentTarget, '{}', false);
            popup.addClass('import');
            popup.mouseleave(function(event) {
                $('div.popup.import').remove();
            });
            $('<button id="import_button">提交</button>').appendTo(popup).click(function(event) {
                _da.importSubscribe($('#popup_text').val());
                popup.remove();
            });
        });
        // 首次点击隐藏其余元素
        $('.dy_content_li').ready(function(event) {
            $('.dy_content_li').click(function(event) {
                _da.choosing_enable = true;
                var item = $(event.currentTarget);
                item.find('.dy_img').toggleClass("comic_chosen");
                item.addClass('on_choosing');

                _da.hideNonChosenItem(event);

                $('.dy_content_li').off('click').click(function(event) {
                    _da.itemOnClick(event);
                });
            });
        });
    }

    function popupText(name, content) {
        var base = "data:text/html;charset=utf-8,";
        return open(base + content, name, 'height=300,width=1000');
    }

    function enable_import() {
        var button = $('<li class="history_tab s-nav3" id="da_import_button" target_div="subscribe_panel">' +
            '<span id="subscribe_tab">订阅导入</span></li>');
        $('li[target_div]').eq(0).after(button);
        button.click(function(event) {
            // _da.importOnClick(event); // 删掉了
        });
    }

    function enable_import2() {
        var button = $('<span class="update"><a href="javascript:;">订阅导入</a></span>');
        $('.anim_title_text').eq(0).after(button);
        button.click(function(event) {
            // _da.importOnClick(event);
        });
    }
    /*global Cookies*/

    function dmzj_init() {
        console.log('dmzj助手 - 正在启动');
        console.log('dmzj助手 - 判断用户登陆情况');

        // get UID
        if (window.userId) {
            var uid = window.userId;
        }
        else {
            var datas = Cookies.get('my');
            if (datas != null && datas != '') {
                var t = datas.split('|');
                uid = t[0];
            }
            else {
                uid = 0;
            }
        }

        if (uid) {
            console.log('dmzj助手 - 用户已登录,UID为' + uid);
        }
        else {
            console.log('dmzj助手 - 用户未登录,请先登录');
        }

        if (uid) {
            _da.uid = uid;
            /* //before 4/28 OLD
            var mh_found = $('#mh');
            var mh_clicked = $('#mh2');
            if (mh_found.length > 0 || mh_clicked.length > 0) {
                enable_subscribe();
            }*/
            var mh_found = $('#yc1.optioned');
            if (mh_found.length > 0) {
                enable_subscribe();
            }
            else {
                /*
                if (location.hostname == 'manhua.dmzj.com') {
                    if (location.pathname == '/') {
                        // enable_import();
                    }
                    else {
                        // enable_import2();
                    }
                }
                // enable_teleport();*/
            }
            console.log('dmzj助手 - 启动完成');
            console.log('dmzj助手 - v' + _da.version + ' repo: https://github.com/DriverHub/dmzj-assistant author: UnluckyNinja, pa001024');
        }
    }

    /*! js-cookie v2.1.0 | MIT */
    ! function(a) {
        if ("function" == typeof define && define.amd) define(a);
        else if ("object" == typeof exports) module.exports = a();
        else {
            var b = window.Cookies,
                c = window.Cookies = a();
            c.noConflict = function() {
                return window.Cookies = b, c
            }
        }
    }(function() {
        function a() {
            for (var a = 0, b = {}; a < arguments.length; a++) {
                var c = arguments[a];
                for (var d in c) b[d] = c[d]
            }
            return b
        }

        function b(c) {
            function d(b, e, f) {
                var g;
                if (arguments.length > 1) {
                    if (f = a({
                            path: "/"
                        }, d.defaults, f), "number" == typeof f.expires) {
                        var h = new Date;
                        h.setMilliseconds(h.getMilliseconds() + 864e5 * f.expires), f.expires = h
                    }
                    try {
                        g = JSON.stringify(e), /^[\{\[]/.test(g) && (e = g)
                    }
                    catch (i) {}
                    return e = c.write ? c.write(e, b) : encodeURIComponent(String(e)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent), b = encodeURIComponent(String(b)), b = b.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent), b = b.replace(/[\(\)]/g, escape), document.cookie = [b, "=", e, f.expires && "; expires=" + f.expires.toUTCString(), f.path && "; path=" + f.path, f.domain && "; domain=" + f.domain, f.secure ? "; secure" : ""].join("")
                }
                b || (g = {});
                for (var j = document.cookie ? document.cookie.split("; ") : [], k = /(%[0-9A-Z]{2})+/g, l = 0; l < j.length; l++) {
                    var m = j[l].split("="),
                        n = m[0].replace(k, decodeURIComponent),
                        o = m.slice(1).join("=");
                    '"' === o.charAt(0) && (o = o.slice(1, -1));
                    try {
                        if (o = c.read ? c.read(o, n) : c(o, n) || o.replace(k, decodeURIComponent), this.json) try {
                            o = JSON.parse(o)
                        }
                        catch (i) {}
                        if (b === n) {
                            g = o;
                            break
                        }
                        b || (g[n] = o)
                    }
                    catch (i) {}
                }
                return g
            }
            return d.get = d.set = d, d.getJSON = function() {
                return d.apply({
                    json: !0
                }, [].slice.call(arguments))
            }, d.defaults = {}, d.remove = function(b, c) {
                d(b, "", a(c, {
                    expires: -1
                }))
            }, d.withConverter = b, d
        }
        return b(function() {})
    });


    $('body').ready(dmzj_init);

}).call(unsafeWindow || window, (unsafeWindow || window).$, unsafeWindow || window);