/**
 * Created with JetBrains WebStorm.
 * User: cangya.jyt
 * Date: 13-7-23
 * Time: 上午11:17
 * To change this template use File | Settings | File Templates.
 */

var config = window.config || {};

(function (debug, test) {
    var Util = {
        isFunction: function (fn) {
            return typeof fn === 'function';
        },
        removeElement: function(list, index) {
            return list.slice(0, index).concat(list.slice(index + 1, list.length));
        },
        /**
         * 等同于_.extend()
         * @param  {Object} obj
         * @return {Object}
         */
        extend: function(obj) {
            if (arguments.length < 2 || !obj) return obj;
            var sources = [].slice.call(arguments, 1);
            for (var i = 0; i < sources.length; i++) {
                var source = sources[i];
                for (var key in source) {
                    if (!source.hasOwnProperty(key)) continue;
                    obj[key] = source[key];
                }
            }
            return obj;
        }
    };

    window.QNUtil = Util;
})(config.isDebug, config.isTest);

/**
 * Created with JetBrains WebStorm.
 * User: cangya.jyt
 * Date: 13-7-23
 * Time: 下午1:30
 * To change this template use File | Settings | File Templates.
 */

//requires utils.js
var config = window.config || {};
(function (test) {
    var waitingTasks = {};
    test && (window.waitingTasks = waitingTasks);

    var TaskHalper =  {
        clearTask: function (sid) {
            delete waitingTasks[sid];
        },
        bindCallback: function (name, callback, task) {
            task['on' + name.substring(0, 1).toUpperCase() + name.substring(1)] = function (rsp, raw) {
                if (QNUtil.isFunction(callback)) {
                    callback(rsp, task.cmd, task.param, raw);
                }
                TaskHalper.clearTask(task.id);
            };

            return task;
        },

        bindPluginCallback: function (name, callback, task) {
            task['on' + name.substring(0, 1).toUpperCase() + name.substring(1)] = function (rsp, raw) {
                if (QNUtil.isFunction(callback)) {
                    callback(rsp, task.category, task.cmd, task.param, raw);
                }
                TaskHalper.clearTask(task.id);
            }
            return task;
        }
    };

    var workBenchProxy = {
        invoke: function (task) {
            switch (task.SDKGroup) {
                case 'plugin':
                case 'component':
                case 'wwplugin':
                    workbench[task.SDKGroup].invoke(task.id, task.category || '', task.cmd || '', JSON.stringify(task.param), task.appkey || '');
                    break;
                case 'top':
                case 'jdy':
                    workbench[task.SDKGroup].invoke(task.id, task.cmd || '', JSON.stringify(task.param), task.method || '');
                    break;
                case 'widgetplugin':
                    workbench[task.SDKGroup].invoke(task.id, task.category || '', task.host || '', task.cmd || '', JSON.stringify(task.param), task.appkey || '');
                    break;
                case 'application':
                    workbench[task.SDKGroup].invoke(task.id, task.cmd || '', JSON.stringify(task.param), task.source || '');
                    break;
                default:
                    workbench[task.SDKGroup].invoke(task.id, task.cmd || '', JSON.stringify(task.param));

            }
        }
    };

    function Task(SDKGroup, config) {
        this.id = workbench.createSequenceId();
        this.SDKGroup = SDKGroup;
        this.cmd = config.cmd || '';
        this.param = config.param || {};
        this.bindCallback = TaskHalper.bindCallback;
    }

    Task.prototype = {
        clearTask: function (sid) {
            waitingTasks[sid] = null;
        },
        bindCallback: TaskHalper.bindCallback,
        success: function (callback) {
            this.bindCallback('success', callback, this);
            return this;
        },
        error: function (callback) {
            this.bindCallback('error', callback, this);
            return this;
        },
        timeout: function (callback, time) {
            if (time) {
                this.bindCallback('timeout', callback, this);
                setTimeout(this.onTimeout,time);
            }
            return this;
        },
        run: function () {
            workBenchProxy.invoke(this);
        }
    }

    function ApplicationTask(SDKGroup, config) {
        Task.apply(this, arguments);
        /**
         * @see http://confluence.taobao.ali.com/pages/viewpage.action?pageId=232693110
         * workbench.jdy判断是否为千牛内的非插件页面
         * 非插件页面调用通用协议默认的spm为 qn.pcCommon.0.0
         * 插件页面调用通用协议无默认的spm，如果ISV调用时也没有传，客户端会添加一个 isv.$appkey.0.0 传给后台
         */
        this.source = config.source || (workbench.jdy ? 'qn.pcCommon.0.0' : '');
    }

    ApplicationTask.prototype = Task.prototype;

    function PluginTask(SDKGroup, config) {
        Task.call(this, SDKGroup, config);

        this.category = config.category;
        this.appkey = config.appkey;
        this.bindCallback = TaskHalper.bindPluginCallback;
    }

    PluginTask.prototype = Task.prototype;

    function TopTask(SDKGroup, config) {
        Task.call(this, SDKGroup, config);
        this.method = config.method || 'post';
        delete this.bindCallback;
        this.timing = {
            start: 0,
            end: 0
        };
    }

    TopTask.prototype = QNUtil.extend({}, Task.prototype, {
        bindCallback: function(name, callback, task) {
            task['on' + name.substring(0, 1).toUpperCase() + name.substring(1)] = function (rsp, raw) {
                task.timing.end = performance.now();
                if (QNUtil.isFunction(callback)) {
                    callback(rsp, task.cmd, task.param, raw);
                }
                TaskHalper.clearTask(task.id);
                // 记录Top/jdy请求时间
                if (QN.page.needTopUac) {
                    var data = ['ethernet', QN.page.pluginId, task.cmd, 0, Number(name === 'success'),
                                '', '', 0, '', '', '', '', '', '', '', '', '', '', '', '',
                                Math.round(task.timing.end - task.timing.start), '', '', '', '', '', '', '', '', '', '']
                                .map(function(d) {return d.toString();});
                    // var errorMsg = rsp.sub_msg || rsp.msg;
                    // rsp.code && data.push(rsp.code);
                    // errorMsg && data.push(errorMsg);
                    QN.application.invoke({
                        cmd: 'pluginPerformanceUacLog',
                        param: {
                            url_or_cmd: task.cmd,
                            data: JSON.stringify(data)
                        },
                        success: function() {
                            arguments;
                        },
                        error: function() {
                            console.log('调用失败: pluginPerformanceUacLog', arguments);
                        }
                    });
                }
            };
        },

        run: function() {
            // console.log(this);
            this.timing.start = performance.now();
            workBenchProxy.invoke(this);
        }
    });

    function WidgetTask(SDKGroup, config) {
        Task.call(this, SDKGroup, config);
        this.appkey = config.appkey;
        this.host = config.host;
        this.category = config.category;
    }

    WidgetTask.prototype = Task.prototype;

    var TaskFactory = {
        getTask: function (SDKGroup, config) {
            var newTask = null;
            switch (SDKGroup) {
                case 'plugin':
                case 'component' :
                case 'wwplugin':
                    newTask = new PluginTask(SDKGroup, config);
                    break;
                case 'jdy':
                case 'top':
                    newTask = new TopTask(SDKGroup, config);
                    break;
                case 'widgetplugin':
                    newTask = new WidgetTask(SDKGroup, config);
                    break;
                case 'application':
                    newTask = new ApplicationTask(SDKGroup, config);
                    break;
                default:
                    newTask = new Task(SDKGroup, config);
                    break;
            }
            waitingTasks[newTask.id] = newTask;
            return newTask;
        }
    };

    // global notify for Native code
    function onNotify(sid, success, rawRsp) {
        var task = waitingTasks[sid];
        if (typeof task !== 'undefined') {
            var rsp;
            try {
                rsp = JSON.parse(rawRsp);
            } catch(ex) {
                rsp = rawRsp;
            }
            task[success === 0 ? 'onSuccess' : 'onError'](rsp, rawRsp);
        }
    }

    window.QNTaskFactory = TaskFactory;
    window.onInvokeNotify = onNotify;
    if(test) {
        window.TaskHalper = TaskHalper
        window.Task = Task;
        window.PluginTask = PluginTask;
    }
})(config.isTest);


