/**
 * @date 12-12-12
 * @describe: 简单实现 CommonJS 的 Modules/1.1.1
 *
 * 定义模块
 * math.js
 * define('math', function(require, exports, module) {
 *     exports.add = function(a, b){
 *          return a+b;
 *     }
 * });
 *
 * 调用模块
 * init.js
 * define('init',function(require, exports, module) {
 *     console.log(require('math').add(1,2,));
 * });
 * @author: KnightWu
 * @version: 0.0.1
 */

 window.youdao = window.youdao || {};

(function (youdao, undefined) {

    var STATUS = {
        "DEFINED": "The module is just DEFINED",
        "INITIALIZED": "The module is compiled and module.exports is available."
    };

    if (youdao.define) {
        return;
    }

    function isFunction(obj) {
        return Object.prototype.toString.call(obj) === '[object Function]';
    }

    var moduleCache = {}; //所有模块的集合
    var parent; //记录当前运行模块
    var entryModuleIdentifier = null; //初始化模块的名称

    entryModuleIdentifier = "index.js";
    if (!entryModuleIdentifier) {   //入口不存在则抛出异常
        throw new Error('No data-main attribute in script tag.');
    }
    /**
     * require 的实现
     * @param identifier
     * @return {*}
     */
    function require(identifier) {
        if (!moduleCache[identifier]) { //不存在该模块，则抛出异常
            throw new Error('Module ' + identifier + ' is not defined.');
        }
        var foreign = moduleCache[identifier]; //否则，取出该模块
        if (foreign.module.status !== STATUS.INITIALIZED) {  //未初始化
            initializeModule(identifier);   //初始化该模块
        }
        return foreign.module.exports;  //返回模块的exports或其他
    }

    /**
     * 初始化模块
     * @param identifier 模块名
     */
    function initializeModule(identifier) {
        var current = moduleCache[identifier], //获取模块
            module = current.module,
            exports = module.exports,
            factory = module.factory;
        module.parent = parent;//指向初始化时调用当前模块的模块。根据该属性，可以得到模块初始化时的 Call Stack.
        parent = current;
        if (isFunction(factory)) {  //检测模块的工厂方法是否函数
            var ret = factory(
                require,
                exports, //定义模块的exports
                module
            );
            if (ret !== undefined) {
                module.exports = ret;
            }
        } else {  //不是函数 直接返回factory 可以是对象
            module.exports = moduleCache[identifier].factory;
        }
        module.status = STATUS.INITIALIZED;
    }

    /**
     * define ，用来定义模块
     * @param identifier 模块名
     * @param dependencies 依赖模块名
     * @param factory  模块的实现
     */
    function define(identifier, dependencies, factory) {
        if (moduleCache[identifier]) {  //存在相同名称，抛出错误
            throw new Error('Module ' + identifier + ' has been defined already.');
        }
        if (typeof factory === 'undefined') {
            factory = dependencies;
        }
        if (isFunction(factory) || factory === Object(factory)) { //factory不是函数或者对象，抛出异常
            /**
             *
             * @type {{factory: *, module: {id: *, exports: {}}, uri: string, dependencies: Array, parent: {}, factory: *, status: null}}
             */
            moduleCache[identifier] = {  //模块的module属性，记录本模块的重要属性和方法
                module: {
                    id: identifier,
                    exports: {},
                    uri: '',
                    dependencies: [],
                    parent: undefined,
                    factory: factory,
                    status: STATUS.DEFINED
                }
            };
            if (identifier === entryModuleIdentifier) {   //如果此模块名称和初始化名称相同，表明是入口函数，直接执行
                initializeModule(identifier);
            }
        } else {
            throw new Error('factory of module ' + identifier + ' must be an object or a function.');
        }
    }

    youdao.define = define;
})(window);

define("code.js", function(require, exports, module) {

    // var $ = require('jQuery');

    // var sendLog = function (logParams) {
    //     var version = window.version || "";
    //     var vendor = window.vendor || "";
    //     var extid = window.extid || "";
    //     var logUrlPrefix = "http://zhushou.huihui.cn/log?type=ARMANI_EXTENSION_ACTION&browser=ie&version="
    //         + version + "&vendor=" + vendor + "&extensionid=" + extid + "&";
    //     if (window.TypeName) {
    //         logUrlPrefix += "defaultType=" + window.TypeName + "&";
    //     }

    //     var source = /source=(.*)/.exec(location.search);
    //     if (!!source && source.length == 2) {
    //         logUrlPrefix += "source=" + source[1] + "&";
    //     }

    //     var finalLogUrl = logUrlPrefix + logParams.join("&");
    //     img = document.createElement('img');
    //     img.setAttribute('src', finalLogUrl);
    //     img.setAttribute('style', 'display:none');
    // };

    // // 注册log事件
    // $(document).delegate('[clkAction]', 'click', function (event) {

    // });


    var md5 = function(str) {
      /*
       * Configurable variables. You may need to tweak these to be compatible with
       * the server-side, but the defaults work in most cases.
       */
      var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase        */
      var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance   */

      /*
       * These are the functions you'll usually want to call
       * They take string arguments and return either hex or base-64 encoded strings
       */
      function hex_md5(s) {
        return rstr2hex(rstr_md5(str2rstr_utf8(s)));
      }

      function b64_md5(s) {
        return rstr2b64(rstr_md5(str2rstr_utf8(s)));
      }

      function any_md5(s, e) {
        return rstr2any(rstr_md5(str2rstr_utf8(s)), e);
      }

      function hex_hmac_md5(k, d) {
        return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)));
      }

      function b64_hmac_md5(k, d) {
        return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)));
      }

      function any_hmac_md5(k, d, e) {
        return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e);
      }

      /*
       * Perform a simple self-test to see if the VM is working
       */
      function md5_vm_test() {
        return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
      }

      /*
       * Calculate the MD5 of a raw string
       */
      function rstr_md5(s) {
        return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
      }

      /*
       * Calculate the HMAC-MD5, of a key and some data (raw strings)
       */
      function rstr_hmac_md5(key, data) {
        var bkey = rstr2binl(key);
        if (bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);

        var ipad = Array(16),
          opad = Array(16);
        for (var i = 0; i < 16; i++) {
          ipad[i] = bkey[i] ^ 0x36363636;
          opad[i] = bkey[i] ^ 0x5C5C5C5C;
        }

        var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
        return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
      }

      /*
       * Convert a raw string to a hex string
       */
      function rstr2hex(input) {
        try {
          hexcase
        } catch (e) {
          hexcase = 0;
        }
        var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
        var output = "";
        var x;
        for (var i = 0; i < input.length; i++) {
          x = input.charCodeAt(i);
          output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
        }
        return output;
      }

      /*
       * Convert a raw string to a base-64 string
       */
      function rstr2b64(input) {
        try {
          b64pad
        } catch (e) {
          b64pad = '';
        }
        var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        var output = "";
        var len = input.length;
        for (var i = 0; i < len; i += 3) {
          var triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
          for (var j = 0; j < 4; j++) {
            if (i * 8 + j * 6 > input.length * 8) output += b64pad;
            else output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
          }
        }
        return output;
      }

      /*
       * Convert a raw string to an arbitrary string encoding
       */
      function rstr2any(input, encoding) {
        var divisor = encoding.length;
        var i, j, q, x, quotient;

        /* Convert to an array of 16-bit big-endian values, forming the dividend */
        var dividend = Array(Math.ceil(input.length / 2));
        for (i = 0; i < dividend.length; i++) {
          dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
        }

        /*
         * Repeatedly perform a long division. The binary array forms the dividend,
         * the length of the encoding is the divisor. Once computed, the quotient
         * forms the dividend for the next step. All remainders are stored for later
         * use.
         */
        var full_length = Math.ceil(input.length * 8 /
          (Math.log(encoding.length) / Math.log(2)));
        var remainders = Array(full_length);
        for (j = 0; j < full_length; j++) {
          quotient = Array();
          x = 0;
          for (i = 0; i < dividend.length; i++) {
            x = (x << 16) + dividend[i];
            q = Math.floor(x / divisor);
            x -= q * divisor;
            if (quotient.length > 0 || q > 0)
              quotient[quotient.length] = q;
          }
          remainders[j] = x;
          dividend = quotient;
        }

        /* Convert the remainders to the output string */
        var output = "";
        for (i = remainders.length - 1; i >= 0; i--)
          output += encoding.charAt(remainders[i]);

        return output;
      }

      /*
       * Encode a string as utf-8.
       * For efficiency, this assumes the input is valid utf-16.
       */
      function str2rstr_utf8(input) {
        var output = "";
        var i = -1;
        var x, y;

        while (++i < input.length) {
          /* Decode utf-16 surrogate pairs */
          x = input.charCodeAt(i);
          y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
          if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
            x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
            i++;
          }

          /* Encode output as utf-8 */
          if (x <= 0x7F)
            output += String.fromCharCode(x);
          else if (x <= 0x7FF)
            output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
              0x80 | (x & 0x3F));
          else if (x <= 0xFFFF)
            output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
              0x80 | ((x >>> 6) & 0x3F),
              0x80 | (x & 0x3F));
          else if (x <= 0x1FFFFF)
            output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
              0x80 | ((x >>> 12) & 0x3F),
              0x80 | ((x >>> 6) & 0x3F),
              0x80 | (x & 0x3F));
        }
        return output;
      }

      /*
       * Encode a string as utf-16
       */
      function str2rstr_utf16le(input) {
        var output = "";
        for (var i = 0; i < input.length; i++)
          output += String.fromCharCode(input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
        return output;
      }

      function str2rstr_utf16be(input) {
        var output = "";
        for (var i = 0; i < input.length; i++)
          output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
            input.charCodeAt(i) & 0xFF);
        return output;
      }

      /*
       * Convert a raw string to an array of little-endian words
       * Characters >255 have their high-byte silently ignored.
       */
      function rstr2binl(input) {
        var output = Array(input.length >> 2);
        for (var i = 0; i < output.length; i++)
          output[i] = 0;
        for (var i = 0; i < input.length * 8; i += 8)
          output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
        return output;
      }

      /*
       * Convert an array of little-endian words to a string
       */
      function binl2rstr(input) {
        var output = "";
        for (var i = 0; i < input.length * 32; i += 8)
          output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
        return output;
      }

      /*
       * Calculate the MD5 of an array of little-endian words, and a bit length.
       */
      function binl_md5(x, len) {
        /* append padding */
        x[len >> 5] |= 0x80 << ((len) % 32);
        x[(((len + 64) >>> 9) << 4) + 14] = len;

        var a = 1732584193;
        var b = -271733879;
        var c = -1732584194;
        var d = 271733878;

        for (var i = 0; i < x.length; i += 16) {
          var olda = a;
          var oldb = b;
          var oldc = c;
          var oldd = d;

          a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
          d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
          c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
          b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
          a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
          d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
          c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
          b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
          a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
          d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
          c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
          b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
          a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
          d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
          c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
          b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);

          a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
          d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
          c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
          b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
          a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
          d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
          c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
          b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
          a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
          d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
          c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
          b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
          a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
          d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
          c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
          b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);

          a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
          d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
          c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
          b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
          a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
          d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
          c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
          b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
          a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
          d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
          c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
          b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
          a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
          d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
          c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
          b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);

          a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
          d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
          c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
          b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
          a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
          d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
          c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
          b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
          a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
          d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
          c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
          b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
          a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
          d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
          c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
          b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);

          a = safe_add(a, olda);
          b = safe_add(b, oldb);
          c = safe_add(c, oldc);
          d = safe_add(d, oldd);
        }
        return Array(a, b, c, d);
      }

      /*
       * These functions implement the four basic operations the algorithm uses.
       */
      function md5_cmn(q, a, b, x, s, t) {
        return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
      }

      function md5_ff(a, b, c, d, x, s, t) {
        return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
      }

      function md5_gg(a, b, c, d, x, s, t) {
        return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
      }

      function md5_hh(a, b, c, d, x, s, t) {
        return md5_cmn(b ^ c ^ d, a, b, x, s, t);
      }

      function md5_ii(a, b, c, d, x, s, t) {
        return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
      }

      /*
       * Add integers, wrapping at 2^32. This uses 16-bit operations internally
       * to work around bugs in some JS interpreters.
       */
      function safe_add(x, y) {
        var lsw = (x & 0xFFFF) + (y & 0xFFFF);
        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
        return (msw << 16) | (lsw & 0xFFFF);
      }

      /*
       * Bitwise rotate a 32-bit number to the left.
       */
      function bit_rol(num, cnt) {
        return (num << cnt) | (num >>> (32 - cnt));
      }

      return hex_md5(str);
    };



    module.exports =  {
      md5 : md5
    };


});


