//# sourceURL=reader-base-3.2.0.js

(function (W, D, Q, M) {//window document jQuery SWR
    /* 'use strict';*/
    var ajax = Q.ajax, Config = M.Config;

    var nop = Q.noop, R = {
        Supports: {},
        Context: {
            window: W, document: D,
            instance: {}, active: null,
            listener: Q.Callbacks('memory unique') //listene reader new event
        }
    };

    R.Logger = (function console() {
        if (!W.console) {
            W.console = {};
        }
        var console = W.console;

        var names = ['log', 'debug', 'info', 'warn', 'error'], caches = {};
        Q.each(names, function (_, name) {
            if (!Q.isFunction(console[name])) {
                console[name] = nop;
                caches[name] = nop;
            } else {
                var fun = console[name];
                console[name] = fun;
                caches[name] = fun;
            }
        });
        console.setLevel = function (level) {
            var lv = level.toLowerCase();
            if (lv === 'off') {
                names.forEach(function (name) {
                    console[name] = nop;
                });
            } else {
                lv = (lv === 'log') ? 'debug' : lv;
                var index = names.indexOf(lv);
                if (index !== -1) {
                    names.forEach(function (name, i) {
                        console[name] = i < index ? nop : caches[name];
                    });
                }
            }
        };

        return console;
    })();

    R.Logger.info('Base preparing...');

    (function polyfill() {
        if (!String.prototype.startWith) {
            String.prototype.startWith = function (v) {
                if (!v || v.length === 0) {
                    return false;
                }
                return this.slice(0, v.length) === v;
            };
        }
        if (!String.prototype.firstUpperCase) {
            String.prototype.firstUpperCase = function () {
                if (this.length === 0) {
                    return this;
                }
                return this.charAt(0).toUpperCase() + this.slice(1);
            };
        }
        if (!String.prototype.firstLowerCase) {
            String.prototype.firstLowerCase = function () {
                if (this.length === 0) {
                    return this;
                }
                return this.charAt(0).toLowerCase() + this.slice(1);
            };
        }
        // First, checks if it isn't implemented yet.
        if (!String.prototype.format) {
            String.prototype.format = function () {
                var args = arguments;
                return this.replace(/{(\d+)}/g, function (match, number) {
                    var a = args[number];
                    if (typeof a === 'function') {
                        a = a.call(this);
                    }
                    return typeof a !== 'undefined' ? a : match;
                });
            };
        }

        //ES6 22.1.3.6 Array.prototype.fill (value [ , start [ , end ] ] )
        //From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
        if (!Array.prototype.fill) {
            Array.prototype.fill = function (value/*, start, end*/) {
                var start = arguments[1],
                    end = arguments[2];

                var o = Object(this);
                var len = o.length >>> 0;
                var relativeStart = parseInt(start, 10) || 0;
                var k;
                if (relativeStart < 0)
                    k = Math.max((len + relativeStart), 0);
                else
                    k = Math.min(relativeStart, len);
                var relativeEnd;
                if (end === undefined)
                    relativeEnd = len;
                else
                    relativeEnd = parseInt(end, 10) || 0;
                var final;
                if (relativeEnd < 0)
                    final = Math.max((len + relativeEnd), 0);
                else
                    final = Math.min(relativeEnd, len);
                while (k < final) {
                    var pk = String(k);
                    o[pk] = value;
                    k += 1;
                }
                return o;
            };
        }

        if (!Array.prototype.closest) {
            Array.prototype.closest = function (value) {
                var t = Object(this);
                var len = t.length >>> 0;
                if (value <= t[0]) return t[0];
                if (value >= t[len - 1]) return t[len - 1];

                var binarySearch = function (array, key) {//from java Arrays.binarySearch
                    var low = 0;
                    var high = array.length - 1;

                    while (low <= high) {
                        var mid = (low + high) >>> 1;
                        var midVal = array[mid];

                        if (midVal < key)
                            low = mid + 1;
                        else if (midVal > key)
                            high = mid - 1;
                        else
                            return mid; // key found
                    }
                    return -(low + 1);  // key not found.
                };
                var i = binarySearch(t, value);
                if (i >= 0) {
                    return t[i];
                }
                return t[Math.abs(i) - 1];
            };
        }

        //https://gist.github.com/eligrey/384583
        /*if (!Object.prototype.watch) {
            Object.defineProperty(Object.prototype, 'watch', {
                enumerable : false, configurable : true, writable : false,
                value : function (prop, handler) {
                    var oldval = this[ prop ], newval = oldval,
                        getter = function () {
                            return newval;
                        },
                        setter = function (val) {
                            oldval = newval;
                            return newval = handler.call(this, prop, oldval, val);
                        };
                    if (delete this[ prop ]) { // can't watch constants
                        Object.defineProperty(this, prop, {
                            get : getter, set : setter,
                            enumerable : true, configurable : true
                        });
                    }
                }
            });
        }*/

        // object.unwatch
        /* if (!Object.prototype.unwatch) {
             Object.defineProperty(Object.prototype, 'unwatch', {
                 enumerable: false, configurable: true, writable: false,
                 value: function (prop) {
                     var val = this[prop];
                     delete this[prop]; // remove accessors
                     this[prop] = val;
                 }
             });
         }*/
        // Production steps of ECMA-262, Edition 5, 15.4.4.21
        // Reference: http://es5.github.io/#x15.4.4.21
        // https://tc39.github.io/ecma262/#sec-array.prototype.reduce
        if (!Array.prototype.reduce) {
            Array.prototype.reduce = function (callback /*, initialValue*/) {
                if (this === null) {
                    throw new TypeError('Array.prototype.reduce ' +
                        'called on null or undefined');
                }
                if (typeof callback !== 'function') {
                    throw new TypeError(callback +
                        ' is not a function');
                }
                var o = Object(this);
                var len = o.length >>> 0;
                var k = 0;
                var value;
                if (arguments.length >= 2) {
                    value = arguments[1];
                } else {
                    while (k < len && !(k in o)) {
                        k++;
                    }
                    if (k >= len) {
                        throw new TypeError('Reduce of empty array ' +
                            'with no initial value');
                    }
                    value = o[k++];
                }
                while (k < len) {
                    if (k in o) {
                        value = callback(value, o[k], k, o);
                    }
                    k++;
                }
                return value;
            }
        }
        if (!Array.prototype.filter) {
            Array.prototype.filter = function (fun /* , thisArg*/) {
                if (this === void 0 || this === null)
                    throw new TypeError();
                var t = Object(this);
                var len = t.length >>> 0;
                if (typeof fun !== "function")
                    throw new TypeError();
                var res = [];
                var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
                for (var i = 0; i < len; i++) {
                    if (i in t) {
                        var val = t[i];
                        if (fun.call(thisArg, val, i, t))
                            res.push(val);
                    }
                }
                return res;
            };
        }
        // Production steps of ECMA-262, Edition 5, 15.4.4.18
        // Reference: http://es5.github.io/#x15.4.4.18
        if (!Array.prototype.forEach) {
            Array.prototype.forEach = function (callback, thisArg) {
                var T, k;
                if (this == null) {
                    throw new TypeError(' this is null or not defined');
                }
                var O = Object(this);
                var len = O.length >>> 0;
                if (typeof callback !== "function") {
                    throw new TypeError(callback + ' is not a function');
                }
                if (arguments.length > 1) {
                    T = thisArg;
                }
                k = 0;
                while (k < len) {
                    var kValue;
                    if (k in O) {
                        kValue = O[k];
                        callback.call(T, kValue, k, O);
                    }
                    k++;
                }
            };
        }
    })();

    function Util() {
        this.iev = (function () {// http://stackoverflow.com/questions/19999388/jquery-check-if-user-is-using-ie
            var ua = W.navigator.userAgent;
            var msie = ua.indexOf('MSIE ');
            if (msie > 0) { // IE 10 or older => return version number
                return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
            }
            var trident = ua.indexOf('Trident/');
            if (trident > 0) { // IE 11 => return version number
                var rv = ua.indexOf('rv:');
                return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
            }
            var edge = ua.indexOf('Edge/');
            if (edge > 0) { // IE 12 => return version number
                return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
            }
            return 100; // other browser
        })();
        this.chrome = function () {
            return /chrom(e|ium)/.test(W.navigator.userAgent.toLowerCase());
        }();

        this.isText = function (v) {
            return Q.type(v) === 'string';
        };
        this.isChrome = function () {
            return this.chrome;
        };
        this.isIE = function () {
            return this.iev < 100;
        };
        this.isIE6 = function () {
            return this.iev === 6;
        };
        this.isGtIE8 = function () {
            return this.iev > 8;
        };
        this.isNull = function (v) {
            var type = Q.type(v);
            return type === 'undefined' || type === 'null';
        };
        this.isNotNull = function (v) {
            return !this.isNull(v);
        };
        this.round = function (v, num) {
            var r = Math.pow(10, num || 0);
            return Math.round(v * r) / r;
        };
        this.parseHTML = function (html) {
            var v = Q.trim(html);
            v = v.replace(/<link[^>]*href=[^>]*>/igm, ''); //去除css引用
            v = v.replace(/(<img[^>]*)src(=['"]([^'"]+)[^>]*>)/igm, '$1_src$2');//替换图片src
            return Q.parseHTML(v);
        };
        this.parseJSON = function (json) {
            if (Q.isPlainObject(json) || Q.isArray(json)) {
                return json;
            }
            return Q.parseJSON(json);
        };
        this.uuid = function () {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        };
        this.mm2px = function (mm, dpi) {
            return Math.round(mm * dpi / 25.4);
        };
        this.dpi = function (px, mm) {
            return px * 25.4 / mm;
        };
        /*
         * 判断元素是否显示在当前的容器内
         */
        this.inViewport = function (container, element, reader, Thumb) {//from jquery.lazyload
            var e = Q(element), c = Q(container);
            var eo = e.offset(), co = c.offset();
            if (!Thumb) {
                co.top = co.top + c.height() / 3
            }

            var below = function () {
                return co.top + c.height() <= eo.top;
            };
            var right = function () {
                return co.left + c.width() <= eo.left;
            };
            var above = function () {
                return co.top >= eo.top + e.height();
            };
            var left = function () {
                return co.left >= eo.left + e.width();
            };
            return !right() && !left() && !below() && !above();
        };
        this.doApply = function (fun, _this, args) {
            if (Q.isFunction(fun)) {
                var as = args;
                if (args && !Q.isArray(args)) {
                    as = Q.makeArray(args);
                }
                return fun.apply(_this, as || []);
            }
        };
        this.doData = function (o, name, value) {
            if (o && o.length > 0) {
                if (this.isNull(value)) {
                    return o.data(name);
                } else if (value === false) {
                    o.removeData(name);
                } else {
                    o.data(name, value);
                }
            }
        };
        this.forEach = function (array, fun) {
            if (Q.isArray(array) && Q.isFunction(fun)) {
                if (array.forEach) {
                    array.forEach(fun);
                } else {
                    for (var i = 0; i < array.length; i++) {
                        fun(array[i], i, array);
                    }
                }
            }
        };
        this.safeFun = function (fn) {
            return Q.isFunction(fn) ? fn : nop;
        };
        this.int = function (v, radix) {
            return parseInt(v, radix);
        };
        this.bool = function (v, strict) {
            if (this.isNull(v)) {
                return false;
            }
            var s = v.toString().toLowerCase(), b = (s === 'true');
            if (strict === true) {
                return b;
            }
            return b || s === '1' || s === 'yes' || s === 'on';
        };
        this.float = function (v) {
            return parseFloat(v);
        };
        this.command = function (Class) {
            //兼容ie低版本
            if (Object.getOwnPropertyNames) {
                return Object.getOwnPropertyNames(Class.prototype).filter(function (name) {
                    return name.charAt(0) !== '_' && name !== 'constructor' && Q.isFunction(Class.prototype[name]);
                });
            } else {
                var arr = [];
                for (var k in Class.prototype) {
                    if (Class.hasOwnProperty(k))
                        arr.push(k);
                }
                return arr;
            }
        };

        this.value = function (o, name) {
            var ret = o;
            Q.each(name.split('.'), function (_, n) {
                if (Q.isPlainObject(ret)) {
                    ret = ret[n];
                } else {
                    ret = null;
                }
            });
            return ret;
        };

        this.comparer = function (old) {
            function O(o) {
                var v = old;

                this.get = function () {
                    return v;
                };
                this.set = function (val) {
                    v = val;
                };
                var compare = function (val) {
                    return v === val;
                };
                this.cas = function (val) {
                    if (compare(val)) {
                        return false;
                    } else {
                        this.set(val);
                        return true;
                    }
                };
            }

            return new O(old);
        };
        this.contextMenu = function (operation) {
            typeof Q.contextMenu == 'function' ? Q.contextMenu(Q.extend({zIndex: 1000}, operation)) : null;
        }
        this.baseMenu = function () {
            return {
                "copy": {
                    "name": "复制",
                    "events": "copy"
                },
                "search": {
                    "name": "百度搜索",
                    "events": "search",
                    "target": "http://www.baidu.com/s?wd=${selected}"
                }
            }
        }
        this.rotateMenu = function () {
            return {
                "+": {
                    "name": "顺时针旋转90°"
                },
                "-": {
                    "name": "逆时针旋转90°"
                }
            }
        }
        this.editMenu = function () {
            return {
                "delete": {"name": "删除"}
                , "setting": {"name": "属性设置"}
            }
        }
        this.listenerBeforeunload = function () {
            /*监听页面刷新和页面关闭,并提示相关信息*/
            Config.listenerBeforeunload && Q(W).off('beforeunload').on("beforeunload", function () {
                return "修改的内容将会丢失，确定要离开吗？";
            });
        }
    }

    var RU = R.Util = new Util();

    R.Common = (function common() {
        return {//dom元素对应的class
            CSS: {
                Document: 'rDocument',
                Single: 'rSingle',
                Double: 'rDouble',
                Page: 'rPage',
                Background: 'rBackdrop',
                Content: 'rContent',
                Cover: 'rCover',
                Palette: 'rPalette',
                Watermark: 'rWatermark',
                Group: 'rGroup',
                Text: 'rText',
                Link: 'rLink',
                ALink: 'rALink',
                Action: 'rAction',
                Mark: 'rMark',
                Semantic: 'rSemantic',
                MarkSelect: 'rSelect',
                Demo: 'rDemo',
                Message: 'rMessage',
                PageMix: 'rPageMix',
                Font: '"Times New Roman", Times, Georgia, "Microsoft YaHei", 微软雅黑, Arial, Helvetica, SimSun, "sans-serif"',
                Root: 'rRoot',
                Morph: 'rMorph',
                Faker: 'rFaker',
                Main: 'rMain',
                Sign: 'rSign',
                Extend: 'rExtend',
                Flip: 'rFlip',
                Hide: 'rHide',
                Active: 'rActive',
                Tips: 'rTips'
            },
            //请求返回的错误码表示的含义
            Status: {
                Error: 550,
                Forbidden: 553,
                NotFound: 554,
                RetryWith: 555
            },
            //默认DPI
            DPI: 96,
            //延迟时间1000ms
            RequestDelay: 1000,
            //请求失败重试次数
            RequestMax: function () {
                return Config.responseMax;
            },
            //请求合并间隔
            RequestMerge: 20,
            //数值倍率
            Ratio: 1000,
            //属性名
            Attribute: {
                LoadFlag: '_state',
                Unload: '0',
                Loading: '1',
                Loaded: '2',
                OldWidth: '_width',
                SizeScale: '_size',
                LeftScale: '_left',
                TopScale: '_top',
                MarkFlag: '_mi',
                Level: '_lv',
                PageWidth: '_wmm',
                PageHeight: '_hmm',
                PageRotate: '_rotate',
                Relayout: '_layout',
                Category: "_category"
            },
            //绑定数据名
            DataName: {
                DocInfo: '_docInfo',
                DocActions: '_docActions',
                DocLink: '_docLink',
                DocALink: '_docALink',
                DocContextMenu: '_docContextMenu',
                PageCount: '_pageCount',
                PageIndex: '_pageIndex',
                LoadState: '_loadState',
                PageArea: '_pageArea',
                PageText: '_pagetext',
                DocPerm: '_docPerm',
                DocWatermark: '_docWatermark',
                Value: '_val',
                PartBase: '_partBase',
                DocExpand: '_docExpand'
            },
            //页面排版
            LayoutMode: {
                Single: '-',
                SingleSerial: '=',
                Double: '--',
                DoubleSerial: '=='
            },
            //显示方式
            ViewMode: {
                Image: 'image',
                Text: 'text',
                Mix: 'mix'
            },
            //缩放方式
            ZoomMode: {
                FitW: '<>',
                FitH: '|'
            },
            //命令前缀
            CmdPrefix: {
                Core: 'core:',
                Book: 'book:',
                Page: 'page:',
                Operate: 'opt:',
                Action: 'act:'
            },
            FileTypes: {
                NULL: 0,
                OFD: 1,
                PDF: 2,
                EPUB: 3,
                HTML: 4,
                IMAGE: 5
            },
            DevicePixelRatio: (function () {
                // https://gist.github.com/CezaryDanielNowak/9074032

                // Fix fake window.devicePixelRatio on mobile Firefox
                var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
                if (W.devicePixelRatio !== undefined) {
                    return W.devicePixelRatio;
                } else if (W.matchMedia) {
                    var mediaQuery = function (v, ov) {
                        return '(-webkit-min-device-pixel-ratio: ' + v + '),'
                            + '(min--moz-device-pixel-ratio: ' + v + '),'
                            + '(-o-min-device-pixel-ratio: ' + ov + '),'
                            + '(min-resolution: ' + v + 'dppx)';
                    };
                    if (W.matchMedia(mediaQuery('1.5', '3/2')).matches)
                        return 1.5;
                    if (W.matchMedia(mediaQuery('2', '2/1')).matches)
                        return 2;
                    if (W.matchMedia(mediaQuery('0.75', '3/4')).matches)
                        return 0.7;
                }
                return 1;
            })()
        };
    })();

    function Ext() {
        //构造class选择器  传入class和所在父容器
        this.findByClass = function (cls, context) {
            var _cls;
            var preffix = function (v) {
                var c = v + '';
                if (!c.startWith('\\.')) {
                    c = '.' + c;
                }
                return c;
            };
            if (Q.isArray(cls)) {
                _cls = cls.map(function (v) {
                    return preffix(v);
                }).join(',');
            } else {
                _cls = preffix(cls);
            }
            return Q(_cls, context);
        };
        //构造id选择器 传入id和所在父容器
        this.findById = function (id, context) {
            var _id = id + '';
            if (!id.startWith('#')) {
                _id = '#' + id;
            }
            return Q(_id, context);
        };
        //创建新的dom元素
        this.create = function (name, val) {
            var dom = '<' + name + '>';
            if (val) {
                dom += val;
            }
            dom += '</' + name + '>';
            return Q(dom);
        };
        //构造新的div
        this.createDiv = function (val) {
            return this.create('div', val);
        };
        /*
         发送ajax请求 返回json
         url表示url
         data表示请求时传的参数
         event表示回调函数
         setting表示配置参数
         */
        this.json = function (url, data, event, setting) {
            event = Q.extend({}, event);
            setting = Q.extend({}, setting);
            var req = {
                dataType: 'json',
                method: setting.method || 'GET',
                cache: setting.cache === true,
                statusCode: setting.status || {},
                data: data
            };
            if (setting.context) {
                req.context = setting.context;
            }
            var qa = ajax(url, req);

            function invoke(name, args) {
                var e = event[name];
                if (Q.isFunction(e)) {
                    e.apply(qa, args);
                } else if (Q.isArray(e)) {
                    Q.each(e.filter(function (o) {
                        return Q.isFunction(o);
                    }), function (_, o) {
                        o.apply(qa, args);
                    });
                }
            }

            /*Q.each(['done', 'fail', 'always'], function (_, name) {
                var e = event[name], f = qa[name];
                if (Q.isFunction(e)) {
                    f.call(qa, e);
                } else if (Q.isArray(e)) {
                    Q.each(e.filter(function (o) {
                        return Q.isFunction(o);
                    }), function (_, o) {
                        f.call(qa, o);
                    });
                }
            });*/
            qa.done(function (data, textStatus, jqXHR) {
                if (Q.isArray(data) && data[0] == R.Common.Status.RetryWith) {
                    jqXHR.status = R.Common.Status.RetryWith;
                    invoke('fail', [jqXHR, textStatus]);
                } else {
                    invoke('done', [data, textStatus, jqXHR]);
                }
            }) //function( data, textStatus, jqXHR )
                .fail(function (jqXHR, textStatus, errorThrown) {
                    invoke('fail', [jqXHR, textStatus, errorThrown]);
                }) //function( jqXHR, textStatus, errorThrown )
                .always(function (data, textStatus, jqXHR) {
                    invoke('always', [data, textStatus, jqXHR]);
                }); //function( data|jqXHR, textStatus, jqXHR|errorThrown )
            return qa;
        };
        /*
         加载图片
         act表示action
         data表示构成请求的参数
         event表示回调函数
         */
        this.image = function (url, data, event) {
            var dtd = Q.Deferred();

            var img = new Image();
            img.onload = function () {
                img.onload = nop;//防止onload在append后再执行一次
                if (this.width < 60 && this.height < 60) {
                    RU.doApply(event.fail, img, arguments);
                    dtd.reject();
                } else {
                    RU.doApply(event.done, img, arguments);
                    dtd.resolve();
                }
            };
            img.onabort = function () { // 中断
                RU.doApply(event.abort, img, arguments);
                dtd.reject();
            };
            img.onerror = function () {//错误
                RU.doApply(event.fail, img, [arguments, data]);
                dtd.reject();
            };

            var c = '?';
            if (url.indexOf('?') !== -1) {
                c = '&';
            }
            img.src = url + (data ? (c + Q.param(data)) : ''); // 加载图片
            return dtd.promise();
        };
    }

    Ext.prototype = Q.extend(Ext.prototype, {
        qC: function (cls, context) {
            return this.findByClass(cls, context);
        },
        qI: function (id, context) {
            return this.findById(id, context);
        },
        qN: function (name, val) {
            return this.create(name, val);
        },
        qNv: function (val) {
            return this.createDiv(val);
        },
        loadCSS: function (url) {
            var link = document.createElement('link');
            link.type = 'text/css';
            link.rel = 'stylesheet';
            link.href = url;
            Q('head').append(link);
        },
        loadJS: function (url) {
            var dom = document.createElement('script');
            dom.src = url;
            Q('head').append(dom);
        }
    });

    //Extend jQuery
    var RE = R.EjQ = new Ext();

    var ParamRef = {
        file: '_d',
        page: '_i',
        width: '_w',
        count: '_c',
        find: '_f',
        dpi: '_p',
        cst: '_e',
        wholeWord: '_q',
        ignoreChars: '_ic',
        version: '_v',
        ver: '_v',
        type: '_t',
        partBase: '_pb',
        part: '_part',
        del: 'del',
        decrypt: '_decrypt'
    };

    function ParamBuilder(ext) {
        var data = Q.extend({}, ext, {
            '_b': '3.2.0'
        });

        this.build = function () {
            return Q.extend({}, data);
        };
        this.put = function (name, val) {
            if (RU.isNull(name)) {
                return this;
            }
            if (RU.isNull(val)) {
                delete data[name];
            } else {
                data[name] = val;
            }
            return this;
        };
        this.serialize = function () {
            return Q.param(data);
        };

        this.clone = function () {
            return new ParamBuilder(data);
        };

        var gs = {};
        Q.each(ParamRef, function (n, rn) {
            gs[n] = function (v) {
                if (v === undefined) {
                    return data[rn];
                } else {
                    data[rn] = v;
                    return this;
                }
            };
        });

        Q.extend(this, gs);

        return this;
    }

    var RA = R.Action = ['Info', 'Page', 'Image', 'Text', 'Search', 'Outline', 'Thumb', 'Bookmark', 'Attachment', 'Down', 'DownLoc', 'Fonts', 'Actions', 'Link', 'Sign', 'Verify', 'Annot', 'Print', 'Perm', 'docPerm', 'CustomTag', 'Watermark', 'Alink', 'ContextMenu', 'Area', 'Expand', 'OES', 'OPENLOC', 'Digest'].reduce(function (acc, n) {
        acc[n] = n;
        return acc;
    }, {});

    function Request(servlet) {
        if (servlet.charAt(servlet.length - 1) !== '/') {
            servlet += '/';
        }
        this.servlet = servlet;
    }

    var local = RU.bool('true');

    Request.prototype = Q.extend(Request.prototype, {
        builder: function (ext) {
            return new ParamBuilder(ext);
        },
        action: function (name, data) {
            if (RU.isNull(name)) {
                return this.servlet;
            }
            if (name === 'Link') name = 'Annot';

            if (local) {
                var val = function (n) {
                    if (data instanceof ParamBuilder) {
                        return data[n].call(data);
                    }
                    return data[ParamRef[n]];
                };
                var id = val('file'), index = val('page');
                var array = [];
                array.push(id.charAt(0));
                array.push(id);
                array.push(RU.int('0') || '0');
                switch (name) {
                    case 'Info':
                        array.push('info.json');
                        break;
                    case 'Image':
                        array.push('d_' + val('dpi'));
                        array.push(index + '.png');
                        break;
                    case 'Text':
                        array.push('text');
                        array.push(index + '.json');
                        break;
                    case 'Annot':
                        array.push('annot');
                        array.push(index + '.json');
                        break;
                    case 'Thumb':
                        array.push('w_' + RU.int('170') || 98);
                        array.push(index + '.png');
                        break;
                    case 'Outline':
                        array.push('outline.json');
                        break;
                    case 'CustomTag':
                        array.push('customTag.json');
                        break;
                    case 'Bookmark':
                        array.push('bookmark.json');
                        break;
                }
                return array.join('/');
            } else {
                var v = RA[name];
                if (v) {
                    return this.servlet + v.toLowerCase();
                }
            }
            return false;
        },
        json: function (act, data, event, setting) {
            var param = data;
            if (data instanceof ParamBuilder) {
                param = data.build();
            }
            return RE.json(this.action(act, data), local ? null : param, event, setting);
        },
        image: function (act, data, event) {
            var param = data;
            if (data instanceof ParamBuilder) {
                param = data.build();
            }
            return RE.image(this.action(act, data), local ? null : param, event);
        }
    });

    R.Request = Request;

    W.SWR = Q.extend(M, R, {
        version: {server: Config.appVersion, client: '3.2.0'}
    });

    R.Logger.info('Base ready');

})(window, window.document, window.jQuery, window.SWR || {});
