text: /**
   * 数据延迟加载组件
   * @module   datalazyload
   * @creator  玉伯<lifesinger@gmail.com>
   */
  KISSY.add('datalazyload', function(S, undefined) {
  
      var DOM = S.DOM, Event = S.Event,
          win = window, doc = document,
  
          IMG_SRC_DATA = 'data-ks-lazyload',
          AREA_DATA_CLS = 'ks-datalazyload',
          CUSTOM = '-custom',
          MANUAL = 'manual',
          DISPLAY = 'display', DEFAULT = 'default', NONE = 'none',
          SCROLL = 'scroll', RESIZE = 'resize',
  
          defaultConfig = {
  
              /**
               * 懒处理模式
               *   auto   - 自动化。html 输出时，不对 img.src 做任何处理
               *   manual - 输出 html 时，已经将需要延迟加载的图片的 src 属性替换为 IMG_SRC_DATA
               * 注：对于 textarea 数据，只有手动模式
               */
              mod: MANUAL,
  
              /**
               * 当前视窗往下，diff px 外的 img/textarea 延迟加载
               * 适当设置此值，可以让用户在拖动时感觉数据已经加载好
               * 默认为当前视窗高度（两屏以外的才延迟加载）
               */
              diff: DEFAULT,
  
              /**
               * 图像的占位图，默认无
               */
              placeholder: NONE
          };
  
      /**
       * 延迟加载组件
       * @constructor
       */
      function DataLazyload(containers, config) {
          var self = this;
  
          // factory or constructor
          if (!(self instanceof DataLazyload)) {
              return new DataLazyload(containers, config);
          }
  
          // 允许仅传递 config 一个参数
          if (config === undefined) {
              config = containers;
              containers = [doc];
          }
  
          // containers 是一个 HTMLElement 时
          if (!S.isArray(containers)) {
              containers = [S.get(containers) || doc];
          }
  
          /**
           * 图片所在容器（可以多个），默认为 [doc]
           * @type Array
           */
          self.containers = containers;
  
          /**
           * 配置参数
           * @type Object
           */
          self.config = S.merge(defaultConfig, config);
  
          /**
           * 需要延迟下载的图片
           * @type Array
           */
          //self.images
  
          /**
           * 需要延迟处理的 textarea
           * @type Array
           */
          //self.areaes
  
          /**
           * 和延迟项绑定的回调函数
           * @type object
           */
          self.callbacks = {els: [], fns: []};
  
          /**
           * 开始延迟的 Y 坐标
           * @type number
           */
          //self.threshold
  
          self._init();
      }
  
      S.augment(DataLazyload, {
  
          /**
           * 初始化
           * @protected
           */
          _init: function() {
              var self = this;
              self.threshold = self._getThreshold();
  
              self._filterItems();
              self._initLoadEvent();
          },
  
          /**
           * 获取并初始化需要延迟的 images 和 areaes
           * @protected
           */
          _filterItems: function() {
              var self = this,
                  containers = self.containers,
                  n, N, imgs, areaes, i, img,
                  lazyImgs = [], lazyAreas = [];
  
              for (n = 0, N = containers.length; n < N; ++n) {
                  imgs = S.query('img', containers[n]);
                  lazyImgs = lazyImgs.concat(S.filter(imgs, self._filterImg, self));
  
                  areaes = S.query('textarea', containers[n]);
                  lazyAreas = lazyAreas.concat(S.filter(areaes, self._filterArea, self));
              }
  
              self.images = lazyImgs;
              self.areaes = lazyAreas;
          },
  
          /**
           * filter for lazyload image
           */
          _filterImg: function(img) {
              var self = this,
                  dataSrc = img.getAttribute(IMG_SRC_DATA),
                  threshold = self.threshold,
                  placeholder = self.config.placeholder,
                  isManualMod = self.config.mod === MANUAL;
  
              // 手工模式，只处理有 data-src 的图片
              if (isManualMod) {
                  if (dataSrc) {
                      if (placeholder !== NONE) {
                          img.src = placeholder;
                      }
                      return true;
                  }
              }
              // 自动模式，只处理 threshold 外无 data-src 的图片
              else {
                  // 注意：已有 data-src 的项，可能已有其它实例处理过，不用再次处理
                  if (DOM.offset(img).top > threshold && !dataSrc) {
                      DOM.attr(img, IMG_SRC_DATA, img.src);
                      if (placeholder !== NONE) {
                          img.src = placeholder;
                      } else {
                          img.removeAttribute('src');
                      }
                      return true;
                  }
              }
          },
  
          /**
           * filter for lazyload textarea
           */
          _filterArea: function(area) {
              return DOM.hasClass(area, AREA_DATA_CLS);
          },
  
          /**
           * 初始化加载事件
           * @protected
           */
          _initLoadEvent: function() {
              var timer, self = this, resizeHandler;
  
              // scroll 和 resize 时，加载图片
              Event.on(win, SCROLL, loader);
              Event.on(win, RESIZE, (resizeHandler = function() {
                  self.threshold = self._getThreshold();
                  loader();
              }));
  
              // 需要立即加载一次，以保证第一屏的延迟项可见
              if (self._getItemsLength()) {
                  S.ready(function() {
                      loadItems();
                  });
              }
  
              // 加载函数
              function loader() {
                  if (timer) return;
                  timer = S.later(function() {
                      loadItems();
                      timer = null;
                  }, 100); // 0.1s 内，用户感觉流畅
              }
  
              // 加载延迟项
              function loadItems() {
                  self._loadItems();
                  if (self._getItemsLength() === 0) {
                      Event.remove(win, SCROLL, loader);
                      Event.remove(win, RESIZE, resizeHandler);
                  }
              }
          },
  
          /**
           * 加载延迟项
           */
          _loadItems: function() {
              var self = this;
              self._loadImgs();
              self._loadAreas();
              self._fireCallbacks();
          },
  
          /**
           * 加载图片
           * @protected
           */
          _loadImgs: function() {
              var self = this;
              self.images = S.filter(self.images, self._loadImg, self);
          },
  
          /**
           * 监控滚动，处理图片
           */
          _loadImg: function(img) {
              var self = this,
                  scrollTop = DOM.scrollTop(),
                  threshold = self.threshold + scrollTop,
                  offset = DOM.offset(img);
  
              if (offset.top <= threshold) {
                  self._loadImgSrc(img);
              } else {
                  return true;
              }
          },
  
          /**
           * 加载图片 src
           * @static
           */
          _loadImgSrc: function(img, flag) {
              flag = flag || IMG_SRC_DATA;
              var dataSrc = img.getAttribute(flag);
  
              if (dataSrc && img.src != dataSrc) {
                  img.src = dataSrc;
                  img.removeAttribute(flag);
              }
          },
  
          /**
           * 加载 textarea 数据
           * @protected
           */
          _loadAreas: function() {
              var self = this;
              self.areaes = S.filter(self.areaes, self._loadArea, self);
          },
  
          /**
           * 监控滚动，处理 textarea
           */
          _loadArea: function(area) {
              var self = this, top,
                  isHidden = DOM.css(area, DISPLAY) === NONE;
  
              // 注：area 可能处于 display: none 状态，DOM.offset(area).top 返回 0
              // 这种情况下用 area.parentNode 的 Y 值来替代
              top = DOM.offset(isHidden ? area.parentNode : area).top;
  
              if (top <= self.threshold + DOM.scrollTop()) {
                  self._loadAreaData(area.parentNode, area);
              } else {
                  return true;
              }
          },
  
          /**
           * 从 textarea 中加载数据
           * @static
           */
          _loadAreaData: function(container, area) {
              //chengyu 大幅重构，使用正则识别 html 字符串里的 script，提高性能
              // 为了通用性，不要搜索 container 内的全部 script dom 节点执行
  
              // 采用隐藏 textarea 但不去除方式，去除会引发 Chrome 下错乱
              area.style.display = NONE;
              area.className = ''; // clear hook
  
              var content = DOM.create('<div>');
              container.insertBefore(content, area);
              DOM.html(content, area.value, true);
  
              //area.value = ''; // bug fix: 注释掉，不能清空，否则 F5 刷新，会丢内容
          },
  
          /**
           * 触发回调
           */
          _fireCallbacks: function() {
              var self = this,
                  callbacks = self.callbacks,
                  els = callbacks.els, fns = callbacks.fns,
                  scrollTop = DOM.scrollTop(),
                  threshold = self.threshold + scrollTop,
                  i, el, fn, remainEls = [], remainFns = [];
  
              for (i = 0; (el = els[i]) && (fn = fns[i++]);) {
                  if (DOM.offset(el).top <= threshold) {
                      fn.call(el);
                  } else {
                      remainEls.push(el);
                      remainFns.push(fn);
                  }
  
              }
              callbacks.els = remainEls;
              callbacks.fns = remainFns;
          },
  
          /**
           * 添加回调函数。当 el 即将出现在视图中时，触发 fn
           */
          addCallback: function(el, fn) {
              var callbacks = this.callbacks;
              el = S.get(el);
  
              if (el && S.isFunction(fn)) {
                  callbacks.els.push(el);
                  callbacks.fns.push(fn);
              }
          },
  
          /**
           * 获取阈值
           * @protected
           */
          _getThreshold: function() {
              var diff = this.config.diff,
                  vh = DOM['viewportHeight']();
  
              if (diff === DEFAULT) return 2 * vh; // diff 默认为当前视窗高度（两屏以外的才延迟加载）
              else return vh + (+diff); // 将 diff 转换成数值
          },
  
          /**
           * 获取当前延迟项的数量
           * @protected
           */
          _getItemsLength: function() {
              var self = this;
              return self.images.length + self.areaes.length + self.callbacks.els.length;
          },
  
          /**
           * 加载自定义延迟数据
           * @static
           */
          loadCustomLazyData: function(containers, type) {
              var self = this, area, imgs;
  
              // 支持数组
              if (!S.isArray(containers)) {
                  containers = [S.get(containers)];
              }
  
              // 遍历处理
              S.each(containers, function(container) {
                  switch (type) {
                      case 'img-src':
                          if (container.nodeName === 'IMG') { // 本身就是图片
                              imgs = [container];
                          } else {
                              imgs = S.query('img', container);
                          }
                          
                          S.each(imgs, function(img) {
                              self._loadImgSrc(img, IMG_SRC_DATA + CUSTOM);
                          });
                          
                          break;
                      
                      default:
                          area = S.get('textarea', container);
                          if (area && DOM.hasClass(area, AREA_DATA_CLS + CUSTOM)) {
                              self._loadAreaData(container, area);
                          }
                  }
              });
          }
      });
  
      // attach static methods
      S.mix(DataLazyload, DataLazyload.prototype, true, ['loadCustomLazyData', '_loadImgSrc', '_loadAreaData']);
  
      S.DataLazyload = DataLazyload;
  
  }, { requires: ['core'] });
  
  /**
   * NOTES:
   *
   * 模式为 auto 时：
   *  1. 在 Firefox 下非常完美。脚本运行时，还没有任何图片开始下载，能真正做到延迟加载。
   *  2. 在 IE 下不尽完美。脚本运行时，有部分图片已经与服务器建立链接，这部分 abort 掉，
   *     再在滚动时延迟加载，反而增加了链接数。
   *  3. 在 Safari 和 Chrome 下，因为 webkit 内核 bug，导致无法 abort 掉下载。该
   *     脚本完全无用。
   *  4. 在 Opera 下，和 Firefox 一致，完美。
   *  5. 2010-07-12: 发现在 Firefox 下，也有导致部分 Aborted 链接。
   *
   * 模式为 manual 时：（要延迟加载的图片，src 属性替换为 data-lazyload-src, 并将 src 的值赋为 placeholder ）
   *  1. 在任何浏览器下都可以完美实现。
   *  2. 缺点是不渐进增强，无 JS 时，图片不能展示。
   *
   * 缺点：
   *  1. 对于大部分情况下，需要拖动查看内容的页面（比如搜索结果页），快速滚动时加载有损用
   *     户体验（用户期望所滚即所得），特别是网速不好时。
   *  2. auto 模式不支持 Webkit 内核浏览器；IE 下，有可能导致 HTTP 链接数的增加。
   *
   * 优点：
   *  1. 可以很好的提高页面初始加载速度。
   *  2. 第一屏就跳转，延迟加载图片可以减少流量。
   *
   * 参考资料：
   *  1. http://davidwalsh.name/lazyload MooTools 的图片延迟插件
   *  2. http://vip.qq.com/ 模板输出时，就替换掉图片的 src
   *  3. http://www.appelsiini.net/projects/lazyload jQuery Lazyload
   *  4. http://www.dynamixlabs.com/2008/01/17/a-quick-look-add-a-loading-icon-to-your-larger-images/
   *  5. http://www.nczonline.net/blog/2009/11/30/empty-image-src-can-destroy-your-site/
   *
   * 特别要注意的测试用例:
   *  1. 初始窗口很小，拉大窗口时，图片加载正常
   *  2. 页面有滚动位置时，刷新页面，图片加载正常
   *  3. 手动模式，第一屏有延迟图片时，加载正常
   *
   * 2009-12-17 补充：
   *  1. textarea 延迟加载约定：页面中需要延迟的 dom 节点，放在
   *       <textarea class='ks-datalazysrc invisible'>dom code</textarea>
   *     里。可以添加 hidden 等 class, 但建议用 invisible, 并设定 height = '实际高度'，这样可以保证
   *     滚动时，diff 更真实有效。
   *     注意：textarea 加载后，会替换掉父容器中的所有内容。
   *  2. 延迟 callback 约定：dataLazyload.addCallback(el, fn) 表示当 el 即将出现时，触发 fn.
   *  3. 所有操作都是最多触发一次，比如 callback. 来回拖动滚动条时，只有 el 第一次出现时会触发 fn 回调。
   */
  
  /**
   * xTODO:
   *   - [取消] 背景图片的延迟加载（对于 css 里的背景图片和 sprite 很难处理）
   *   - [取消] 加载时的 loading 图（对于未设定大小的图片，很难完美处理[参考资料 4]）
   */
  
  /**
   * UPDATE LOG:
   *   - 2010-07-31 yubo IMG_SRC_DATA 由 data-lazyload-src 更名为 data-ks-lazyload + 支持 touch 设备
   *   - 2010-07-10 chengyu 重构，使用正则表达式识别 html 中的脚本，使用 EventTarget 自定义事件机制来处理回调
   *   - 2010-05-10 yubo ie6 下，在 dom ready 后执行，会导致 placeholder 重复加载，为比避免此问题，默认为 none, 去掉占位图
   *   - 2010-04-05 yubo 重构，使得对 YUI 的依赖仅限于 YDOM
   *   - 2009-12-17 yubo 将 imglazyload 升级为 datalazyload, 支持 textarea 方式延迟和特定元素即将出现时的回调函数
   */
  