define("msg.js", function (require, exports, module) {
    'use strict';

    var Msg = function () {
        return {
            stack : {},  // 事件

            on : function (eventName, callback) {
                if (!eventName || !_.isFunction(callback)) {
                    throw new Error('事件注册参数错误！');
                }

                this.stack[eventName] = this.stack[eventName] || [];
                this.stack[eventName].push(callback);
            },

            fire : function (eventName, params) {
                if (!_.isString(eventName)) {
                    throw new Error('事件触发参数错误！');
                }
                var args = [].slice.call(arguments, 1); // 获取参数
                var events = this.stack[eventName];
                _.each(events, function (callback) {
                    callback.apply(null, args);
                });
            }

        }
    }

    module.exports = new Msg();

});/**
 * jsonp方法
 * 供不在线上环境开发时测试使用
 */
define("jsonp.js", function (require, exports, module) {


    module.exports = function (conf) {
        var jsonpCallback = 'test' + (+new Date());
        // var params = conf.params || {};
        // params['jsonp'] = jsonpCallback;
        var timerHandler = null;

        /**
        * JSONP的函数,如果请求到的数据为空，则执行error函数
        */
        window[jsonpCallback] = function (json) {

            //如果请求成功则清除超时计时器
            window.clearTimeout(timerHandler);
            $('#' + jsonpCallback).remove();
            if (json === null) {

                if (_.isFunction(conf.error)) {
                    conf.error.call(conf.context);
                }

            } else {

                if (_.isFunction(conf.success)) {
                    conf.success.call(conf.context, json);
                }
            }
        };

        // use script tag send a request, imitate ajax
        var script = document.createElement('script');

        //下面的函数在script的onerror的时候或者是请求超时时
        //执行，因为script的onerror不兼容IE9-
        var isOnerrorTrigger = _.once(function () {
            if (_.isFunction(conf.error)) {
                conf.error.call(conf.context);
            }
        });

        //拼接script的请求地址
        script.type = 'text/javascript';
        script.id = jsonpCallback;
        // var src = url + '?josnp=' + jsonpCallback + '?';
        var src = conf.url.split('?')[0]+ '?jsonp=' + jsonpCallback + '&' +  conf.url.split('?')[1];
        script.src = src.substr(0, 1900);
        script.charset = 'utf-8';
        script.onerror = function () {
            isOnerrorTrigger();
        };
        document.getElementsByTagName('head')[0].appendChild(script);

        // timing starts
        var timeoutHandler = function () {
            //console.log('sorry! is time out '+constant.timeout);
            window[jsonpCallback] = function () {};
            isOnerrorTrigger();
        };

        timerHandler = window.setTimeout(timeoutHandler, 1 * 1000);

    };
});
 /**
 * 收藏夹数据模块
 * @author  zhulin
 * @date 2014-4-11
 * 包括以下逻辑：
 *     对收藏项目数据的请求、本地存储、增删改查；
 *     降价提醒的数据本地增删改；网络同步和具体的交互在depricedReminder.js中
 */