/**
 * Created with JetBrains WebStorm.
 * User: cangya.jyt
 * Date: 13-7-23
 * Time: 下午1:38
 * To change this template use File | Settings | File Templates.
 */

//requires util.js tasks.js
var config = window.config || {};
(function (test) {
    function invokeHelper(SDKGroup, config) {
        var task = QNTaskFactory.getTask(SDKGroup, config)
            .success(config.success)
            .error(config.error)
            .timeout(config.timeout, config.limitTime);
        return task.run();
    }

    /*
    structure
    eventChains: {
     '{event_id}': {
      'prepared': true,
      'callbackList': [{
      'eventId': 'eventId',
      'success': callack,
      'error': callback,
      'notify': callback,
      }]
     }
    }
     */
    var eventChains = {},
        regConfigs = {},
        unregConfigs = {};

    if (test) {
        window.QNtest = {
            eventChains: eventChains,
            regConfigs: regConfigs,
            unregConfigs: unregConfigs
        };
    }

    var QN = {
        page: {
            // 是否是入口页，而非跳转页或重定向页
            isMain: 1,
            // 是否需要进行性能监控
            needUac: false,
            // 是否需要进行Top调用打点
            needTopUac: false
        },
        invokeHelper: invokeHelper,
        application: {
            invoke: function (config) {
                invokeHelper('application', config);
            }
        },
        qtask: {
            invoke: function (config) {
                invokeHelper('qtask', config);
            }
        },
        plugin: {
            invoke: function (config) {
                invokeHelper('plugin', config);
            },
            successResponse: function(rsp){
                workbench.setResponse(JSON.stringify({
                    'msg': '',
                    'code': 0,
                    'result': JSON.stringify(rsp)
                }));
            },
            errorResponse: function(msg) {
                workbench.setResponse(JSON.stringify({
                    'msg': msg,
                    'code': 1,
                    'result': ''
                }));
            }
        },
        wwplugin: {
            invoke: function(config) {
                invokeHelper('wwplugin', config);
            }
        },
        widgetplugin: {
            invoke: function(config) {
                invokeHelper('widgetplugin', config);
            }
        },
        wangwang: {
            invoke: function (config) {
                invokeHelper('wangwang', config);

            }
        },
        top: {
            invoke: function (config) {
                invokeHelper('top', config);
            }
        },
        component: {
            invoke: function(config) {
                invokeHelper('component', config);
            }
        },
        sysconfig: {
            invoke: function (config) {
                invokeHelper('sysconfig', config);
            }
        },
        setResponse: function(rsp) {
            var statusCode = {success: 0, error: 1};
            var param = {msg: rsp.msg, code: statusCode[rsp.status], result: ''};
            try {
                param.result = JSON.stringify(rsp.result);
            } catch (ex) {
                param.result = '';
            }
            workbench.setResponse(param);
        },
        event: {
            regEvent: function(config) {
                var eventId = config.eventId || '',
                    eventHolder = eventChains[eventId];

                if(!eventHolder) {
                    eventHolder = {
                        prepared: false,
                        callbackList: []
                    }
                    eventChains[eventId] = eventHolder;
                }
                eventHolder.callbackList.push(config.notify);
                if (!eventHolder.prepared) {
                    regConfigs[eventId] = regConfigs[eventId] || [];
                    regConfigs[eventId].push(config);
                    workbench.regEvent(workbench.createSequenceId(), eventId);
                } else  if (QNUtil.isFunction(config.success)) {
                    config.success(eventId);
                }
            },
            unregEvent: function(config) {
                var eventId = config.eventId || '',
                    eventHolder = eventChains[eventId];
                if (!eventHolder) {
                    return QNUtil.isFunction(config.error) && config.error('事件未注册', eventId);
                }

                var callbackList = eventHolder.callbackList,
                    notify = config.notify,
                    index = callbackList.indexOf(notify);

                if (!QNUtil.isFunction(notify)){
                    eventHolder.callbackList = [];
                } else if (index !== -1) {
                    eventHolder.callbackList = QNUtil.removeElement(callbackList, index);
                } else {
                    QNUtil.isFunction(config.error) && config.error('事件回调函数不存在', eventId);
                }

                if (eventHolder.callbackList.length === 0) {
                    unregConfigs[eventId] = unregConfigs[eventId] || [];

                    unregConfigs[eventId].push(config);
                    workbench.unregEvent(workbench.createSequenceId(), eventId);
                } else {
                    QNUtil.isFunction(config.success) && config.success(eventId);
                }
            },
            postEvent: function(config) {
                var eventId = config.eventId || '',
                    msgData = config.param || {};
                workbench.postEvent(eventId, JSON.stringify(msgData));
            }
        },
        initTheme: function(win){
            win = win || window;
            var less = win.less;
            if (!less) {return;}
            var titleBarHeight = 31;

            function genPalette(color, count) {
                var i, cl = [], step, pos;
                count = count || 25;
                var hsl = color.toHSL();

                step = (100/count);
                pos = count - Math.ceil(hsl.l * 100 / step);

                for (i = 0; i < count; i++) {
                    if (i == pos) {
                        cl.push(color);
                    } else {
                        cl.push (less.tree.functions.hsla(hsl.h, hsl.s, (100 - i * step)/100, hsl.a));
                    }
                }

                return {
                    list: cl,
                    pos: pos
                };
            }

            function changeTheme(theme) {
                var color = new less.tree.Color(theme.rgb);
                var i, len, vars = {
                    '@color-base': color.toRGB(),
                    '@theme-background-image': 'url(' + theme.backgroundImageUrl + ')',
                    '@theme-bg-img': 'url(' + theme.backgroundImageUrl + ')',
                    '@theme-bg-url': '"' + theme.backgroundImageUrl + '"',
                    '@theme-bg-x': theme.backgroundPositionX || 'left',
                    '@title-bar-height': titleBarHeight + 'px'
                }

                var list = genPalette(color).list;
                len = list.length;
                for (i = 1; i <= len; i ++) {
                    vars['@palette-color-' + i] = list[i-1].toRGB();
                }

                if (less && QNUtil.isFunction(window.less.modifyVars)) {
                    window.less = window.less || {};
                    window.less.globalResetDefaultVars = vars;
                    less.modifyVars(vars);
                }
            }

            QN.application.invoke({
                cmd : 'getSkinTheme',
                param : {},
                success : function(rsp) {
                    titleBarHeight = Number(rsp.titleBarHeight || 31) || 31;
                    changeTheme(rsp);
                }
            });

            QN.event.regEvent({
                eventId: 'bench.colorize',
                notify: function(rsp){
                    rsp = JSON.parse(rsp);
                    changeTheme(rsp);
                }
            });
        }
    };

    var implementedAPI = {};
    QN.implement = {
        api: function(config) {
            if (!config || !config.cmd || !config.onInvoke || typeof config.cmd !== 'string' || typeof config.onInvoke !== 'function') {
                throw '参数错误';
            }
            if (implementedAPI[config.cmd]) {
                console.log('Implementation of ' + config.cmd + ' is overrided.');
            }
            implementedAPI[config.cmd] = config.onInvoke;
        }
    }

    var readyCallbacks = []; // QN.ready的回调队列
    var isReady = false; // QN.ready的标志位

    /**
     * 类似于$.ready，认定的标准是workbench对象被释放到window中
     * @param  {Function} callback ready之后的回调函数，执行的上下文是window
     * @return {Boolean} 回调是否添加成功
     */
    QN.ready = function(callback) {
        if (!QNUtil.isFunction(callback)) {
            throw '调用QN.ready参数类型错误，必须为函数';
            return false;
        }
        if (isReady) {
            callback.apply(window);
            return true;
        }
        readyCallbacks.push(callback);
        return true;
    };

    // 适时的触发Ready并执行其回调
    window.addEventListener('DOMContentLoaded', function() {
        // 递归执行队列中的回调函数
        var exeCallbacks = function() {
            if (readyCallbacks.length === 0) return;
            readyCallbacks.shift().apply(window);
            arguments.callee();
        };
        // 如果domready的时候，如果workbench已经存在，认为已经ready（2.x以上版本千牛，但并非所有2.x都支持）
        if (window.workbench) {
            isReady = true;
            exeCallbacks();
            return;
        }
        // 部分老版本千牛（2.x和1.x）要等到onload之后，workbench才存在
        window.addEventListener('load', function() {
            isReady = true;
            exeCallbacks();
        });
    });


    // global event notify for Native code
    function onEventNotify(sid, eventId, status, rsp) {
        var regConfig = regConfigs[eventId],
            unregConfig = unregConfigs[eventId];
        switch (status) {

            case 0: //注册成功
                regConfig && regConfig.forEach(function(e){
                    QNUtil.isFunction(e.success) && e.success(eventId);
                });
                delete regConfigs[eventId];
                eventChains[eventId].prepared = true;
                break;
            case 1: //注册失败
                regConfig && regConfig.forEach(function(e){
                    QNUtil.isFunction(e.error) && e.error(rsp, eventId);
                });
                delete regConfigs[eventId];
                break;
            case 2: //消息通知
                var callbacks = eventChains[eventId] && eventChains[eventId].callbackList;
                callbacks && callbacks.forEach(function(fn){
                    QNUtil.isFunction(fn) && fn(rsp, eventId);
                });
                break;
            case 3: //注销成功
                unregConfig && unregConfig.forEach(function(e){
                    QNUtil.isFunction(e.success) && e.success(eventId);
                });
                delete unregConfigs[eventId];
                delete eventChains[eventId];
                break;
            case 4: //注销失败
                unregConfig && unregConfig.forEach(function(e){
                    QNUtil.isFunction(e.error) && e.error(rsp, eventId);
                });
                delete unregConfigs[eventId];
                break;
        }
    }

    // global API invoke for native code
    function onInvokeAPI(cmd, param) {
        if (implementedAPI[cmd]) {
            var args = param ? [param] : [];
            implementedAPI[cmd].apply(window, args);
            return true;
        }
        return false;
    }

    function initQNTheme(){
        /**
         * 匹配以下URL
         * //g.alicdn.com/sj/qnui/1.4.3/css/sui.css
         * //g.alicdn.com/sj/qnui/1.4.3/css/sui.min.css
         * //g.alicdn.com/sj/qnui/1.4.3/css/sui-append.css
         * //g.alicdn.com/sj/qnui/1.4.3/css/sui-append.min.css
         * //g.alicdn.com/sj/qnui/??1.4.3/css/sui.css
         * //g.alicdn.com/sj/qnui/1.4.3/??css/sui.css
         * //g.alicdn.com/sj/qnui/1.4.3/css/??sui.css
         */
        var reg = /qnui\/(?:\?\?)?(\d+\.\d+\.\d+)\/.+?sui(-append)?(?:\.min)?\.css/;
        var links = document.getElementsByTagName('link');
        var themes = {
                'systemdefaultid' : '',
                'skin_texture_0'  : '-themes-green',
                'skin_texture_1'  : '-themes-pink',
                'skin_texture_2'  : '-themes-dark-green',
            };
        var qnuiLinks = [].filter.call(links, function(link) {
            return reg.test(link.href);
        });
        if (qnuiLinks.length === 0) {return;}
        var version = qnuiLinks[0].href.match(reg)[1];
        var compareVersion = function(v1, v2) {
            var v1Arr = v1.split('.'), v2Arr = v2.split('.');
            for (var i = 0; i < v1Arr.length; i++) {
                v1Arr[i] = parseInt(v1Arr[i]);
                v2Arr[i] = parseInt(v2Arr[i]);
                if (v1Arr[i] === v2Arr[i] && i + 1 === v1Arr.length) {
                    return 0;
                }
                if (v1Arr[i] === v2Arr[i]) {
                    continue;
                }
                if (v1Arr[i] > v2Arr[i]) {
                    return 1;
                }
                return -1;
            }
        };
        if (compareVersion(version, '1.1.1') < 0) {return;} // 1.1.1版本以下不支持换肤
        var linkAfter = function(link, target) {
            if (target.nextElementSibling) {
                target.parentNode.insertBefore(link, target.nextElementSibling);
            } else {
                target.parentNode.appendChild(link);
            }
        };
        var themeLinks = qnuiLinks.map(function(link) {
            var theme = document.createElement('link');
            theme.rel = 'stylesheet';
            theme.type = 'text/css';
            linkAfter(theme, link);
            return theme;
        });
        var updateSuiTheme = function(theme) {
            qnuiLinks.forEach(function(qnuiLink, i) {
                var append = qnuiLink.href.match(reg)[2] || '';
                // 兼容2.08.00以下版本客户端，全量3.02.00后可去除此兼容
                var protocol = (/^https?:/).test(location.protocol) ? location.protocol : 'http:';
                themeLinks[i].href = protocol + '//g.alicdn.com/sj/qnui/' + version + '/css/sui' + themes[theme] + append + '.min.css';
            });
        };

        QN.application.invoke({
            cmd : 'getSkinTheme',
            param : {},
            success : function(rsp) {
                updateSuiTheme(rsp.theme);
            }
        });
        QN.event.regEvent({
            eventId: 'bench.colorize',
            notify: function(rsp){
                rsp = JSON.parse(rsp);
                updateSuiTheme(rsp.theme);
            }
        });
    }

    if (document.all){
        window.attachEvent('onload', initQNTheme);
    }else{
        window.addEventListener('load', initQNTheme);
    }

    window.QN = QN;
    window.onEventNotify = onEventNotify;
    window.onInvokeAPI = onInvokeAPI;
})(config.isTest);
(function() {
    QN.jdy = QN.jdy || {};
    !QN.jdy.invoke && (QN.jdy.invoke = function(config) {
        QN.invokeHelper('jdy', config);
    });
}) ();
(function() {

    var timing = performance.timing;

    var Track = {

        data: {
            networkEnvironment         : 'ethernet',
            pluginId                   : '',
            // 'url|cmd'                  : '',
            url                        : location.protocol + '//' + location.host + location.pathname,
            isMain                     : 0, // 0 == false
            isSuccess                  : 1, // 1 == true
            redirectCount              : '',
            navigationType             : '',
            navigationStart            : '',
            unloadEventStart           : '',
            unloadEventEnd             : '',
            redirectStart              : '',
            redirectEnd                : '',
            fetchStart                 : '',
            domainLookupStart          : '',
            domainLookupEnd            : '',
            connectStart               : '',
            connectEnd                 : '',
            secureConnectionStart      : '',
            requestStart               : '',
            responseStart              : '',
            responseEnd                : '',
            domLoading                 : '',
            domInteractive             : '',
            domContentLoadedEventStart : '',
            domContentLoadedEventEnd   : '',
            domComplete                : '',
            loadEventStart             : '',
            loadEventEnd               : '',
            initialJSHeapSize          : '',
            averageJSHeapSize          : '',
            maxJSHeapSize              : ''
            // ErrorCode                  : null,
            // ErrorCodeDesc              : null
        },

        loadEvent: {
            triggered: false,
            callbacks: []
        },

        init: function() {
            this.events();
            this.ballot();
        },

        events: function() {
            var self = this;
            window.addEventListener('load', function() {
                var exeCallbacks = function() {
                    if (self.loadEvent.callbacks.length === 0) return;
                    self.loadEvent.callbacks.shift().apply(self);
                    arguments.callee();
                };
                self.loadEvent.triggered = true;
                exeCallbacks();
            });
        },

        winLoad: function(callback) {
            var self = this;
            if (self.loadEvent.triggered) {
                callback.apply(self);
                return true;
            }
            self.loadEvent.callbacks.push(callback);
        },

        /**
         * 将data根据传入的key转换成数组
         * @param  {Array} format log数据的格式
         * @return {Array} 数据数组
         */
        toArray: function(format) {
            var result = [];
            for (var i = 0; i < format.length; i++) {
                result.push((this.data[format[i]] || '').toString());
            }
            return result;
        },

        // 抽签
        ballot: function() {
            var self = this;
            QN.application.invoke({
                cmd: 'needPluginPerformanceUac',
                success: function(rsp) {
                    QN.page.needUac = rsp.needUac;
                    QN.page.needTopUac = rsp.needUac && rsp.needTopUac;
                    QN.page.pluginId = self.data.pluginId = rsp.pluginId;
                    QN.page.isMain = self.data.isMain = Number(rsp.isMain);
                    if (!rsp.needUac) return;
                    // 接口返回数据后，可能onload事件已经触发，因此实现类似$.ready()的Track.winload()函数
                    self.winLoad(function() {
                        setTimeout(function() {
                            self.calcData();
                            self.logPerformance();                            
                            self.trackJSHeap();
                        });
                    });
                },
                error: function() {
                    console.log('调用失败: needPluginPerformanceUac', arguments);
                }
            });
        },

        /**
         * 计算性能数据
         * 注意：因为平均内存需要在页面打开期间定期记录并且计算，所以内存数据取上次记录的用于本次打点  
         */
        calcData: function() {
            var data  = this.data;
            data.redirectCount = performance.navigation.redirectCount;
            data.navigationType = performance.navigation.type;
            data.navigationStart = 0;
            if (timing.unloadEventStart > 0) {
                data.unloadEventStart = timing.unloadEventStart - timing.navigationStart;
            }
            if (timing.unloadEventEnd > 0) {
                data.unloadEventEnd = timing.unloadEventEnd - timing.navigationStart;
            }
            if (timing.redirectStart > 0) {
                data.redirectStart = timing.redirectStart - timing.navigationStart;
            }
            if (timing.redirectEnd > 0) {
                data.redirectEnd = timing.redirectEnd - timing.navigationStart;
            }
            if (timing.fetchStart > 0) {
                data.fetchStart = timing.fetchStart - timing.navigationStart;
            }
            if (timing.domainLookupStart > 0) {
                data.domainLookupStart = timing.domainLookupStart - timing.navigationStart;
            }
            if (timing.domainLookupEnd > 0) {
                data.domainLookupEnd = timing.domainLookupEnd - timing.navigationStart;                
            }
            if (timing.connectStart > 0) {
                data.connectStart = timing.connectStart - timing.navigationStart;
            }
            if (timing.connectEnd > 0) {
                data.connectEnd = timing.connectEnd - timing.navigationStart;
            }
            if (timing.secureConnectionStart > 0) {
                data.secureConnectionStart = timing.secureConnectionStart - timing.navigationStart;
            }
            if (timing.requestStart > 0) {
                data.requestStart = timing.requestStart - timing.navigationStart;
            }
            if (timing.responseStart > 0) {
                data.responseStart = timing.responseStart - timing.navigationStart;
            }
            if (timing.responseEnd > 0) {
                data.responseEnd = timing.responseEnd - timing.navigationStart;
            }
            if (timing.domLoading > 0) {
                data.domLoading = timing.domLoading - timing.navigationStart;
            }
            if (timing.domInteractive > 0) {
                data.domInteractive = timing.domInteractive - timing.navigationStart;
            }
            if (timing.domContentLoadedEventStart > 0) {
                data.domContentLoadedEventStart = timing.domContentLoadedEventStart - timing.navigationStart;
            }
            if (timing.domContentLoadedEventEnd > 0) {
                data.domContentLoadedEventEnd = timing.domContentLoadedEventEnd - timing.navigationStart;
            }
            if (timing.domComplete > 0) {
                data.domComplete = timing.domComplete - timing.navigationStart;
            }
            if (timing.loadEventStart > 0) {
                data.loadEventStart = timing.loadEventStart - timing.navigationStart;
            }
            if (timing.loadEventEnd > 0) {
                data.loadEventEnd = timing.loadEventEnd - timing.navigationStart;
            }
            data.initialJSHeapSize = Number(localStorage.getItem('initialJSHeapSize')) || '';
            data.maxJSHeapSize = Number(localStorage.getItem('maxJSHeapSize')) || '';
            // 从localStorage取出上次页面访问期间统计的内存总和和统计次数
            var trackedJSHeapSize  = Number(localStorage.getItem('trackedJSHeapSize'));
            var trackedJSHeapCount = Number(localStorage.getItem('trackedJSHeapCount'));
            if (trackedJSHeapSize && trackedJSHeapCount) {
                data.averageJSHeapSize      = Math.round(trackedJSHeapSize / trackedJSHeapCount);
            }
        },

        // 页面性能打点
        logPerformance: function() {
            var self = this;
            var data = self.data;
            var format = [
                'networkEnvironment', 'pluginId', 'url', 'isMain', 'isSuccess', 'redirectCount', 'navigationType',
                'navigationStart', 'unloadEventStart', 'unloadEventEnd', 'redirectStart', 'redirectEnd', 'fetchStart',
                'domainLookupStart', 'domainLookupEnd', 'connectStart', 'connectEnd', 'secureConnectionStart',
                'requestStart', 'responseStart', 'responseEnd', 'domLoading', 'domInteractive',
                'domContentLoadedEventStart', 'domContentLoadedEventEnd', 'domComplete', 'loadEventStart', 'loadEventEnd',
                'initialJSHeapSize', 'averageJSHeapSize', 'maxJSHeapSize'
            ];
            QN.application.invoke({
                cmd: 'pluginPerformanceUacLog',
                param: {
                    url_or_cmd: data.url,
                    data: JSON.stringify(self.toArray(format)),
                },
                success: function() {
                    arguments;
                },
                error: function() {
                    console.log('调用失败: pluginPerformanceUacLog', arguments);
                }
            });
        },

        trackJSHeap: function() {
            var self = this;
            var memory = performance.memory;
            localStorage.setItem('initialJSHeapSize', memory.totalJSHeapSize);
            localStorage.setItem('maxJSHeapSize', memory.totalJSHeapSize);
            localStorage.setItem('trackedJSHeapSize', memory.totalJSHeapSize);
            localStorage.setItem('trackedJSHeapCount', 1);
            setInterval(function() {
                var memory = performance.memory;
                var maxJSHeapSize = Number(localStorage.getItem('maxJSHeapSize'));
                var trackedJSHeapSize = Number(localStorage.getItem('trackedJSHeapSize'));
                localStorage.setItem('maxJSHeapSize', Math.max(maxJSHeapSize, memory.totalJSHeapSize));
                localStorage.setItem('trackedJSHeapSize', trackedJSHeapSize + memory.totalJSHeapSize);
                localStorage.setItem('trackedJSHeapCount', Number(localStorage.getItem('trackedJSHeapCount')) + 1);
            }, 5 * 60 * 1000);
        }
    };

    QN.ready(function() {
        Track.init();
    });
    window.Track = Track;
})();