define("data.js", function (require, exports, module) {


    var md5 = require('code.js').md5;
    var msg = require('msg.js');

    var data = module.exports;

    // ie67的时候，替换base64图片为默认图片
    var isIE67 = false;

    if ($.browser.msie && ($.browser.version === '7.0' || $.browser.version === '6.0')) {
        isIE67 = true;
    }

    var DEFAULT_ITEM_IMAGE = 'http://shared.ydstatic.com/gouwuex/images/extension_2_3/default-img.png';

    function replaceBase64 (item) {
        if (item.img.slice(0, 4) === 'http') return;
        item.img = DEFAULT_ITEM_IMAGE;
    }

    // -------------------------------------------------------------------------
    // 配置 start
    var URL_BASE = 'http://zhushou.huihui.cn/api/';
    var DEFAULT_INIT_DATA_COUNTS = 5; // 首次加载默认的加载数目
    var DEFAULT_PAGE_COUNTS = 20; // 每页的条目数目

    // 本地数据存储
    data.list = [];
    data.pricelowlist = [];
    data.promotionlits = [];

    data.allsize = undefined;  // 服务端所有收藏的数目
    data.depricedsize = undefined; // 记录derpiced的项目数目
    data.promotionsize = undefined; // 记录有促销的数目
    data.priceLowSize = undefined;  // !!!该数目只用于记录与上次访问相比 服务端所有收藏有降价的数目 并非depried接口返回的数据数目

    // 个人信息相关
    data.personal = {
        userEmail : undefined// 用户降价提醒使用的邮箱
    };

    var supportedType = ['all', 'depriced', 'promotion', 'depricedReminder'];
    var deleteAPI = 'delurls';
    // 不同类型需要的信息
    data.ref = {
        'all' : {
            api : 'myzhushou/collection/list',
            deleteApi : 'myzhushou/collection/delurls',
            target : 'list',
            apiTarget : 'list',
            apiSizeName : 'allsize',
            localSizeName : 'allsize'
        },
        'depriced' : {
            api : 'myzhushou/collection/pricelowlist',
            deleteApi : 'myzhushou/collection/delurls',
            target : 'pricelowlist',
            apiTarget : 'list',
            apiSizeName : 'allsize',
            localSizeName : 'depricedsize'
        },
        'promotion' : {
            api : 'myzhushou/collection/promotionlist',
            deleteApi : 'myzhushou/collection/delurls',
            target : 'promotionlist',
            apiTarget : 'list',
            apiSizeName : 'allsize',
            localSizeName : 'promotionsize'
        },
        'depricedReminder' : {
            api : 'pricehub/get_detail_subs',
            target : 'depricedReminderList',
            apiTarget : 'items',
            apiSizeName : 'total',
            localSizeName : 'depricedReminderSize'
        }
    };

    var failedReq = [];


    // 配置 end
    // -------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // 私有方法 start

    /**
     * 向服务端请求数据
     */
    // 供收藏夹api使用的参数
    var colletionsDefaultParam = {
        // 从0开始
        'start' : 0,
        'length' : DEFAULT_INIT_DATA_COUNTS
    };

    var _requestData = function (type, startId, n, callback) {

        if (!_.contains(supportedType, type)) {
            throw new Error('获取数据的类型错误');
        }

        var url;
        var params;
        switch(type) {
            case 'all':
            case 'depriced':
            case 'promotion':
                // 拼出来url
                params = _.clone(colletionsDefaultParam);
                params.start = startId;
                params.length = n;

                var temp = _.reduce(_.keys(params), function (memo, key) {
                    return memo  + key + '=' + params[key] + '&';
                }, '')

                url = URL_BASE + data.ref[type].api + '?' + temp.slice(0, temp.length - 1);

                break;
            case 'depricedReminder':
                // 降价提醒
                // 只提供page一个接口 page=1 对于0-9数据 page=2 对应10-19数据
                params = {
                    page : Math.ceil(startId/9)
                };
                var page = Math.ceil(startId/9);
                url = URL_BASE + data.ref[type].api + '?' + 'page=' + page;

                break;
        }

        // 增加时间戳
        url += '&t=' + (new Date().getTime());

        // jsonp 供浏览器无法跨域时进行本地测试
        var ajax = require('jsonp.js');
        ajax({
            url : url,
            success : function (res) {
                callback(res);
            },
            error : function (res) {
                callback('net-error');
            }
        });

        // $.ajax({
        //     url : url,
        //     dataType : 'json',
        //     timeout : 5000,
        //     success : function (res) {
        //         // // 模拟猜你喜欢数据
        //         // var tempData = require('./guess.js');
        //         // var list = res.list;
        //         // _.each(list, function (item) {
        //         //     item.recmds = item.recmds ? item.recmds : tempData.list;
        //         // });

        //         // debugger;
        //         callback(res);
        //     },
        //     error : function (err) {
        //         callback('net-error');
        //         failedReq.push([type, startId, n, function (res) {
        //             callback(res);

        //         }]);
        //         console.log('服务请求出错', err);
        //     }
        // });


    };


    /**
     * 使用请求回来的数据更新现有本地存储
     * @param  {[type]} rawData 原始数据
     * @return {[type]}         [description]
     */
    var _updateLocal = function (rawData, type, startId, callback) {

        if (!_.isObject(rawData) || !_.isString(type)) {
            throw new Error('update 本地数据的参数出错');
        };

        var localSizeName = data.ref[type].localSizeName;
        var apiSizeName = data.ref[type].apiSizeName;
        var curListName = data.ref[type].target;
        var apiTarget = data.ref[type].apiTarget;

        if (type === 'all') {
            // 全部接口中带有需要高亮提示用户的降价数目和促销数目
            data.priceLowSize = rawData.priceLowSize;
        }

        if (isIE67) {
             _.each(rawData[apiTarget], function (item) {
                replaceBase64(item);

             });
        }

        if (data[localSizeName] < rawData[apiSizeName]) {
            // 返回来的数据中如果allsize有增加而非减少 说明用户有收藏新商品
            // 数目减少无需处理是因为在删除的回调中应该已经做过删除数据的处理
            // 数目增加的话，需要额外请求一次数据，补足队列首
            var increasedNum = rawData[apiSizeName] - data[localSizeName];
            _requestData(type, 0, increasedNum, function (raw) {
                data[curListName].length = raw[apiSizeName];
                [].splice.apply(data[curListName], [0, increasedNum].concat(raw[apiTarget]));
                msg.fire('updateMaxPageNum', Math.ceil(rawData[apiSizeName] /DEFAULT_PAGE_COUNTS), type);
                callback();
            });
        } else {
            [].splice.apply(data[curListName], [startId, rawData[apiTarget].length].concat(rawData[apiTarget]));
            msg.fire('updateMaxPageNum', Math.ceil(rawData[apiSizeName] /DEFAULT_PAGE_COUNTS), type);
            callback();
        }


    };

    /**
     *  从本地数据中获取指定类型，开始id，长度的数据
     */
    var _getLocalData = function (type, startId, n) {
        var curListName = data.ref[type].target;
        return data[curListName].slice(startId,  n + startId);
    };


    /**
     * 删除指定列表里的指定项
     * @param  {[type]} list [description]
     * @param  {[type]} urls [description]
     * @return {[type]}      [description]
     */
    var _deleteItemsInList = function (type, urls) {
        var inx, curItem;
        _.each(urls, function (url) {
            curItem = _.findWhere(data[data.ref[type].target], {
                        url : url
                    });
            if (_.isObject(curItem)) {
                inx = _.indexOf(data[data.ref[type].target], curItem);
                data[data.ref[type].target].splice(inx, 1);
                data[data.ref[type].localSizeName]--; // 更新本地数目
                msg.fire('updateMaxPageNum', Math.ceil(data[data.ref[type].localSizeName] / DEFAULT_PAGE_COUNTS), type); // 更新最大的页码
            }
        });
    };


    var _initialDataRequest = function (type, startId, n, callback) {
        var curListName = data.ref[type].target;
        var apiTarget = data.ref[type].apiTarget;
        var apiSizeName = data.ref[type].apiSizeName;
        var localSizeName = data.ref[type].localSizeName;

        _requestData(type, startId, n, function (rawData) {

            // 测试无数据的情况
            // if (type === 'depriced') {
            //     rawData[apiSizeName] = 0;
            //     rawData.list = [];
            // }

            if (rawData === 'net-error') {
                // 网络错误
                msg.fire('offline', type);
                return;
            }

            //!! 降价提醒的返回值内没有login类型
            if (type === 'all' && !rawData.login) {
                // 未登录状态下的请求只返回{login:false};
                msg.fire('noLogin');
                return;
            }

            if (!_.isNumber(rawData[apiSizeName])) {
                // throw new Error('数据返回的清单长度类型错误');
                // 无清单的认定为是没有收藏
                rawData[apiSizeName] = 0;
                rawData[apiTarget] = [];
            }

            data[localSizeName] = rawData[apiSizeName];
            data[curListName] = [];

            // 初始化的时候即按照服务端数据生成同等长度的数组
            // 空着的数据使用undefined占位
            data[curListName].length = rawData[apiSizeName];

            if (type === 'all') {
                // rawData.priceLowSize = 3;
                // rawData.promotionSize = 3;
                // rawData.pricehubSize = 3;
                // rawData.used = false;
                msg.fire('setTipNumber', 'depricedNumber', rawData.priceLowSize);
                msg.fire('setTipNumber', 'promotionNumber', rawData.promotionSize);
                msg.fire('setTipNumber', 'depricedReminderNumber', rawData.pricehubSize);
                if (rawData.used) {
                    msg.fire('showJifenGuide', rawData.jifenUrl);
                } else {
                    msg.fire('showNewUserGuide', rawData.jifenUrl);
                }
            }

            if (type === 'depricedReminder') {
                // rawData.descendNum = 3;
                // rawData.reachExpectNum = 3;
                // 降价提醒的数字
                msg.fire('setTipNumber', 'descendNum', rawData.descendNum);
                msg.fire('setTipNumber', 'reachExpectNum', rawData.reachExpectNum);
                // 设置降价提醒的邮箱地址
                msg.fire('setEmailData', rawData.email);
            }

            _updateLocal(rawData, type, startId, function () {
                if (rawData[apiSizeName] === 0) {
                    callback('none');
                } else {
                    callback(_getLocalData(type, startId, n));
                }
            });

        });
    };

    var _getAndUpdateLocalData = function (type, startId, n, callback) {
        var temp = _getLocalData(type, startId, n);

        // 判断本地数据中该段数据是否存在undefined
        // 若存在重新向服务端请求本段数据
        var hasUndefined = false;
        // 此处不适用原生的迭代方法，包括在此基础上的underscore some方法
        // 因按照标准 undefined的数组项在迭代的时候会被跳过
        for (var i = 0, l = temp.length; i < l;i++) {
            if (temp[i] === undefined) {
                hasUndefined = true;
                break;
            }
        }

        if (hasUndefined) {
            // 如果该区段里有空的数据；
            // 发起请求更换现有数据
            _requestData(type, startId, n, function (rawData) {

                if (rawData === 'net-error') {
                    // 网络错误
                    msg.fire('offline', type);
                    return;
                }

                _updateLocal(rawData, type, startId, function () {
                    callback(_getLocalData(type, startId, n));
                });
            });
        } else {
            // 数据完整 直接返回给回调执行
            callback(temp);
        }

    };

    // 私有方法 end
    // -------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // 暴露的接口 start

    /**
     * 视图获取数据的方法：
     * @param  {[type]}   type      [all | depriced]
     * @param  {[type]}   startId  [description]
     * @param  {[type]}   n         数据个数
     * @description  包括获取数据、本地更新数据、执行回调
     *               回调获得的数据只包括其必须的数据列表 不接触到原数据
     */
    data.getData = function (type, startId, n, callback) {
        var curListName = data.ref[type].target;
        var apiTarget = data.ref[type].apiTarget;
        var apiSizeName = data.ref[type].apiSizeName;
        var localSizeName = data.ref[type].localSizeName;

        if (!_.isFunction(callback)) {
            callback = $.noop;
        };

        if (data[localSizeName] === 0) {
            // 无收藏数据
            callback('none');
            return false;
        }

        if (data[localSizeName] === undefined) {
            // 首次请求该类型数据
            _initialDataRequest(type, startId, n, callback);

        } else {
            // 本地已经有该类型的数据
            // 获取本地数据 若本地数据该区段不完整 则向服务端请求该区段数据
            _getAndUpdateLocalData(type, startId, n, callback);
        };
    };

    /**
     * 删除指定数据
     * @param  {[url1, url2, ...]}   ids      实际上是对应的url列表
     * @param  {Function} callback [description]
     * @return {[type]}            [description]
     */
    data.deleteData = function (type, urls, callback) {

        if (!_.isArray(urls)) {
            if (_.isString(urls)) {
                urls = [urls]; // 支持传入单个url 自动转为数组
            } else {
                throw new Error('deletData 参数类型错误！');
            }
        }

        // 订阅类的网路同步单独处理 这里只处理收藏夹数据
        if (type === 'depricedReminder') {
            _deleteItemsInList('depricedReminder', urls);
            if (_.isFunction(callback)) {
                callback(urls);
            }
            return;
        }

        var deleteAPI = data.ref[type].deleteApi

        var resultUrl = '';
        var md5Urls = (_.map(urls, function (url) {
                    return md5(url);
                })).join('|');
        resultUrl = URL_BASE + deleteAPI + '?' + 'allmd5=' + md5Urls;
        // 成功的回调里：
        // 删除时需要同时更新两个列表

        // jsonp 供浏览器无法跨域时进行本地测试
        var ajax = require('jsonp.js');
        ajax({
            url : resultUrl,
            success : function (res) {
                _deleteItemsInList('all', urls);
                _deleteItemsInList('depriced', urls);
                _deleteItemsInList('promotion', urls);
                callback();
            },
            error : function (res) {
                callback('net-error');
            }
        });
        // $.ajax({
        //     url : resultUrl,
        //     dataType : 'json'
        // }).done(function (res) {
        //     alert(2);
        //     // 删除两个列表里的该项数据
            // _deleteItemsInList('all', urls);
            // _deleteItemsInList('depriced', urls);
            // _deleteItemsInList('promotion', urls);
        //     callback();
        // });
    };

    /**
     * 重新发起之前失败的请求
     */
    data.retry =  function () {
        var params;
        do {
            params = failedReq.shift();
            _requestData.apply(this, params);
        } while (failedReq .length !== 0)
    };


    /**
     * 个人数据
     */
    data.userInfo = {
        getDepricedEmail : function () {
            return data.personal.userEmail;
        },
        changeDepricedEmail : function (email) {
            data.personal.userEmail = email;
        }
    };

    // 初始化：
    // 首次加载时向服务端请求默认10条数据，同时获取所有数据个数和降价了的数目
    data.init = function (callback) {
        data.getData('all', 0, DEFAULT_INIT_DATA_COUNTS, function () {
            callback();
            // data.getData('depricedReminder', 0);
        });
    }

    // 暴露的接口end
    // -------------------------------------------------------------------------
    window.data = data;

});
define("log.js", function (require, exports, module) {


    var URL = 'http://zhushou.huihui.cn/log?';
    var LOG_TYPE = 'ARMANI_EXTENSION_PAGE';

    var img = new Image();

    var defaultParams = {
        action: 'click',
        type: LOG_TYPE
    };

    var _sendLog = function (logAction, logType, position) {
        var params = _.clone(defaultParams);
        params.action = logAction;

        if (logType) {
            params.type = logType;
        }

        if (position) {
            params.position = position;
        }

        var url = URL;
        for (var pro in params) {
            if (params.hasOwnProperty(pro)) {
                url = url + pro + '=' + params[pro] + '&';
            }
        }
        url = url.slice(0, url.length - 1);
        img.src = url;
    };


    // 注册点击事件
    $(document).delegate('[clkAction]', 'click', function (e) {
        var logAction = $(this).attr('clkAction');
        var position = $(this).data('position');
        _sendLog(logAction, LOG_TYPE, position);
    });

    // 注册hover事件
    $(document).delegate('[hovAction]', 'hover', function (e) {
        var logAction = $(this).attr('hovAction');
        var position = $(this).data('position');
        _sendLog(logAction, LOG_TYPE, position);
    });


    module.exports = function (logAction, logType, position) {
        _sendLog(logAction, logType, position);
    };

});
define('popout/index.html', function () { return '  <div class="pop-out">\r\n    <div class="pop-out-hd">\r\n        <div class="pop-out-line"></div>\r\n        <div class="pop-out-title"><%=pop.title%></div>\r\n        <span class="pop-out-close">X</span>\r\n    </div>\r\n    <div class="pop-out-bd">\r\n        <div class="pop-out-contents">\r\n            <%=pop.contentsHtmlString%>\r\n        </div>\r\n        <div class="pop-out-btns  <%= (pop.hasCancel ? \'has-cancel\' : \'\') %>">\r\n            <span class="pop-out-btn-confirm">确&nbsp;定</span>\r\n            <span class="pop-out-btn-cancel">取&nbsp;消</span>\r\n        </div>\r\n    </div>\r\n</div>';});
define("popout/index.js", function (require, exports, module) {
    'use strict';

    var isIE6 = false;;
    if ($.browser['msie'] && $.browser['version'] === '6.0') {
        isIE6 = true;
    }


    var tmpl = _.template(require('popout/index.html'));

    var defaultConf = {

        containerId : 'box',
        containerClass : '',

        posTop : 200,
        posLeft : 200,

        title : '...',
        contents : '<p>...</p>',

        minHeight : '300',

        hasCancel : false, // 是否有取消按钮

        callbackAfterPopOut : function () {
            // alter('被关掉了！_(￣0￣)_[哦~]');
        },

        cbAfterCancel : function () {
            // 关闭或者取消的回调
            // console.log('关闭弹窗');
        }
    };

    /**
     * 遮罩层渲染方法
     * @return {[type]} [description]
     */
    var mask = {

        $el : $('#mask'),

        show : function () {
            if (this.$el.length === 0) {
                this.$el = $('<div id="mask"></div>');

                this.$el.css({
                    position : isIE6 ? 'absolute' : 'fixed',
                    top : '0',
                    left: '0',
                    height : $(window).height(),
                    width : $(window).width(),
                    'z-index' : 10
                });
                this.$el.appendTo($('body'));
            }

            this.$el.show();
        },

        hide : function () {
            this.$el.hide();
        }
    };



    var Pop = function (conf, eventsConf) {
        var me = this;

        me.$box = $('<div></div>');
        me.conf = _.extend(defaultConf, conf);

        me.init(me.conf, eventsConf);

        return me;
    };

    var popProto = Pop.prototype;

    var _eventsBinder= function (eventsConf, el) {

        if (!_.isObject(eventsConf)) {
            throw new Error('事件绑定参数错误');
        }

        if (el === undefined) {
            // 默认绑定到document上
            el = document;
        }

        var eventType, target, callback;
        for (var evnt in eventsConf) {
            if (eventsConf.hasOwnProperty(evnt)) {
                callback = eventsConf[evnt];
                target = evnt.split(' ');
                eventType = target.shift();
                target = target.join(' ');

                $(el).on(eventType, target, callback);
            }
        }

    };

    popProto.init = function (conf, events) {

        var me = this;

        me.events = _.extend({
            'click .pop-out-close' : function () {
                me.remove();
                conf.cbAfterCancel('CLOSE');
            },
            'click .pop-out-btn-confirm' : function () {
                me.remove();
            },
            'click .pop-out-btn-cancel' : function () {
                me.remove();
                conf.cbAfterCancel('CANCEL');
            },
            'keydown input' : function (e) {
                if (e.keyCode !== 13) return;
                me.$box.find('.pop-out-btn-confirm').click();
            }
        }, events);

        me.$box.attr({
            'id' : conf.containerId,
            'class' : conf.containerClass
        }).css({
            'display' : 'none'
        });

        var pop = _.clone(defaultConf, conf);

        me.$box.html(tmpl({
            pop : pop
        }));

        // 计算位置
        me.$box.appendTo('body');
        _eventsBinder(me.events, me.$box);
        me.show();
    };


    popProto.show = function () {
        var me = this;
        // 重新计算位置；
        me.$box.css({
            'position' : isIE6 ? 'absolute' : 'fixed',
            'top' : ($(window).height() - me.$box.outerHeight()) / 2,
            'left' : ($(window).width() - me.$box.outerWidth()) / 2,
            'z-index' : 20
        });

        if (!isIE6) {
            mask.show();
        }

        this.$box.show();
    };
    popProto.hide = function () {
        mask.hide();
        this.$box.hide();
    };
    popProto.remove = function () {
        mask.hide();
        this.$box.remove();
    };

    popProto.updateContent = function (contentsHtmlString) {
        this.$box.find('.pop-out-contents').html(contentsHtmlString);
    };


    module.exports = Pop;

});
/**
 * 降价提醒相关接口
 * 目前只做了修改邮箱；删除订阅
 * TODO：
 *     修改单个订阅设置；
 *     增加订阅
 */
define("depricedReminder.js", function (require, exports, module) {
    'use strict';
    var dr = module.exports;
    var Box = require('popout/index.js');

    // 邮箱设置弹框
    var settingEmailBox;
    var settingBoxID = 'settingEmail';
    var msg = require('msg.js');

    var URL_BASE = 'http://zhushou.huihui.cn/api/';
    var apis = {
        setEmail : 'pricehub/setemail',
        cancelItem : 'pricehub/del',
        cancelItems : 'pricehub/del_subs'
    };

    var EMAIL_REG = /^([a-zA-Z0-9_-]|\.)+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,5}){1,4})$/;


    /**
     * 发送请求
     * @param  {[type]} type   setEmail | cancelItem
     * @param  {[type]} params [description]
     */
    var _sendRequest = function (type, params, callback) {
        var url;
        var api = apis[type];
        var paramstring = '';
        _.each(params, function (value, key) {
            paramstring += key + '=' + value + '&';
        });

        // 加时间戳
        paramstring += 't=' + (new Date().getTime()) + '&';

        paramstring = paramstring.slice(0, paramstring.length - 1);
        url = URL_BASE + api + '?' + paramstring;
        $.ajax({
            url : url,
            dataType : 'json'
        }).done(function (res) {
            if (_.isFunction(callback)) {
                callback(res);
            }
        }).fail(function (err) {
            console.log(url, err, 'fail');
        });
    };

    // 修改订阅邮箱的内部模板
    var tmplString = [
    '<div id="modifyEmail">',
        '<label for="depricedEmailInput">修改邮箱：</label>',
        '<input type="text" id="depricedEmailInput" name="depricedEmailInput" value="' + '<%=local.email%>' + '"/>',
        '<p class="error">*请填写正确的邮箱地址</p>',
    '</div>'
    ].join('');
    var emailInputTmpl = _.template(tmplString);

    /**
     * 修改订阅邮箱
     */
    dr.modifyEmail = function (originalEmail) {
        if (settingEmailBox !== undefined) {
            if ($('#' + settingBoxID + ':visible').length !== 0) {
                // 已经弹出
                return;
            }
            // 更新内容
            settingEmailBox.updateContent(emailInputTmpl({
                local : {
                    email : originalEmail
                }
            }));

            settingEmailBox.show();
        } else {
            // 首次弹出该窗口
            // 进行创建&渲染
            settingEmailBox = new Box({
                'containerId' : settingBoxID,
                'title' : '设置接收邮箱',
                'contentsHtmlString' : emailInputTmpl({
                    local : {
                        email : originalEmail
                    }
                })
            }, {
                'click .pop-out-btn-confirm' : function () {
                    // 提交修改
                    var email = $('#depricedEmailInput').val().replace(/\s/g, '');
                    if (email !== "" && EMAIL_REG.test(email)) {
                        // 验证通过
                        _sendRequest('setEmail', {
                            email : email
                        });
                        msg.fire('setEmailData', email);
                        settingEmailBox.hide();
                    } else {
                        settingEmailBox.$box.find('.error').show();
                    }

                }
            });
        }
    };

    /**
     * 取消订阅
     */
    dr.cancelSubscribtion = function (subscriptionData, callback) {
        var needLogin = true;
        var email = $('.modify-depriced-reminder-email').data('email');

        var sd = subscriptionData;
        _sendRequest('cancelItem', {
            email : email,
            nl : needLogin,

            id : sd.id,
            cluster : sd.iscluster
        }, function (res) {
            // 取消订阅成功
            if(_.isFunction(callback)) {
                callback(res);
            }
        });
    };

    /**
     * 批量取消订阅
     * @param  {[type]}   itemId     单品id 用逗号隔开
     * @param  {[type]}   clusterIds 聚类id 用逗号隔开
     * @param  {Function} callback   [description]
     */
    dr.cancelBundledSubcribtion = function (itemIds, clusterIds, callback) {

        _sendRequest('cancelItems', {
            itemIds : itemIds.join(','),
            clusterIds : clusterIds.join(',')
        }, function (res) {
            callback(res);
        });
    };

    /**
     * 修改单个订阅
     */
    dr.modifyDetail = function () {

    };



});define('templates/collections.html', function () { return ' <%for (var i = 0, l = list.length; i<l; i++) {%>\r\n    <%var avaliableFlag = list[i] && list[i].avaliable;%>\r\n    <li data-url="<%=list[i].url%>" class="<%=avaliableFlag ? \'\' : \'not-avalible\'%>" >\r\n        <div class="item-detail">\r\n            <input type="checkbox" class="select">\r\n            <a href="<%=list[i].cpsUrl%>" target="_blank" class="item-img"  clkAction="FAVOR_ITEM_COLLECT_CLICK" data-position="IMG">\r\n                <%if (list[i].img !== undefined) {%>\r\n                    <img src="<%=list[i].img%>" alt="<%=list[i].title%>">\r\n                <%}%>\r\n            </a>\r\n            <div class="item-content">\r\n                <a href="<%=list[i].cpsUrl%>" clkAction="FAVOR_ITEM_COLLECT_CLICK" data-position="TITLE" target="_blank" class="title"><%=list[i].    title%></a>\r\n\r\n                <p class="shop-price"><span class="shop"><%=list[i].domainName%></span>：<span class="price"><%=(list[i].curPrice === \'-1\') ? \'暂无价格\' : list[i].curPrice + list[i].priceunit%></span></p>\r\n                <%if (!avaliableFlag) {%>\r\n                    <p class="not-avalible">无货</p>\r\n                <%} else {%>\r\n                    <%if (list[i].lastSecondPrice !== -1 && list[i].lastSecondPrice - list[i].curPrice > 0) {%>\r\n                        <p class="deprice"><span class="icon icon-deprice"></span>价格下降<span class="depriced-number"><%=Math.floor((list[i].lastSecondPrice - list[i].curPrice) * 100 ) / 100%><%=list[i].priceunit%></span></p>\r\n                    <%}%>\r\n                    <%if (list[i].promotionInfo !== undefined) {%>\r\n                        <p class="promotion"><span class="icon icon-promotion"></span><%=list[i].promotionInfo%></p>\r\n                    <%}%>\r\n                <%}%>\r\n            </div>\r\n            <div class="item-ops">\r\n                <a class="go-to-item-page" href="<%=list[i].cpsUrl%>" target="_blank"  clkAction="FAVOR_ITEM_COLLECT_CLICK" data-position="BTN">去看看</a>\r\n                <a id="deleteCurItem" class="delete" href="javascript:;" clkAction="FAVOR_ITEM_DEL_CLICK">删除收藏</a>\r\n            </div>\r\n        </div>\r\n        <%var recmds = list[i].recmds;%>\r\n        <%if (recmds !== undefined) {%>\r\n            <div class="items-recmds">\r\n                <span class="prev prev-disable" data-btn-type="prev"  clkAction="FAVOR_ITEM_RECPAGECHANGE_CLICK" data-position="PREV"></span>\r\n            <div class="contents-wrapper">\r\n                <div class="recom-contents">\r\n                    <%for (var j = 1, gl = recmds.length;j <= gl;j++) {%>\r\n                        <%var recoItem = recmds[j - 1];%>\r\n                        <%if (/^DISCOUNT.+/g.test(recoItem.type)) {%>\r\n                            <span class="recom-content">\r\n                                <span class="icon special-offer-tip"></span>\r\n                                <a clkAction="FAVOR_ITEM_REC_CLICK" target="_blank" href="<%=recoItem.url%>">\r\n                                    <img src="<%=recoItem.imgUrl%>" title="<%=recoItem.title%>">\r\n                                </a>\r\n                                <span class="intro">\r\n                                    <span class="icon icon-depriced"></span><span class="cur-price"><%=recoItem.price%><%=list[i].priceunit%></span>\r\n                                    <span class="ori-price"><%=recoItem.maxPrice%><%=list[i].priceunit%></span>\r\n                                </span>\r\n                            </span>\r\n                        <%} else if (/^ITEMCF.+/g.test(recoItem.type)) {%>\r\n                            <span class="recom-content">\r\n                                <a clkAction="FAVOR_ITEM_REC_CLICK" target="_blank" href="<%=recoItem.url%>">\r\n                                    <img src="<%=recoItem.imgUrl%>" title="<%=recoItem.title%>">\r\n                                </a>\r\n                                <span class="intro">\r\n                                    <span class="cur-price"><%=recoItem.price%><%=list[i].priceunit%></span>\r\n                                    <span class="people-counts"><%=recoItem.covisitNum%>人还看</span>\r\n                                </span>\r\n\r\n                            </span>\r\n                        <%} else if (/^BRAND.+/g.test(recoItem.type)) {%>\r\n                            <!-- 品牌特卖 -->\r\n                        <%}%>\r\n                    <%}%>\r\n                    <span class="recom-content">\r\n                        <p class="no-more-recommand">暂无更多</p>\r\n                    </span>\r\n                </div>\r\n            </div>\r\n                <span class="next <%=recmds.length >= 3 ? \'\' : \'next-disable\' %>"  data-btn-type="next" clkAction="FAVOR_ITEM_RECPAGECHANGE_CLICK" data-position="NEXT"></span>\r\n            </div>\r\n        <%} else {%>\r\n            <p class="no-recommands">暂无你可能喜欢的商品推荐~</p>\r\n        <%}%>\r\n    </li>\r\n<%}%>';});
define('templates/depricedReminder.html', function () { return ' <%for (var index = 0, length = list.length;index < length; index++){%>\r\n    <li class="deprice-item"  data-url="<%=list[index].url%>" data-id="<%=list[index].itemid%>" data-iscluster="<%=list[index].isCluster%>">\r\n        <input type="checkbox" class="select">\r\n        <a clkAction="PRICEALERT_ITEM_CLICK" data-position="IMG" target="_blank" href="<%=list[index].url%>" class="item-img">\r\n            <img src="<%=list[index].imgUrl%>" alt="<%=list[index].title%>">\r\n        </a>\r\n        <div class="item-detail">\r\n            <a  clkAction="PRICEALERT_ITEM_CLICK" data-position="TEXT"  target="_blank" href="<%=list[index].url%>" class="title"><%=list[index].title%></a>\r\n            <p class="origin-price">原价：<span class="price"><%=list[index].originalPrice%><%=list[index].priceunit%></span></p>\r\n        </div>\r\n        <div class="current-price">\r\n            <p class="price"><%=list[index].price%><%=list[index].priceunit%></p>\r\n            <%if (list[index].isDescend && list[index].descendPrice > 0) {%>\r\n                <p class="depriced"><span class="icon icon-deprice"></span>价格下降<span class="depriced-number"><%=list[index].descendPrice%><%=list[index].priceunit%></span></p>\r\n            <%}%>\r\n            <%if (list[index].isReachExpect) {%>\r\n                <p class="reach-expect">达到目标价格</p>\r\n            <%}%>\r\n        </div>\r\n        <div class="changes">\r\n            <ul>\r\n                <%for (var i = 0, l = list[index].priceInfoList.length; i<l;i++) {%>\r\n                    <li class="<%= i === 0 ? \'cur\' : \'\'%>"><%=list[index].priceInfoList[i]%></li>\r\n                <%}%>\r\n            </ul>\r\n            <%if (list[index].priceInfoList.length === 0 ) {%>\r\n                <span class="no-price-info-list" >...</span >\r\n            <%}%>\r\n        </div>\r\n        <div class="ops">\r\n            <!-- <a href="javascript:;">修改设置</a> -->\r\n            <a href="javascript:;"  clkAction="PRICEALERT_ITEM_DEL_CLICK"  class="cancel-subscription">取消订阅</a>\r\n        </div>\r\n    </li>\r\n<%}%>';});
define('templates/bundleOps.html', function () { return ' <div class="bundle-ops all-ops">\r\n    <label class="select-all" for="selectAll" >\r\n        <input class="selectAll" name="selectAll" type="checkbox">全选\r\n    </label>\r\n    <a href="javascript:;" id="deleteSelected" class="delete-selected">\r\n        <span class="icon icon-delete"></span>\r\n    删除</a>\r\n</div>\r\n<div id="hdPageControl" class="page-indecators-container page-indecators-hd" data-sourc"hd">\r\n    <a href="javascipt:;"  data-inx="prev" id="hdPrevPage" class="page-btn prev">上一页</a>\r\n    <a href="javascipt:;"   data-inx="next" id="hdNextPage" class="page-btn next">下一页</a>\r\n</div>';});
define('templates/noCollection.html', function () { return ' <!-- 收藏夹为空 -->\r\n<div class="collection-empty">\r\n    <p>您还没有收藏任何商品哦! <a style="display:none;" id="jifenGuideNoCollection" target="_blank" href="">首次使用获积分活动正在进行中！</a></p>\r\n    <p>无需登录各电商，只要在商品页面点击“<span>收藏</span>”按钮，就可以时刻查看商品动态啦！</p>\r\n    <img src="images/myspace/example.png" alt="助手收藏">\r\n</div>';});
define('templates/noDepricedItem.html', function () { return ' <!-- 无降价 -->\r\n<div class="collection-no-deprice">\r\n    <p>暂时没有商品降价哦！</p>\r\n    <p>您还可以到商品页面，订阅商品的降价提醒，第一时间获知价格变化！</p>\r\n</div>\r\n';});
define('templates/noPromotion.html', function () { return ' <!-- 无促销 -->\r\n<div class="collection-no-promotion">\r\n    <p>暂时没有促销哦！</p>\r\n</div>\r\n';});
define('templates/noLogin.html', function () { return ' <!-- {noLogin : { loginUrl : \'http://www.huihui.cn/login?url=http%3A%2F%2Fzhushou.huihui.cn\'}} -->\r\n<p class="no-login">查看收藏的商品，请先<a href="<%=noLogin.loginUrl%>">登录</a></p>';});
define('templates/loading.html', function () { return ' <p class="loading-tip"><img src="http://zhushou.huihui.cn/images/myspace/loading.gif" alt="">正在加载ing</p>';});
define('templates/offLine.html', function () { return ' <p class="offline"><span class="icon icon-fail"></span>加载失败 <a href="javascript:;" class="retry">重试</a></p>';});
define('templates/pageIndicators.html', function () { return ' \r\n<!--         var conf = {\r\n            startInx : startInx,\r\n            endInx : endInx,\r\n            hasFirstFolder : hasFirstFolder,\r\n            hasLastFolder : hasLastFolder,\r\n\r\n            pagesCounts : maxPage,\r\n            curPage : pageIndex // 当前页\r\n        };\r\n -->\r\n<div data-source="ft" class="page-indecators-container ft-page-indicators">\r\n    <%if (curPage !== 1) {%>\r\n        <a href="javascript:;" data-inx="prev" class="page-btn prev">&lt;</a>\r\n    <%}%>\r\n    <a href="javascript:;" data-inx="1" class="<%= (1 === curPage) ? \'cur\' : \'\'%> page-btn">1</a>\r\n    <%if (hasFirstFolder) {%>\r\n        <a href="javascript:;" class="folder">…</a>\r\n    <%}%>\r\n    <%for (var i = startInx ; i < endInx + 1; i++) {%>\r\n        <a href="javascript:;" data-inx="<%=i%>" class="page-btn <%= (i === curPage) ? \'cur\' : \'\'%>" ><%=i%></a>\r\n    <%}%>\r\n    <%if (hasLastFolder) {%>\r\n        <a href="javascript:;" class="folder">…</a>\r\n    <%}%>\r\n\r\n    <%if (1 !== pagesCounts) {%>\r\n        <a href="javascript:;"  data-inx="<%=pagesCounts%>" class="<%= (pagesCounts === curPage) ? \'cur\' : \'\'%> folder page-btn"><%=pagesCounts%></a>\r\n    <%}%>\r\n    <%if (curPage !== pagesCounts) {%>\r\n        <a href="javascript:;" data-inx="next" class="next page-btn" >&gt;</a>\r\n    <%}%>\r\n</div>';});
/**
 * 视图控制
 * @date 2014-4-11
 * @author zhulin
 */
define("view.js", function (require, exports, module) {
    'use strict';
    var msg = require('msg.js');
    var sendLog = require('log.js');
    var Box = require('popout/index.js');
    var drControl = require('depricedReminder.js'); // 降价提醒模块方法

    var view = module.exports;

    // --------------------------------------------------------------------
    // 配置项 start
    // --------------------------------------------------------------------
    // 模板引用
    var tmpls = {

        // 收藏夹条目模板，all/depriced/promitioned 共用
        collections : _.template(require('templates/collections.html')),
        // 降价提醒订阅项模板
        depricedReminders : _.template(require('templates/depricedReminder.html')),

        // bundle controls 包括上下页和全选删除
        bundleOps : _.template(require('templates/bundleOps.html')),

        // 无收藏
        noCollection : _.template(require('templates/noCollection.html')),
        // 无降价提醒订阅
        noDepricedItem : _.template(require('templates/noDepricedItem.html')),
        // 无促销
        noPromotion : _.template(require('templates/noPromotion.html')),

        // 未登录提示框
        noLogin : _.template(require('templates/noLogin.html')),

        // 正在加载
        loading : _.template(require('templates/loading.html')),

        // 网络错误
        offLine : _.template(require('templates/offLine.html')),

        // 页码控制 供所有类别使用
        pageBtns : _.template(require('templates/pageIndicators.html'))
    };


    var colllectionTabs = ['all', 'depriced', 'promotion'];
    var supportedType = ['all', 'depriced', 'promotion', 'depricedReminder'];

    // 视图参数配置
    view.ref = {
        'all' : {
            container : $('#allContainer'),
            tmpl : tmpls.collections,
            emptyTmpls : tmpls.noCollection,
            pagesCounts : undefined,
            hdControls : $('#allOps')
        },
        'depriced' : {
            container : $('#depricedContainer'),
            tmpl : tmpls.collections,
            emptyTmpls : tmpls.noDepricedItem,
            pagesCounts : undefined,
            hdControls : $('#depricedOps')
        },
        'promotion' : {
            container : $('#promotionContainer'),
            tmpl : tmpls.collections,
            emptyTmpls : tmpls.noPromotion,
            pagesCounts : undefined,
            hdControls : $('#promotionOps')
        },
        'depricedReminder' : {
            container : $('#depricedReminderContainer'),
            tmpl : tmpls.depricedReminders,
            emptyTmpls : tmpls.noDepricedItem,
            pagesCounts : undefined,
            hdControls : $('#depricedReminderOps')
        },
        containerSelector : '.list',
        loadingSelector : '.loading',
        pageBtnsContainerSelector : '.pageBtns'
    };

    var PAFE_COUNTS_IN_SHOW = 3; // 最多展示的页码按钮数目/2

    var ref = view.ref;

    // --------------------------------------------------------------------
    // 配置项 end
    // --------------------------------------------------------------------


    // --------------------------------------------------------------------
    // 私有方法 begin
    // --------------------------------------------------------------------

    var allSelectedFlag = false;// 是否当前处于全选状态

    /**
     * 批量事件绑定代理辅助函数
     * @param  {[type]} eventsConf [description]
     * {
     *     eventType selector, callback
     *     'click #selectAll' : selectAll,
     *     'click #deleteSelected' : deleteSelected,
     *     'click #deleteCurItem' : deleteCurItem,
     * }
     * @param  {selector} el  事件绑定的目标元素选择器, 或者目标元素
     * @return {[type]}            [description]
     */
    var _eventsBinder= function (eventsConf, el) {

        if (!_.isObject(eventsConf)) {
            throw new Error('事件绑定参数错误');
        }

        if (el === undefined) {
            // 默认绑定到document上
            el = document;
        }

        var eventType, target, callback;
        for (var evnt in eventsConf) {
            if (eventsConf.hasOwnProperty(evnt)) {
                callback = eventsConf[evnt];
                target = evnt.split(' ');
                eventType = target.shift();
                target = target.join(' ');

                $(el).on(eventType, target, callback);
            }
        }

    };

    /**
     * 渲染收藏夹数据
     * @param  {[type]} data [description]
     * @param  {[type]} type [all | depriced]
     * @return {[type]}      [description]
     */
    var _renderCollections = function (type, data) {

        if ( !_.isArray(data) || _.indexOf(supportedType, type) === -1) {
            // 参数类型错误
            throw new Error('返回数据不是数组！或者是填错参数啦，all或者depriced类型的啦！');
            return false;

        }

        var temp = {};
        temp.list = data || [];
        return view.ref[type].tmpl(temp);

    };


    /**
     * 渲染页码控制区域
     * @param  {[type]} cur 当前页码
     * @param  {[type]} max   最大页码
     */
    var _pageControlsRender = function (cur, max) {

        var LENGTH  = 5; // 展示数目 - 4{}
        var startInx, endInx; // 计算要展示的页码范围
        var hasFirstFolder = false,
            hasLastFolder = true; // 是否展示前后..
        // 1 和 max是肯定会展示的

        if (max <=9) {
            startInx = 2;
            endInx = max - 1;
            hasLastFolder = false;
            hasFirstFolder = false;
        } else {
            if (cur <= LENGTH) {
                startInx = 2;
                if (LENGTH + 2 >= max) {
                    endInx = max - 1;
                    hasLastFolder = false;
                } else {
                    endInx = LENGTH + 2;
                }
            } else if (cur <= max - LENGTH) {
                startInx = cur - 2;
                endInx = cur + 2;
                hasFirstFolder = true;
                hasLastFolder = true;
            } else {
                // cur > max - Length
                startInx = max - 6;
                endInx = max - 1;
                hasFirstFolder = true;
                hasLastFolder = false;
            }
        }

        hasFirstFolder = (startInx - 1 >1);
        hasLastFolder = (max - endInx >1);


        var conf = {
            startInx : startInx,
            endInx : endInx,

            hasFirstFolder : hasFirstFolder,
            hasLastFolder : hasLastFolder,

            pagesCounts : max,
            curPage : cur // 当前页
        };
        return tmpls.pageBtns(conf);
    };

    // -------------------------------------------------------------------------
    // 暴露出去供index调用的接口
    /**
     * 设置降价提醒tab按钮上的浮标数字
     */
    view.setNumber = function (selector, number, originalNumber) {
        $(selector).text(number).toggle(originalNumber > 0 );
    };


    /**
     * 初始化 渲染头部批量控制和上下页
     */
    view.init = function () {
        _.each(supportedType, function (type) {
            $('.ops' + '[data-type=' + type + ']').html(tmpls.bundleOps());
        });
        $('.ops').first().show();
        $('.go-top').hide();

    };

    view.checkContents = function (type) {
        return (ref[type].container.find('.list').children().length === 0);
    };

    /**
     * 渲染新tab
     * @param  {[type]} type    [description]
     * @param  {[type]} data    [description]
     * @param  {[type]} curPage [description]
     * @return {[type]}         [description]
     */
    view.renderNewTab = function (type, data, curPage, maxPage) {

        var listCont = ref[type].container.find(ref.containerSelector);
        var pageBtnsCont = ref[type].container.find(ref.pageBtnsContainerSelector);

        listCont.html(_renderCollections(type, data));

        pageBtnsCont.html(_pageControlsRender(curPage, maxPage));

    };

    /**
     * 渲染无数据模板
     */
    view.renderNoDataTip = function (type) {
        ref[type].container.html(view.ref[type].emptyTmpls({}));
    };

    /**
     * 追加渲染当前tab
     */
    view.appendCurTab = function (type, data) {
        var temp = $(_renderCollections(type, data));
        temp.appendTo(ref[type].container.find(ref.containerSelector));

        // 判断当前是否在全选状态
        // 全选状态的话需要自动全选后添加的项
        if (allSelectedFlag) {
            temp.find('.select').click();
        }
    };

    /**
     * 删除指定条目
     */
    view.deleteItem =  function (url) {
        var item = $('[data-url="' + url + '"]');
        item.css({
            'transition' : '.3 opacity',
            'opacity' : .5
        });
        setTimeout(function() {
            item.remove();
        }, 300);
    };

    /**
     * 更新头部页码导航状态
     */
    view.refreshPageControl = function (type, prevFlag, nextFlag) {
        var hdPageBtns = ref[type].hdControls;
        hdPageBtns.find('.next').toggleClass('disable', !nextFlag);
        hdPageBtns.find('.prev').toggleClass('disable', !prevFlag);

    };

    view.renderNoLoginTip = function () {
        var targetUrl = encodeURIComponent('http://zhushou.huihui.cn/myzhushou');
        var data = {
            noLogin : {
                loginUrl : 'http://www.huihui.cn/login?url=' + targetUrl
            }
        };

        view.ref['all'].container.html(tmpls.noLogin(data));
    };

    view.renderLoadingTip = function (type) {
        var curRef = view.ref[type];
        var loadingMod = curRef.container.find(ref.loadingSelector);
        if (loadingMod.children().length === 0 ) {
            loadingMod.html(tmpls.loading());
        } else if (loadingMod.children('.offline').length !== 0) {
            loadingMod.html(tmpls.loading()).show();
        } else {
            loadingMod.show();
        }
    };
    view.hideLoadingTip = function (type) {
        var curRef = view.ref[type];
        var loadingMod = curRef.container.find(ref.loadingSelector);
        loadingMod.hide();
    };

    view.renderOffLineTip = function (type) {
        var curRef = ref[type];
        curRef.container.find('.loading').html(tmpls.offLine());
    };

    // -------------------------------------------------------------------------
    // 注册dom事件
    // -------------------------------------------------------------------------

    /**
     * 事件配置
     */
    var domEvents = {
        selectSingleItem : function () {
            var curType = $(this).parents('.items-list').data('type');
            var $selectAll = ref[curType].hdControls.find('.selectAll');
            if (allSelectedFlag) {
                if ( $($selectAll[0].checked)) {
                    $selectAll[0].checked = false;
                    allSelectedFlag = false;
                }
            } else {
                // check 是否是全选状态
                if (_.every($('.items-list:visible .select'), function (item) {
                    return item.checked;
                })) {
                    allSelectedFlag = true;
                    $selectAll[0].checked = true;
                }

            }
        },

        /**
         * 全选或者全不选
         */
        selectAll : function (e) {
            allSelectedFlag = this.checked;
            _.each($('.items-list:visible').find('.select'), function (item) {
                item.checked = allSelectedFlag;
            });

        },

        /**
         * 删除选中项目
         */
        deleteSelected : function (e) {
            var type = $(this).parents('.ops').data('type');
            var urls = [];
            var list = ref[type].container.find('.select');
            _.each(list, function (item) {
                if (item.checked) {
                    urls.push($(item).parents('li').data('url'));
                }
            });

            if (urls.length === 0) {
                return;
            }

            // log
            sendLog('FAVOR_ITEM_SELECTDEL_CLICK');

            var $confirmBox;
            var bundleDeleteBoxId = 'bundleDeleteBoxId';
            // 弹窗确认
            $confirmBox = new Box({
                'containerId' : bundleDeleteBoxId,
                'title' : '批量删除',
                'hasCancel' : true,
                'contentsHtmlString' : '<p>确定删除所选收藏商品？</p>'
            }, {
                'click .pop-out-btn-confirm' : function () {
                    msg.fire('deleteItems', urls, function () {
                        _.each(urls, function (url) {
                            view.deleteItem(url);
                        });
                    });
                    $confirmBox.remove();
                }
            });

        },

        /**
         *  删除当前条目
         */
        deleteCurItem : function (e) {
            var url = $(this).parents('li').data('url');

            var $confirmBox = undefined;
            var deleteBoxId = 'deleteBoxId';
            // 弹窗确认
            $confirmBox = new Box({
                'containerId' : deleteBoxId,
                'title' : '删除',
                'hasCancel' : true,
                'contentsHtmlString' : '<p>确定删除所选收藏商品？</p>',
                cbAfterCancel : function(cancelMethod) {
                    // 发送取消的log
                    sendLog('FAVOR_DALDEL_CANCEL_CLICK', undefined, cancelMethod);
                }
            }, {
                'click .pop-out-btn-confirm' : function () {
                    msg.fire('deleteItems', url, function () {
                        view.deleteItem(url);
                    });
                    $confirmBox.remove();
                    sendLog('FAVOR_DALDEL_CONFIRM_CLICK');

                }
            });
        },

        /**
         *  页码控制
         */
        goToPage : function (e) {
            e.preventDefault();

            // 删除选定状态
            allSelectedFlag = false;
            $('#allOps input').attr('checked', false);

            if($(this).hasClass('disable')) return;
            var pageInx = $(this).data('inx') || '#';

            var source = $(this).parents('.page-indecators-container').data('source');
            msg.fire('goToPage', pageInx, source);

        },

        modifyEmail : function (e) {
            e.preventDefault();
            var email = $(this).data('email');
            // 修改订阅邮箱
            drControl.modifyEmail(email);
        },

        cancelSubscribtion : function (e) {
            e.preventDefault();
            var curItem = $(this).parents('li');
            var subscriptionData = curItem.data();


            var $confirmBox;
            var deleteBoxId = 'deleteBoxId';
            // 弹窗确认
            $confirmBox = new Box({
                'containerId' : deleteBoxId,
                'title' : '删除',
                'hasCancel' : true,
                'contentsHtmlString' : '<p>确定删除所选订阅商品？</p>',
                cbAfterCancel : function(cancelMethod) {
                    // 发送取消的log
                    sendLog('PRICEALERT_DALDEL_CANCEL_CLICK', undefined, cancelMethod);
                }
            }, {
                'click .pop-out-btn-confirm' : function () {

                    sendLog('PRICEALERT_DALDEL_CONFIRM_CLICK');
                    drControl.cancelSubscribtion(subscriptionData, function () {
                        // 更新本地数据
                        msg.fire('deleteItems', subscriptionData.url, function () {

                            // 移除页面中的当前元素
                            curItem.remove();

                        })
                    });
                    $confirmBox.remove();
                }
            });

        },

        /**
         * 批量取消订阅
         */
        cancelBundledSubcribtion : function (e) {
            e.preventDefault();
            var datas = [];
            var list = $('#depricedReminderContainer .list .select');
            _.each(list, function (item) {
                if (item.checked) {
                    datas.push($(item).parents('li').data());
                }
            });

            if (datas.length === 0) {
                // 当前无选中元素
                return;
            }

            sendLog('PRICEALERT_SELECTITEM_DEL_CLICK');


            // 弹窗确认
            var $bundleCanelConfirmBox;
            var bundleCanelBoxId = 'bundleCanelBoxId';
            $bundleCanelConfirmBox = new Box({
                'containerId' : bundleCanelBoxId,
                'title' : '批量删除',
                'hasCancel' : true,
                'contentsHtmlString' : '<p>确定删除所选订阅商品？</p>',
                cbAfterCancel : function(cancelMethod) {
                    // 发送取消的log
                    sendLog('PRICEALERT_DALDEL_CANCEL_CLICK', undefined, cancelMethod);
                }
            }, {
                'click .pop-out-btn-confirm' : function () {

                    var items = _.where(datas, {
                        iscluster : false
                    });

                    var clusters = _.where(datas, {
                        iscluster : true
                    });

                    var itemsIds = _.pluck(items, 'id');
                    var clusterIds = _.pluck(clusters, 'id');
                    var urls = _.pluck(datas, 'url');

                    drControl.cancelBundledSubcribtion(itemsIds, clusterIds, function () {
                        msg.fire('deleteItems', urls);
                    });
                    $bundleCanelConfirmBox.remove();
                }
            });

        },

        slideControl : function () {
            var $btn = $(this);
            if ($btn.hasClass('prev-disable') || $btn.hasClass('next-disable')) {
                return;
            }

            var type = $btn.data('btn-type');

            var curContainer = $btn.parents('.items-recmds');
            var curContents = curContainer.find('.recom-contents');

            var length = curContents.find('.recom-content').length;
            var cur = curContainer.data('curInx') || 0;

            cur = ( type === 'next' ? cur + 3 : cur - 3);
            curContainer.find('.prev').toggleClass('prev-disable', cur === 0);
            curContainer.find('.next').toggleClass('next-disable', cur >= length - 3);

            curContainer.data('curInx', cur);
            curContents.animate({
                left : ((type === 'next') ? "-=" : "+=" ) + 3*130
            }, 500);

        }


    };

    var domEventsConf = {
        'click .select' : domEvents.selectSingleItem,
        'click .selectAll' : domEvents.selectAll,
        'click #deleteSelected' : domEvents.deleteSelected,
        'click #deleteCurItem' : domEvents.deleteCurItem,
        'click .page-btn' : domEvents.goToPage,
        'click .modify-depriced-reminder-email' : domEvents.modifyEmail,
        'click .cancel-subscription' : domEvents.cancelSubscribtion,
        'click .cancel-bundle-subscription' : domEvents.cancelBundledSubcribtion,

        'click .items-recmds .prev' : domEvents.slideControl,
        'click .items-recmds .next' : domEvents.slideControl,

        'click .go-top' : function () {
            $(document).scrollTop(0);
        },

        'click .retry' : function () {
            msg.fire('tryLoadPrevReq');
        }
    };

    _eventsBinder(domEventsConf, document);

    // test
    window.view = view;

});
define("tab.js",  function (require, exports, module) {
    'use strict';

    // var conf = {
    //     containerSelector : '',
    //     btnSelector : '',
    //     contentSelector : '',
    //     curClass : ''，
    //     mode : 'click'
    // }

    var Tab = function (conf) {

        return {

            btnSelector : conf.btnSelector,
            $container : $(conf.containerSelector),
            $btns     : $(conf.btnSelector),
            $content  : $(conf.contentSelector),
            curClass : conf.curClass,
            mode : conf.mode,
            callback : conf.callback,

            init : function () {

                var me = this;
                me.$btns.each(function (index, item) {
                    $(this).attr('tab-index', index);
                });

                me.$container.on(me.mode, me.btnSelector,  function (e) {

                    me.$btns.removeClass(me.curClass);
                    $(this).addClass(me.curClass);
                    var inx = $(this).attr('tab-index');

                    me.$content.hide();
                    me.$content.eq(inx).show();
                    if(_.isFunction(me.callback)) {
                        // 绑定此时的this
                        me.callback.call(this);
                    }

                });

                me.$btns.first().addClass(me.curClass);
                me.$content.hide();
                me.$content.first().show();
            }
        };

    };


    module.exports = Tab;



});
/**
 * 收藏夹页面主流程
 * @athor zhulin
 * @date 2014年4月11日
 * msg事件全部在这里注册；触发在data.js view.js等；
 *
 */
define("index.js", function (require, exports, module) {



    'use strict';
    // require('jquery');
    // require('underscore');

    var COUNTS_PER_PAGE = 20; // 每页的条目数量
    var APPEND_COUNTS = 5; // 下滚时每次追加的条目数目
    var BOTTOM_MARGIN = 200; // 滚动到离底部多少px时进行追加

    var logFlag = true;// 是否已经登录

    var data = require('data.js');
    var view = require('view.js');
    var msg = require('msg.js');
    var sendLog = require('log.js');


    // -------------------------------------------------------------------------
    // 初始化Tab
    // -------------------------------------------------------------------------
    var Tab = require('tab.js');

    var cardsTab = new Tab({
        containerSelector : '.cards',
        btnSelector : '.cards-tab-hd',
        contentSelector : '.cards-tab-bd',
        curClass : 'current',
        mode : 'click',
        callback : function () {
            curCollectionTab = $(this).data('type');

            var logType;
            // 发送log
            switch(curCollectionTab) {
                case 'all':
                    logType = 'FAVOR_LIST';
                    break;
                case 'depricedReminder':
                    logType = 'PRICEALERT_LIST';
                    break;
            }

            setTimeout(function(){
                sendLog(logType);
            }, 200);

            // 切换到降价提醒
            if ($(this).hasClass('never-clicked')) {
                // 首次点该击tab 获取数据进行渲染
                newTab('depricedReminder');
                $(this).removeClass('never-clicked');
                // 隐藏提示数字
                $(this).find('#depricedReminderNumber').hide();

            } else {
                // 查看当前tab下是否内容已被清空；如果被清空则重新进行渲染
                // debugger;
                if (view.checkContents(curCollectionTab)) {
                    newTab(curCollectionTab, tabsStatus[curCollectionTab].curPage,tabsStatus[curCollectionTab].maxPage);

                    if (curCollectionTab === 'depricedReminder') {
                        // 隐藏降价信息提示
                        $('.deprice-info').hide();
                    }
                }

            }

        }
    });


    var collectionTab = new Tab({
        containerSelector : '.collections-hd',
        btnSelector : '.tab-hd',
        contentSelector : '.tab-bd',
        curClass : 'current',
        mode : 'click',
        callback : function () {
            curCollectionTab = $(this).data('type');
            var logType;
            // 发送log
            switch(curCollectionTab) {
                case 'all':
                    logType = 'FAVOR_LIST';
                    break;
                case 'depriced':
                    logType = 'FAVOR_RPICEDROPED';
                    break;
                case 'promotion':
                    logType = 'FAVOR_PROMOTIONED';
                    break;
            }
            setTimeout(function(){
                sendLog(logType);
            }, 200);
            if ($(this).hasClass('never-clicked')) {
                // 首次点击该tab 获取数据进行渲染
                newTab(curCollectionTab);


                $(this).removeClass('never-clicked');
                // 移除数字提示
                $(this).find('.number-tip').hide();
            }
            $('.ops').hide();
            // 控制对应的操作按钮展示和隐藏
            $('#' + curCollectionTab + 'Ops').show();
        }
    });

    var tabsInit = function () {
        cardsTab.init();
        collectionTab.init();
    };

    // -------------------------------------------------------------------------

    /**
     * 状态记录
     * type :
     * all | depriced | depricedReminder
     */
    var curCollectionTab = 'all';  // 切换collectiontab的时候改变
    var tabsStatus = {
        'all' : {
            'counts' : 0,
            'curPage' : 0, // 当前页码
            'maxPage' : 0 // 页码总数
        },
        'depriced' : {
            'counts' : 0,
            'curPage' : 0, // 当前页码
            'maxPage' : 0 // 页码总数
        },
        'promotion' : {
            'counts' : 0,
            'curPage' : 0, // 当前页码
            'maxPage' : 0 // 页码总数
        },
        'depricedReminder' : {
            'counts' : 0,
            'curPage' : 0, // 当前页码
            'maxPage' :0 // 页码总数
        }
    };

    // -------------------------------------------------------------------------
    // 注册消息
    // -------------------------------------------------------------------------
    /**
     * 未登录
     */
    msg.on('noLogin', function () {
        //未登录
        logFlag = false;
        view.renderNoLoginTip();
    });

    /**
     * 删除收藏夹中的条目
     */
    msg.on('deleteItems', function (urls) {

        data.deleteData(curCollectionTab, urls, function () {
            // 删除视图中的该项条目
            if (_.isArray(urls)) {
                _.each(urls, function (url) {
                    view.deleteItem(url);
                });
            } else {
                view.deleteItem(urls);
            }
        });
    });

    /**
     * 切换页码
     */
    msg.on('goToPage', function (pageIndex, source) {
        if (!_.isNumber(pageIndex) && !_.isString(pageIndex)) {
            throw new Error('页码参数错误！');
        }

        var type = curCollectionTab;
        // 当前页不做跳转
        if (pageIndex === tabsStatus[type].curPage) {
            return;
        }

        var logType, logPosition;
        switch(type) {
            case 'depricedReminders':
                logType = (source === 'hd' ?
                    'PRICEALERT_LIST_PAGECHANGERIGHT_CLICK' : 'PRICEALERT_LIST_PAGECHANGE_CLICK');
                break;
            default:
                logType = (source === 'hd' ?
                    'FAVOR_LIST_RIGHTPAGECHANGE_CLICK' : 'FAVOR_LIST_PAGECHANGE_CLICK');
                break;
        }

        switch(pageIndex) {
            case 'prev':
                pageIndex = tabsStatus[type].curPage - 1;
                logPosition = 'PREV';
                break;
            case 'next':
                pageIndex = tabsStatus[type].curPage + 1;
                logPosition = 'NEXT';
                break;
            default:
              logPosition = 'NUM';
        }
        sendLog(logType, undefined, logPosition);

        if (pageIndex > tabsStatus[type].maxPage) return;
        newTab(type, pageIndex);
    });

    /**
     * 更新总页码数目
     */
    msg.on('updateMaxPageNum', function (totalPageCounts, type) {
        tabsStatus[type].maxPage = totalPageCounts;
    });

    /**
     * 更改接受订阅提醒的邮箱
     */
    msg.on('setEmailData', function (email) {
        data.userInfo.changeDepricedEmail(email);
        $('.modify-depriced-reminder-email').data('email', email);
    });

    /**
     * 渲染网络错误提示
     */
    msg.on('offline', function (type) {
        view.renderOffLineTip(type);
    });

    msg.on('tryLoadPrevReq', function () {
        view.renderLoadingTip(curCollectionTab);
        data.retry();
    });

    /**
     * 展示获取积分的链接
     */
    msg.on('showJifenGuide', function (link) {
        $('#jifenGuide').attr('href', link).show();
    });
    var jifenGuideNoCollection = false;
    msg.on('showNewUserGuide', function (link) {
        jifenGuideNoCollection = link;
    });

    /*
     * 设置数字
     */
    var placeholders = {
        // 降价提醒的数字
        depricedReminderNumber : {
            selector : '#depricedReminderNumber',
            pre : function (number) {
                return '(' + number + ')';
            },
            callback : function (number) {
                if (number > 0) {
                    $('#depriceIcon').show();
                }
            }
        },
        descendNum : {
            selector : '.descendNum',
            callback : function (number) {
                if (number <= 0) {
                    $('#reminderHasDepricedItem').hide();
                } else {
                    $('#depriceIcon').show();
                }
            }
        },
        reachExpectNum : {
            selector : '.reachExpectNum',
            callback : function (number) {
                if (number > 0) {
                    $('#hasReachedTargetItem').show();
                    $('#depriceIcon').show();
                }
            }
        },
        // 收藏夹类别上的数字
        depricedNumber : {
            selector : '.priceLowSize'
        },
        promotionNumber : {
            selector : '.promotionSize'
        }
    };
    msg.on('setTipNumber', function (type, number) {
        var ref = placeholders[type];
        var originalNumber = number;
        if (_.isFunction(ref.pre)) {
            number = ref.pre(number);
        }
        view.setNumber(ref.selector, number, originalNumber);
        if (_.isFunction(ref.callback)) {
            ref.callback(originalNumber);
        }
    });

    // -------------------------------------------------------------------------
    //  方法
    // -------------------------------------------------------------------------
    /**
     * 渲染新tab页面
     * @param  {[type]} type      [description]
     * @param  {[type]} pageIndex [description]
     * @return {[type]}           [description]
     */
    function newTab(type, pageIndex, maxPage) {

        view.renderLoadingTip(type);
        if (pageIndex === undefined) {
            pageIndex = 1; // 默认为当前页面
        }

        var startInx = (pageIndex - 1) * COUNTS_PER_PAGE;

        // 渲染清单
        data.getData(type, startInx, APPEND_COUNTS, function (res) {

            // 必须等数据更新后知道当前有多少页才能够去取最大页码
            // 因此必须放在数据更新完毕的回调中看数据
            if (maxPage === undefined) {
                maxPage = tabsStatus[type].maxPage;
            }

            view.hideLoadingTip(type);

            if (res === 'none') {
                // 无数据
                view.renderNoDataTip(type);
                if (jifenGuideNoCollection) {
                    $('#jifenGuideNoCollection').attr('href', jifenGuideNoCollection).show();
                }
                // 更新状态
                tabsStatus[type].counts = 0;
                tabsStatus[type].curPage = pageIndex; // 首页
            } else {
                view.renderNewTab(type, res, pageIndex, maxPage);
                // 更新状态
                tabsStatus[type].counts = APPEND_COUNTS;
                tabsStatus[type].curPage = pageIndex; // 首页
            }

            // 刷新导航部分的前后页切换状态
            view.refreshPageControl(type, pageIndex !== 1, pageIndex !== tabsStatus[type].maxPage && tabsStatus[type].maxPage !== 0);
            // maxpage为0意味着没有数据
            // // 若页面加载5条后过短未出现滚动条
            // // 则继续加载一屏，直到填满或者已经渲染整个tab完毕
            // if (window.innerHeight + $(document).scrollTop() <= $(document).height()) {
            //     // renderAppendCurTab();
            // }
        });

        // 回到页面顶部
        $(document).scrollTop(0);
    }


    /**
     * 加载当前tab下5条到列表末尾
     * 只供滚动下拉触发更多加载的时候使用
     * @return {[type]} [description]
     */
    function renderAppendCurTab() {
        var type = curCollectionTab;
        var ref = tabsStatus[type];

        if (ref.counts === 0) {
            // 本模块无数据
            return;
        }

        view.renderLoadingTip(type);

        var startInx = (ref.curPage - 1) * COUNTS_PER_PAGE + ref.counts;
        data.getData(type, startInx, APPEND_COUNTS, function (res) {
            view.hideLoadingTip(type);
            view.appendCurTab(type, res);
        });
        tabsStatus[type].counts += APPEND_COUNTS;
    }

    /**
     * 滚动到距离最底部还有200px时多加载5条
     */
    $(window).on('scroll', function (e) {

        if ($(window).scrollTop() > $(window).height()) {
            $('.go-top').show();
        } else {
            $('.go-top').hide();

        }

        if (($(window).height() + $(this).scrollTop()) + BOTTOM_MARGIN >= $(document).height()) {
            // 滚到了最底部
            var type = curCollectionTab;
            if (tabsStatus[type].counts < COUNTS_PER_PAGE) {
                // 当前不足20条的时候继续加载
                renderAppendCurTab();
            }
        }

    });


    //--------------------------------------------------------------------------
    // 启动
    //
    module.exports = function () {

        view.init();

        view.renderLoadingTip('all');
        data.init(function () {
            // 渲染收藏夹数据
            newTab('all', 1);
            sendLog('FAVOR_LIST');

            if (logFlag) {
                tabsInit();
            }

        });

    };
    //--------------------------------------------------------------------------

});

