<commit_before>var recommend = {
    searchName: mw.config.get('wgPageName'),
    myId: mw.config.get('wgArticleId'),
    category: '',
    pageRec: mw.config.get('wgRecByUser')||[],
    arr: [],
    item: 0,
    netData:'',

    funAddBox: function(){
        $('.comments-body').before('

');
    },

    funGetPageRec: function(){
        var self = this;
        this.pageRec.forEach(function(item,i){
            if(i<4) {
                var content = '';
                content += '
' +
                    '
' +
                    item.title + '' + item.siteName + '

';
                self.arr.push(item.title+item.siteName);
                self.funGetImg(item.title, i, item.site);
                $('.recommend').append(content);
            }
        });
    },

    funGetCategory: function(){
        var self = this;
        if($('#mw-normal-catlinks').length!=0) {
            $('#mw-normal-catlinks li:not(.last)').each(function () {
                self.category += $(this).text() + ' ';
            });
        }
    },

    funGetNetRec: function(){
        var self = this;
        $.ajax({
            url:'http://121.42.179.100:8080/queryService/webapi/page/recommend/',
            data:{content:self.searchName,category:self.category,sitePrefix:mw.config.get('wgHuijiPrefix')},
            success: function (data) {
                var content = '';
                var len = self.pageRec.length;
                self.netData = data;
                if (data.length == 0) return;

                //除去指定推荐，剩下的进行遍历
                document.body.clientWidth>768?pcTraverse():mobilTraverse();
                $('.recommend').append(content);

                function pcTraverse(){
                    for (var i = len; i < 10; i++) {
                        var item = data[i - len];
                        var searchtitle = item.title;
                        var id = item.id;

                        //排除本词条和重复词条
//                    if (id != self.myId && self.arr.indexOf(searchtitle+item.siteName)<0) {
                        var sitePrefix = item.sitePrefix;

                        //去em标签
                        searchtitle = searchtitle.replace(//g, '').replace(/<\/em>/g, '');

                        content += '

' +
                            '' + item.title + '' + item.siteName + '

';
                        self.funGetImg(searchtitle, i, sitePrefix, item.address, false);
                    }
                    self.item = i;
                }
                function mobilTraverse(){
                    for (var i = len; i < 4; i++) {
                        var item = data[i - len];
                        var searchtitle = item.title;
                        var id = item.id;

                        //排除本词条和重复词条
//                    if (id != self.myId && self.arr.indexOf(searchtitle+item.siteName)<0) {
                        var sitePrefix = item.sitePrefix;

                        //去em标签
                        searchtitle = searchtitle.replace(//g, '').replace(/<\/em>/g, '');

                        content += '

' +
                            '' + item.title + '' + item.siteName + '

';
                        self.funGetImg(searchtitle, i, sitePrefix, item.address, false);
                    }
                    self.item = i;
                }
//                }

            },
            type: 'post'
        });
    },

    funGetImg: function(title,id,sitePrefix,address,ajax){
        var self = this;
        $.ajax({
            url:'http://'+sitePrefix+'.huiji.wiki/api.php?action=query&prop=pageimages&format=json&pithumbsize=250&titles='+title,
            type: 'get',
            success: function(data){
                var x;
                for( x in data.query.pages) {
                    if (data.query.pages[x].thumbnail) {
                        var img = '';
                        var percent = data.query.pages[x].thumbnail.width / data.query.pages[x].thumbnail.height;

                        $('#' + id ).prepend(img);
                        $('#' + id).removeClass('re-opacity');

                        self.funClipImg(percent,id);

                    } else {
                        $('#' + id).removeClass('re-opacity');
                        $('#' + id).prepend('');
                    }
                }
                if(ajax == false) {
                    if ($('.recommend-header').length == 0) {
                        $('.recommend').before('
更多推荐
');
                    }

                    //最后一次取图
                    if (id == self.item-1) {
                        doOwl();
                    }
                }

                function doOwl(){
                    if ($('.recommend .recommend-item').length == 0) $('.recommend').remove();
                    setTimeout(function(){
                        lazyLoad.autocheck();
                    },100);

                    $("#recommend").owlCarousel({
                        items: 5,
                        beforeMove: function () {
                            var sp = $('.owl-wrapper').css('transform').split(',');
                            var len = self.pageRec.length;
                            var data = self.netData;
                            var n = self.item;
                            sp = Math.abs(parseInt(sp[4]));

                            if(n<30&&document.body.clientWidth>768) {
                                if (sp > ($('.owl-item').length - 5) * $('.owl-item').width()) {
                                    setTimeout(function(){
                                        var load = '

';
                                        self.funAddItem(load,n);
                                    },100);
                                    for (var i = n; i < n + 2; i++) {
                                        var item = data[i - len];
                                        var searchtitle = item.title;
                                        var id = item.id;
                                        //排除本词条和重复词条
//                                        if (id != self.myId && self.arr.indexOf(searchtitle+item.siteName)<0) {
                                        var sitePrefix = item.sitePrefix;

                                        //去em标签
                                        searchtitle = searchtitle.replace(//g, '').replace(/<\/em>/g, '');

                                        self.funGetImg2(searchtitle, i, sitePrefix, address, item, n, true);
//                                        }
                                        self.item++;
                                    }
                                }
                            }else if(n>=30&&document.body.clientWidth>768){
                                if (sp > ($('.owl-item').length - 5) * $('.owl-item').width()) {
                                    setTimeout(function(){
                                        for(var i=n; i<n+2; i++){
                                            var img = '

' +
                                                'U FOUND ME
cool cool cool!

';
                                            self.funAddItem(img,n);
                                            self.item++;
                                        }
                                    },100);

                                }
                            }
                        }
                    });
                }
            }
        });
    },

    funClipImg: function(percent,id){
        var wid,wid2;
        //更宽裁剪两边
        if (percent > 0.7) {
            wid = percent * 100 - 70;
            wid2 = percent * 100 + 70;
            $('#' + id).find('img').css({'clip': 'rect(0,' + wid2 + 'px,200px,' + wid + 'px)', 'left': '-' + wid + 'px', 'height': '200px'});
        }
        //更高裁剪上下
        else if (percent < 0.7) {
            wid = 1 / percent * 70 - 100;
            wid2 = 1 / percent * 70 + 100;
            $('#' + id).find('img').css({'clip': 'rect(' + wid + 'px,140px,' + wid2 + 'px,0)', 'top': '-' + wid + 'px', 'width': '140px'});
        }
        else {
            $('#' + id).find('img').css('height', '200px');
            $('#' + id).removeClass('re-opacity');
        }
    },

    funAddItem: function(img,n){
        $('#recommend').data('owlCarousel').addItem(img);
        $('#recommend').data('owlCarousel').jumpTo(n);
    },

    funGetImg2: function(title,id,sitePrefix, address, item, n, ajax){
        var self = this;
        $.ajax({
            url: 'http://' + sitePrefix + '.huiji.wiki/api.php?action=query&prop=pageimages&format=json&pithumbsize=250&titles=' + title,
            type: 'get',
            success: function (data) {
                var x;
                var img;
                for (x in data.query.pages) {
                    if (data.query.pages[x].thumbnail) {
                        img = '

' +
                            '' + item.title + '' + item.siteName + '

';
                        var percent = data.query.pages[x].thumbnail.width / data.query.pages[x].thumbnail.height;
                        if($('.recommend-load').length>0){
                            $('#recommend').data('owlCarousel').removeItem();
                        }
                        self.funAddItem(img,n);
                        self.funClipImg(percent,id);

                    } else {
                        img = '

' +
                            '' + item.title + '' + item.siteName + '

';
                        if($('.recommend-load').length>0){
                            $('#recommend').data('owlCarousel').removeItem();
                        }
                        self.funAddItem(img,n);
                    }
                }
            }
        });
    },

    init: function(){
        if(mw.config.get('wgRec')||$('body').hasClass('ns-0')&&!mw.config.get('wgNoRec')){
            this.funAddBox();
            this.funGetPageRec();
            this.funGetCategory();
            this.funGetNetRec();
        }
    }
}

var lazyLoad = {
    area1: $('#recommend').get(0),
    //懒加载子域集合
    arr:[this.area1],

    getClient:function (){
    var l, t, w, h;
        l = document.documentElement.scrollLeft || document.body.scrollLeft;
        t = document.documentElement.scrollTop || document.body.scrollTop;
        w = document.documentElement.clientWidth;
        h = document.documentElement.clientHeight;
        return { left: l, top: t, width: w, height: h };
    },
    getSubClient:function (p){
        var l = 0, t = 0, w, h;
        w = p.offsetWidth;
        h = p.offsetHeight;
        while(p.offsetParent){
            l += p.offsetLeft;
            t += p.offsetTop;
            p = p.offsetParent;
        }
        return { left: l, top: t, width: w, height: h };
    },
    intens:function (rec1, rec2){
        var lc1, lc2, tc1, tc2, w1, h1;
        lc1 = rec1.left + rec1.width / 2;
        lc2 = rec2.left + rec2.width / 2;
        tc1 = rec1.top + rec1.height / 2 ;
        tc2 = rec2.top + rec2.height / 2 ;
        w1 = (rec1.width + rec2.width) / 2 ;
        h1 = (rec1.height + rec2.height) / 2;
        return Math.abs(lc1 - lc2) < w1 && Math.abs(tc1 - tc2)
= 0; i--) {
            if (arr[i]) {
                prec2 = self.getSubClient(arr[i]);
                if (self.intens(prec1, prec2)) {
                    callback(arr[i]);
    // 加载资源后，删除监测
                    delete arr[i];
                }
            }
        }
    },
    autocheck: function (){
        var prec1 = this.getClient();
        var arr = [$('#recommend').get(0)];
        this.detection(arr, prec1, function(obj){
            $('.lazy-loading img').each(function(){
                this.src = $(this).data('src');
                $(this).removeAttr('data-src');
            })
        })
    },

    init: function(){
        var self = this;
        self.autocheck();
        window.onscroll = function(){
            self.autocheck()
        };
        window.onresize = function(){
            self.autocheck()
        }
    }

}
$(function(){

    recommend.init();

    lazyLoad.init();
});
<commit_msg>contentbug<commit_after>var recommend = {
    searchName: mw.config.get('wgPageName'),
    myId: mw.config.get('wgArticleId'),
    category: '',
    pageRec: mw.config.get('wgRecByUser')||[],
    arr: [],
    item: 0,
    netData:'',

    funAddBox: function(){
        $('.comments-body').before('

');
    },

    funGetPageRec: function(){
        var self = this;
        this.pageRec.forEach(function(item,i){
            if(i<4) {
                var content = '';
                content += '
' +
                    '
' +
                    item.title + '' + item.siteName + '

';
                self.arr.push(item.title+item.siteName);
                self.funGetImg(item.title, i, item.site);
                $('.recommend').append(content);
            }
        });
    },

    funGetCategory: function(){
        var self = this;
        if($('#mw-normal-catlinks').length!=0) {
            $('#mw-normal-catlinks li:not(.last)').each(function () {
                self.category += $(this).text() + ' ';
            });
        }
    },

    funGetNetRec: function(){
        var self = this;
        $.ajax({
            url:'http://121.42.179.100:8080/queryService/webapi/page/recommend/',
            data:{content:self.searchName,category:self.category,sitePrefix:mw.config.get('wgHuijiPrefix')},
            success: function (data) {
                var content = '';
                var len = self.pageRec.length;
                self.netData = data;
                if (data.length == 0) return;

                //除去指定推荐，剩下的进行遍历
                document.body.clientWidth>768?pcTraverse():mobilTraverse();
                $('.recommend').append(content);

                function pcTraverse(){
                    for (var i = len; i < 10; i++) {
                        var item = data[i - len];
                        var searchtitle = item.title;
                        var id = item.id;

                        //排除本词条和重复词条
//                    if (id != self.myId && self.arr.indexOf(searchtitle+item.siteName)<0) {
                        var sitePrefix = item.sitePrefix;

                        //去em标签
                        searchtitle = searchtitle.replace(//g, '').replace(/<\/em>/g, '');

                        content += '

' +
                            '' + item.title + '' + item.siteName + '

';
                        self.funGetImg(searchtitle, i, sitePrefix, item.address, false);
                    }
                    self.item = i;
                }
                function mobilTraverse(){
                    for (var i = len; i < 4; i++) {
                        var item = data[i - len];
                        var searchtitle = item.title;
                        var id = item.id;

                        //排除本词条和重复词条
//                    if (id != self.myId && self.arr.indexOf(searchtitle+item.siteName)<0) {
                        var sitePrefix = item.sitePrefix;

                        //去em标签
                        searchtitle = searchtitle.replace(//g, '').replace(/<\/em>/g, '');

                        content += '

' +
                            '' + item.title + '' + item.siteName + '

';
                        self.funGetImg(searchtitle, i, sitePrefix, item.address, false);
                    }
                    self.item = i;
                }
//                }

            },
            type: 'post'
        });
    },

    funGetImg: function(title,id,sitePrefix,address,ajax){
        var self = this;
        $.ajax({
            url:'http://'+sitePrefix+'.huiji.wiki/api.php?action=query&prop=pageimages&format=json&pithumbsize=250&titles='+title,
            type: 'get',
            success: function(data){
                var x;
                for( x in data.query.pages) {
                    if (data.query.pages[x].thumbnail) {
                        var img = '';
                        var percent = data.query.pages[x].thumbnail.width / data.query.pages[x].thumbnail.height;

                        $('#' + id ).prepend(img);
                        $('#' + id).removeClass('re-opacity');

                        self.funClipImg(percent,id);

                    } else {
                        $('#' + id).removeClass('re-opacity');
                        $('#' + id).prepend('');
                    }
                }
                if(ajax == false) {
                    if ($('.recommend-header').length == 0) {
                        $('.recommend').before('
更多推荐
');
                    }

                    //最后一次取图
                    if (id == self.item-1) {
                        doOwl();
                    }
                }

                function doOwl(){
                    if ($('.recommend .recommend-item').length == 0) $('.recommend').remove();
                    setTimeout(function(){
                        lazyLoad.autocheck();
                    },100);

                    $("#recommend").owlCarousel({
                        items: 5,
                        beforeMove: function () {
                            var sp = $('.owl-wrapper').css('transform').split(',');
                            var len = self.pageRec.length;
                            var data = self.netData;
                            var n = self.item;
                            sp = Math.abs(parseInt(sp[4]));

                            if(n<30&&document.body.clientWidth>768) {
                                if (sp > ($('.owl-item').length - 5) * $('.owl-item').width()) {
                                    setTimeout(function(){
                                        var load = '

';
                                        self.funAddItem(load,n);
                                    },100);
                                    for (var i = n; i < n + 2; i++) {
                                        var item = data[i - len];
                                        var searchtitle = item.title;
                                        var id = item.id;
                                        //排除本词条和重复词条
//                                        if (id != self.myId && self.arr.indexOf(searchtitle+item.siteName)<0) {
                                        var sitePrefix = item.sitePrefix;

                                        //去em标签
                                        searchtitle = searchtitle.replace(//g, '').replace(/<\/em>/g, '');

                                        self.funGetImg2(searchtitle, i, sitePrefix, address, item, n, true);
//                                        }
                                        self.item++;
                                    }
                                }
                            }else if(n>=30&&document.body.clientWidth>768){
                                if (sp > ($('.owl-item').length - 5) * $('.owl-item').width()) {
                                    setTimeout(function(){
                                        for(var i=n; i<n+2; i++){
                                            var img = '

' +
                                                'U FOUND ME
cool cool cool!

';
                                            self.funAddItem(img,n);
                                            self.item++;
                                        }
                                    },100);

                                }
                            }
                        }
                    });
                }
            }
        });
    },

    funClipImg: function(percent,id){
        var wid,wid2;
        //更宽裁剪两边
        if (percent > 0.7) {
            wid = percent * 100 - 70;
            wid2 = percent * 100 + 70;
            $('#' + id).find('img').css({'clip': 'rect(0,' + wid2 + 'px,200px,' + wid + 'px)', 'left': '-' + wid + 'px', 'height': '200px'});
        }
        //更高裁剪上下
        else if (percent < 0.7) {
            wid = 1 / percent * 70 - 100;
            wid2 = 1 / percent * 70 + 100;
            $('#' + id).find('img').css({'clip': 'rect(' + wid + 'px,140px,' + wid2 + 'px,0)', 'top': '-' + wid + 'px', 'width': '140px'});
        }
        else {
            $('#' + id).find('img').css('height', '200px');
            $('#' + id).removeClass('re-opacity');
        }
    },

    funAddItem: function(img,n){
        $('#recommend').data('owlCarousel').addItem(img);
        $('#recommend').data('owlCarousel').jumpTo(n);
    },

    funGetImg2: function(title,id,sitePrefix, address, item, n, ajax){
        var self = this;
        $.ajax({
            url: 'http://' + sitePrefix + '.huiji.wiki/api.php?action=query&prop=pageimages&format=json&pithumbsize=250&titles=' + title,
            type: 'get',
            success: function (data) {
                var x;
                var img;
                for (x in data.query.pages) {
                    if (data.query.pages[x].thumbnail) {
                        img = '

' +
                            '' + item.title + '' + item.siteName + '

';
                        var percent = data.query.pages[x].thumbnail.width / data.query.pages[x].thumbnail.height;
                        if($('.recommend-load').length>0){
                            $('#recommend').data('owlCarousel').removeItem();
                        }
                        self.funAddItem(img,n);
                        self.funClipImg(percent,id);

                    } else {
                        img = '

' +
                            '' + item.title + '' + item.siteName + '

';
                        if($('.recommend-load').length>0){
                            $('#recommend').data('owlCarousel').removeItem();
                        }
                        self.funAddItem(img,n);
                    }
                }
            }
        });
    },

    init: function(){
        if(mw.config.get('wgRec')||$('body').hasClass('ns-0')&&!mw.config.get('wgNoRec')){
            this.funAddBox();
            this.funGetPageRec();
            this.funGetCategory();
            this.funGetNetRec();
        }
    }
}

var lazyLoad = {
    area1: $('#recommend').get(0),
    //懒加载子域集合
    arr:[this.area1],

    getClient:function (){
    var l, t, w, h;
        l = document.documentElement.scrollLeft || document.body.scrollLeft;
        t = document.documentElement.scrollTop || document.body.scrollTop;
        w = document.documentElement.clientWidth;
        h = document.documentElement.clientHeight;
        return { left: l, top: t, width: w, height: h };
    },
    getSubClient:function (p){
        var l = 0, t = 0, w, h;
        w = p.offsetWidth;
        h = p.offsetHeight;
        while(p.offsetParent){
            l += p.offsetLeft;
            t += p.offsetTop;
            p = p.offsetParent;
        }
        return { left: l, top: t, width: w, height: h };
    },
    intens:function (rec1, rec2){
        var lc1, lc2, tc1, tc2, w1, h1;
        lc1 = rec1.left + rec1.width / 2;
        lc2 = rec2.left + rec2.width / 2;
        tc1 = rec1.top + rec1.height / 2 ;
        tc2 = rec2.top + rec2.height / 2 ;
        w1 = (rec1.width + rec2.width) / 2 ;
        h1 = (rec1.height + rec2.height) / 2;
        return Math.abs(lc1 - lc2) < w1 && Math.abs(tc1 - tc2)
= 0; i--) {
            if (arr[i]) {
                prec2 = self.getSubClient(arr[i]);
                if (self.intens(prec1, prec2)) {
                    callback(arr[i]);
    // 加载资源后，删除监测
                    delete arr[i];
                }
            }
        }
    },
    autocheck: function (){
        var prec1 = this.getClient();
        var arr = [$('#recommend').get(0)];
        this.detection(arr, prec1, function(obj){
            $('.lazy-loading img').each(function(){
                this.src = $(this).data('src');
                $(this).removeAttr('data-src');
            })
        })
    },

    init: function(){
        var self = this;
        self.autocheck();
        window.onscroll = function(){
            self.autocheck()
        };
        window.onresize = function(){
            self.autocheck()
        }
    }

}
$(function(){

    recommend.init();

    lazyLoad.init();
});
<|endoftext|>


<commit_before>
Kadira = {};
Kadira.options = __meteor_runtime_config__.kadira || {};
Kadira.options.errorDumpInterval = Kadira.options.errorDumpInterval || 1000*60;
Kadira.options.maxErrorsPerInterval = Kadira.options.maxErrorsPerInterval || 10;

if(Kadira.options.collectAllStacks === undefined) {
  Kadira.options.collectAllStacks = false;
}

Kadira.errors = {};
Kadira.sendSavedErrors = function () {
  var errors = _.values(Kadira.errors);
  // only send errors which have occured again
  errors = _(errors).filter(function (error) {
    return error.count > 0;
  });

  Kadira.errors = {};
  if(errors && errors.length) {
    Kadira.sendErrors(errors);
  }
}

if(Kadira.options && Kadira.options.endpoint) {
  Kadira.syncedDate = new Ntp(Kadira.options.endpoint);
  Kadira.syncedDate.sync();
  setInterval(Kadira.sendSavedErrors, Kadira.options.errorDumpInterval);
}

Kadira.trackError = function (error) {
  if(Kadira.errors[error.name]) {
    Kadira.errors[error.name].count++;
  } else {
    Kadira.sendErrors([error]);
    Kadira.errors[error.name] = _.clone(error);
    Kadira.errors[error.name].count = 0;
  }
}

/**
 * Send error metrics/traces to kadira server
 * @param  {Object} payload Contains browser info and error traces
 */
Kadira.sendErrors = function (errors) {
  errors = errors.slice(0, Kadira.options.maxErrorsPerInterval);
  _(errors).forEach(Kadira.prepareError);
  var payload = {errors: JSON.stringify(errors)};
  Kadira.send(payload);
}

Kadira.send = function (payload) {
  var endpoint = Kadira.options.endpoint + '/errors';
  var retryCount = 0;
  var retry = new Retry({
    minCount: 1,
    minTimeout: 0,
    baseTimeout: 1000*5,
    maxTimeout: 1000*60,
  });
  tryToSend();

  function tryToSend() {
    if(retryCount < 5) {
      retry.retryLater(retryCount++, function () {
        $.ajax({url: endpoint, data: payload, error: tryToSend});
      });
    } else {
      console.warn('Error sending error traces to kadira server');
    }
  }
}

Kadira.prepareError = function (error) {
  if(error.stacks) {
    _(error.stacks).forEach(Kadira.prepareErrorStack);
  }
}

Kadira.prepareErrorStack = function (stack) {
  if(stack.ownerArgs) {
    stack.ownerArgs = _(stack.ownerArgs).map(Kadira.prepareArgument);
  }

  if(stack.events) {
    _(stack.events).forEach(function (event) {
      for(var key in event) {
        if(event.hasOwnProperty(key)) {
          event[key] = Kadira.prepareArgument(event[key]);
        }
      }
    });
  }

  if(stack.info) {
    _(stack.info).forEach(function (item) {
      for(var key in item) {
        if(item.hasOwnProperty(key)) {
          item[key] = Kadira.prepareArgument(item[key]);
        }
      }
    });
  }
}

Kadira.prepareArgument = function (value) {
  if(value && typeof value === 'function') {
    return '--- argument is a Function ---';
  }

  try {
    var string = JSON.stringify(value);
  } catch (e) {
    return '--- cannot stringify argument ---';
  }

  if(value && string.length > 1024) {
    return '--- argument size exceeds limit ---';
  }

  return value;
}

/**
 * IE8 and IE9 does not support CORS with the usual XMLHttpRequest object
 * If XDomainRequest exists, use it to send errors.
 */
if (window.XDomainRequest) {
  $.ajaxTransport(function(s) {
    return {
      send: function (headers, callback) {
        var xdr = new XDomainRequest();
        var data = s.data || null;

        xdr.onload = function () {
          var headers = {'Content-Type': xdr.contentType};
          callback(200, 'OK', {text: xdr.responseText}, headers);
        }

        xdr.onerror = function () {
          callback(404);
        }

        xdr.open(s.type, s.url);
        xdr.send(data);
      }
    };
  });
}
<commit_msg>Use 'POST' to send data<commit_after>
Kadira = {};
Kadira.options = __meteor_runtime_config__.kadira || {};
Kadira.options.errorDumpInterval = Kadira.options.errorDumpInterval || 1000*60;
Kadira.options.maxErrorsPerInterval = Kadira.options.maxErrorsPerInterval || 10;

if(Kadira.options.collectAllStacks === undefined) {
  Kadira.options.collectAllStacks = false;
}

Kadira.errors = {};
Kadira.sendSavedErrors = function () {
  var errors = _.values(Kadira.errors);
  // only send errors which have occured again
  errors = _(errors).filter(function (error) {
    return error.count > 0;
  });

  Kadira.errors = {};
  if(errors && errors.length) {
    Kadira.sendErrors(errors);
  }
}

if(Kadira.options && Kadira.options.endpoint) {
  Kadira.syncedDate = new Ntp(Kadira.options.endpoint);
  Kadira.syncedDate.sync();
  setInterval(Kadira.sendSavedErrors, Kadira.options.errorDumpInterval);
}

Kadira.trackError = function (error) {
  if(Kadira.errors[error.name]) {
    Kadira.errors[error.name].count++;
  } else {
    Kadira.sendErrors([error]);
    Kadira.errors[error.name] = _.clone(error);
    Kadira.errors[error.name].count = 0;
  }
}

/**
 * Send error metrics/traces to kadira server
 * @param  {Object} payload Contains browser info and error traces
 */
Kadira.sendErrors = function (errors) {
  errors = errors.slice(0, Kadira.options.maxErrorsPerInterval);
  _(errors).forEach(Kadira.prepareError);
  var payload = {errors: JSON.stringify(errors)};
  Kadira.send(payload);
}

Kadira.send = function (payload) {
  var endpoint = Kadira.options.endpoint + '/errors';
  var retryCount = 0;
  var retry = new Retry({
    minCount: 1,
    minTimeout: 0,
    baseTimeout: 1000*5,
    maxTimeout: 1000*60,
  });
  tryToSend();

  function tryToSend() {
    if(retryCount < 5) {
      retry.retryLater(retryCount++, function () {
        $.ajax({
          type: 'POST',
          url: endpoint,
          contentType: 'application/json',
          data: JSON.stringify(payload),
          error: tryToSend
        });
      });
    } else {
      console.warn('Error sending error traces to kadira server');
    }
  }
}

Kadira.prepareError = function (error) {
  if(error.stacks) {
    _(error.stacks).forEach(Kadira.prepareErrorStack);
  }
}

Kadira.prepareErrorStack = function (stack) {
  if(stack.ownerArgs) {
    stack.ownerArgs = _(stack.ownerArgs).map(Kadira.prepareArgument);
  }

  if(stack.events) {
    _(stack.events).forEach(function (event) {
      for(var key in event) {
        if(event.hasOwnProperty(key)) {
          event[key] = Kadira.prepareArgument(event[key]);
        }
      }
    });
  }

  if(stack.info) {
    _(stack.info).forEach(function (item) {
      for(var key in item) {
        if(item.hasOwnProperty(key)) {
          item[key] = Kadira.prepareArgument(item[key]);
        }
      }
    });
  }
}

Kadira.prepareArgument = function (value) {
  if(value && typeof value === 'function') {
    return '--- argument is a Function ---';
  }

  try {
    var string = JSON.stringify(value);
  } catch (e) {
    return '--- cannot stringify argument ---';
  }

  if(value && string.length > 1024) {
    return '--- argument size exceeds limit ---';
  }

  return value;
}

/**
 * IE8 and IE9 does not support CORS with the usual XMLHttpRequest object
 * If XDomainRequest exists, use it to send errors.
 */
if (window.XDomainRequest) {
  $.ajaxTransport(function(s) {
    return {
      send: function (headers, callback) {
        var xdr = new XDomainRequest();
        var data = s.data || null;

        xdr.onload = function () {
          var headers = {'Content-Type': xdr.contentType};
          callback(200, 'OK', {text: xdr.responseText}, headers);
        }

        xdr.onerror = function () {
          callback(404);
        }

        xdr.open(s.type, s.url);
        xdr.send(data);
      }
    };
  });
}
<|endoftext|>


<commit_before>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( jQuery, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !jQuery.support.display || !jQuery.support.position || !jQuery.support.overflow || !jQuery.support.floatclear ) {
		return;
	}

	//these properties should be made easy to override externally
	jQuery.mobile = {};

	jQuery.extend(jQuery.mobile, {
		subPageUrlKey: 'ui-page' //define the key used in urls for sub-pages. Defaults to &ui-page=
	});
	jQuery.mobile.subPageUrlKey = 'ui-page';

	var $window = jQuery(window),
		$html = jQuery('html'),
		$head = jQuery('head'),
		$body,
		$loader = jQuery('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		pageTransition,
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [ {
			url: location.hash.replace( /^#/, "" ),
			transition: "slide"
		} ],
		nextPageRole = null,
		noCache = '.ui-dialog';

	// hide address bar
	function hideBrowserChrome() {
		// prevent scrollstart and scrollstop events
		jQuery.event.special.scrollstart.enabled = false;
		window.scrollTo( 0, 0 );
		setTimeout(function() {
			jQuery.event.special.scrollstart.enabled = true;
		}, 150 );
	}

	function getBaseURL(){
	    var newBaseURL = location.hash.replace(/#/,'').split('/');
		if(newBaseURL.length && /[.|&]/.test(newBaseURL[newBaseURL.length-1]) ){
			newBaseURL.pop();
		}
		newBaseURL = newBaseURL.join('/');
		if(newBaseURL !== "" && newBaseURL.charAt(newBaseURL.length-1) !== '/'){  newBaseURL += '/'; }
		return newBaseURL;
	}

	function setBaseURL(){
		//set base url for new page assets
		$('#ui-base').attr('href', getBaseURL());
	}

	function resetBaseURL(){
		$('#ui-base').attr('href', location.pathname);
	}

	// send a link through hash tracking
	jQuery.fn.ajaxClick = function() {
		var href = jQuery( this ).attr( "href" );
		pageTransition = jQuery( this ).attr( "data-transition" ) || "slide";
		nextPageRole = jQuery( this ).attr( "data-rel" );

		//find new base for url building
		var newBaseURL = getBaseURL();

		//if href is absolute but local, or a local ID, no base needed
		if( /^\//.test(href) || (/https?:\/\//.test(href) && !!(href).match(location.hostname)) || /^#/.test(href) ){
			newBaseURL = '';
		}

		// set href to relative path using baseURL and
		if( !/https?:\/\//.test(href) ){
			href = newBaseURL + href;
		}

		//if it's a non-local-anchor and Ajax is not supported, or if it's an external link, go to page without ajax
		if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) {
			location = href
		}
		else{
			// let the hashchange event handler take care of requesting the page via ajax
			location.hash = href;
		}
		return this;
	};

	// ajaxify all navigable links
	jQuery( "a:not([href=#]):not([target]):not([rel=external])" ).live( "click", function(event) {
		jQuery( this ).ajaxClick();
		return false;
	});

	// turn on/off page loading message.. also hides the ui-content div
	function pageLoading( done ) {
		if ( done ) {
			$html.removeClass( "ui-loading" );
			//fade in page content, remove loading msg
			jQuery('.ui-page-active .ui-content').addClass('dissolve in');
		} else {
			$html.addClass( "ui-loading" );
			$loader.appendTo( $body ).addClass( "dissolve in" );
		}
	};

	// transition between pages - based on transitions from jQtouch
	function changePage( from, to, transition, back ) {
		hideBrowserChrome();

		// kill keyboard (thx jQtouch :) )
		jQuery( document.activeElement ).blur();

		// animate in / out
		from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
		to.appendTo($body).addClass( activePageClass + " " + transition +
			" in " + ( back ? "reverse" : "" ) );

		// callback - remove classes, etc
		to.animationComplete(function() {
			from.add( to ).removeClass(" out in reverse " + transitions );
			from.removeClass( activePageClass );
			pageLoading( true );
			jQuery.fixedToolbars.show();
		});
	};

	jQuery(function() {
		$body = jQuery( "body" );
		pageLoading();

		// needs to be bound at domready (for IE6)
		// find or load content, make it active
		$window.bind( "hashchange", function(e) {
			var url = location.hash.replace( /^#/, "" ),
				stackLength = urlStack.length,
				// pageTransition only exists if the user clicked a link
				back = !pageTransition && stackLength > 1 &&
					urlStack[ stackLength - 2 ].url === url,
				transition = pageTransition || "slide",
				fileUrl = url;
			pageTransition = undefined;

			//reset base to pathname for new request
			resetBaseURL();

			// if the new href is the same as the previous one
			if ( back ) {
				transition = urlStack.pop().transition;
			} else {
				urlStack.push({ url: url, transition: transition });
			}

			//remove any pages that shouldn't cache
			jQuery(noCache).remove();

			//function for setting role of next page
			function setPageRole( newPage ) {
				if ( nextPageRole ) {
					newPage.attr( "data-role", nextPageRole );
					nextPageRole = undefined;
				}
			}

			if ( url ) {
				// see if content is present already
				var localDiv = jQuery( "[id='" + url + "']" );
				if ( localDiv.length ) {
					if ( localDiv.is( "[data-role]" ) ) {
						setPageRole( localDiv );
					}
					setBaseURL();
					mobilize( localDiv );
					changePage( jQuery( ".ui-page-active" ), localDiv, transition, back );
				} else { //ajax it in
					pageLoading();
					if(url.match( '&' + jQuery.mobile.subPageUrlKey )){
						fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0];
					}
					var newPage = jQuery( "
" )
						.appendTo( $body )
						.load( fileUrl + ' [data-role="page"]', function() {
							// TODO: test this (avoids querying the dom for new element):
//							var newPage = jQuery( this ).find( ".ui-page" ).eq( 0 )
//								.attr( "id", url );
//							jQuery( this ).replaceWith( newPage );
//							setPageRole( newPage );
//							mobilize( newPage );
//							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
							jQuery( this ).replaceWith(
								jQuery( this ).find( '[data-role="page"]' ).eq( 0 ).attr( "id", fileUrl ) );
							var newPage = jQuery( "[id='" + fileUrl + "']" );
							setPageRole( newPage );
							mobilize( newPage );
							newPage = jQuery( "[id='" + url + "']" );
							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
						});

						setBaseURL();
				}
			} else {
				// either we've backed up to the root page url
				// or it's the first page load with no hash present
				var currentPage = jQuery( ".ui-page-active" );
				if ( currentPage.length && !startPage.is( ".ui-page-active" ) ) {
					changePage( currentPage, startPage, transition, back );
				} else {
					startPage.addClass( activePageClass );
					jQuery.fixedToolbars.show();
					pageLoading( true );
				}
			}
		});

		hideBrowserChrome();
	});

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta - these will need to be configurable somehow.
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />'+
		'<link rel="apple-touch-icon" href="images/homeicon.png" />'+
		'<link rel="apple-touch-startup-image" href="images/startscreen.png" />'+
		'<meta name="apple-mobile-web-app-capable" content="yes" />' +
        '<meta name="apple-mobile-web-app-status-bar-style" content="default" />'+
        '<base  href="" id="ui-base" />');

    //set base href to pathname
    resetBaseURL();

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	jQuery.fn.animationComplete = function(callback){
		if(jQuery.support.WebKitAnimationEvent){
			return jQuery(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.not('[data-mobilized]').each(function(){

			var $el = $(this);

			//hide no-js content
			$el.find('[data-role="nojs"]').addClass('ui-nojs');

			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			$el.find('button, input[type=submit], input[type=reset], input[type=image]').customButton();
			$el.find('input[type=text],input[type=number],input[type=password],textarea').customTextInput();
			$el.find("input, select").filter('[data-role="slider"]').slider();
			$el.find('select').not('[data-role="slider"]').customSelect();

			/*temporary datepicker workaround */
			$el.find('input[type=date]').hide().after( $('
').datepicker() );
			$('.ui-datepicker-header').addClass('ui-body-c ui-corner-top').removeClass('ui-corner-all');
			$('.ui-datepicker-prev').buttonMarkup({iconPos: 'notext', icon: 'arrow-l', shadow: true, corners: true});
			$('.ui-datepicker-next').buttonMarkup({iconPos: 'notext', icon: 'arrow-r', shadow: true, corners: true});
			$('.ui-datepicker-calendar thead').remove();
			$('.ui-datepicker-calendar .ui-btn-text span').attr('class','');
			$('.ui-datepicker-calendar td').addClass('ui-btn-up-c');
			$('.ui-datepicker-calendar a').buttonMarkup({corners: false, shadow: false});
			/* //temporary datepicker workaround */

			//pre-find data els
			var $dataEls = $el.find('[data-role]').andSelf().each(function() {
				var $this = $(this),
					role = $this.attr("data-role");
				switch(role) {
				case 'header':
				case 'footer':
					$this.addClass('ui-bar-' + ($(this).data('theme') ? $(this).data('theme') : 'a'));
				case 'page':
				case 'content':
					$this.addClass('ui-' + role);
					break;
				case 'collapsible':
				case 'fieldcontain':
				case 'globalnav':
				case 'listview':
				case 'dialog':
				case 'ajaxform':
					$this[role]();
					break;
				case 'controlgroup':
					// FIXME for some reason this has to come before the form control stuff (see above)
					$this.controlgroup({
						direction: $this.attr("data-type")
					});
					break;
				}
			});

			//fix toolbars
			$el.fixHeaderFooter();

			//add back buttons to headers that don't have them
			// FIXME make that optional?
			// TODO don't do that on devices that have a native back button?
			var backBtn = $el.find('.ui-header a.ui-back');
			if(!backBtn.length){
				backBtn = jQuery('').appendTo($el.find('.ui-header'));
			}

			//buttons from links in headers,footers,bars, or with data-role
			$dataEls.filter('[data-role="button"]').add('.ui-header a, .ui-footer a, .ui-bar a').not('.ui-btn').buttonMarkup();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//make all back buttons mimic the back button (pre-js, these links are usually "home" links)
			backBtn
				.click(function(){
					// FIXME if you navigated from some page directly to subpage (ala .../#_form-controls.html), back button will actually go to the previous page, instead of the parent page
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);

			$el.attr('data-mobilized', true);
		});
	};

	jQuery.extend({
		mobilize: mobilize,
		pageLoading: pageLoading,
		changePage: changePage,
		hideBrowserChrome: hideBrowserChrome
	});

	//dom-ready
	jQuery(function(){

		//set up active page - mobilize it!
		startPage = jQuery('[data-role="page"]:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//mobilize all pages present
		mobilize(jQuery('[data-role="page"]'));

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( jQuery.event.special.orientationchange.orientation( $window ) );

		//some debug stuff for the events pages
		jQuery('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			jQuery('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});
})( jQuery, this );
<commit_msg>removed a superflous statement<commit_after>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( jQuery, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !jQuery.support.display || !jQuery.support.position || !jQuery.support.overflow || !jQuery.support.floatclear ) {
		return;
	}

	//these properties should be made easy to override externally
	jQuery.mobile = {};

	jQuery.extend(jQuery.mobile, {
		subPageUrlKey: 'ui-page' //define the key used in urls for sub-pages. Defaults to &ui-page=
	});

	var $window = jQuery(window),
		$html = jQuery('html'),
		$head = jQuery('head'),
		$body,
		$loader = jQuery('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		pageTransition,
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [ {
			url: location.hash.replace( /^#/, "" ),
			transition: "slide"
		} ],
		nextPageRole = null,
		noCache = '.ui-dialog';

	// hide address bar
	function hideBrowserChrome() {
		// prevent scrollstart and scrollstop events
		jQuery.event.special.scrollstart.enabled = false;
		window.scrollTo( 0, 0 );
		setTimeout(function() {
			jQuery.event.special.scrollstart.enabled = true;
		}, 150 );
	}

	function getBaseURL(){
	    var newBaseURL = location.hash.replace(/#/,'').split('/');
		if(newBaseURL.length && /[.|&]/.test(newBaseURL[newBaseURL.length-1]) ){
			newBaseURL.pop();
		}
		newBaseURL = newBaseURL.join('/');
		if(newBaseURL !== "" && newBaseURL.charAt(newBaseURL.length-1) !== '/'){  newBaseURL += '/'; }
		return newBaseURL;
	}

	function setBaseURL(){
		//set base url for new page assets
		$('#ui-base').attr('href', getBaseURL());
	}

	function resetBaseURL(){
		$('#ui-base').attr('href', location.pathname);
	}

	// send a link through hash tracking
	jQuery.fn.ajaxClick = function() {
		var href = jQuery( this ).attr( "href" );
		pageTransition = jQuery( this ).attr( "data-transition" ) || "slide";
		nextPageRole = jQuery( this ).attr( "data-rel" );

		//find new base for url building
		var newBaseURL = getBaseURL();

		//if href is absolute but local, or a local ID, no base needed
		if( /^\//.test(href) || (/https?:\/\//.test(href) && !!(href).match(location.hostname)) || /^#/.test(href) ){
			newBaseURL = '';
		}

		// set href to relative path using baseURL and
		if( !/https?:\/\//.test(href) ){
			href = newBaseURL + href;
		}

		//if it's a non-local-anchor and Ajax is not supported, or if it's an external link, go to page without ajax
		if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) {
			location = href
		}
		else{
			// let the hashchange event handler take care of requesting the page via ajax
			location.hash = href;
		}
		return this;
	};

	// ajaxify all navigable links
	jQuery( "a:not([href=#]):not([target]):not([rel=external])" ).live( "click", function(event) {
		jQuery( this ).ajaxClick();
		return false;
	});

	// turn on/off page loading message.. also hides the ui-content div
	function pageLoading( done ) {
		if ( done ) {
			$html.removeClass( "ui-loading" );
			//fade in page content, remove loading msg
			jQuery('.ui-page-active .ui-content').addClass('dissolve in');
		} else {
			$html.addClass( "ui-loading" );
			$loader.appendTo( $body ).addClass( "dissolve in" );
		}
	};

	// transition between pages - based on transitions from jQtouch
	function changePage( from, to, transition, back ) {
		hideBrowserChrome();

		// kill keyboard (thx jQtouch :) )
		jQuery( document.activeElement ).blur();

		// animate in / out
		from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
		to.appendTo($body).addClass( activePageClass + " " + transition +
			" in " + ( back ? "reverse" : "" ) );

		// callback - remove classes, etc
		to.animationComplete(function() {
			from.add( to ).removeClass(" out in reverse " + transitions );
			from.removeClass( activePageClass );
			pageLoading( true );
			jQuery.fixedToolbars.show();
		});
	};

	jQuery(function() {
		$body = jQuery( "body" );
		pageLoading();

		// needs to be bound at domready (for IE6)
		// find or load content, make it active
		$window.bind( "hashchange", function(e) {
			var url = location.hash.replace( /^#/, "" ),
				stackLength = urlStack.length,
				// pageTransition only exists if the user clicked a link
				back = !pageTransition && stackLength > 1 &&
					urlStack[ stackLength - 2 ].url === url,
				transition = pageTransition || "slide",
				fileUrl = url;
			pageTransition = undefined;

			//reset base to pathname for new request
			resetBaseURL();

			// if the new href is the same as the previous one
			if ( back ) {
				transition = urlStack.pop().transition;
			} else {
				urlStack.push({ url: url, transition: transition });
			}

			//remove any pages that shouldn't cache
			jQuery(noCache).remove();

			//function for setting role of next page
			function setPageRole( newPage ) {
				if ( nextPageRole ) {
					newPage.attr( "data-role", nextPageRole );
					nextPageRole = undefined;
				}
			}

			if ( url ) {
				// see if content is present already
				var localDiv = jQuery( "[id='" + url + "']" );
				if ( localDiv.length ) {
					if ( localDiv.is( "[data-role]" ) ) {
						setPageRole( localDiv );
					}
					setBaseURL();
					mobilize( localDiv );
					changePage( jQuery( ".ui-page-active" ), localDiv, transition, back );
				} else { //ajax it in
					pageLoading();
					if(url.match( '&' + jQuery.mobile.subPageUrlKey )){
						fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0];
					}
					var newPage = jQuery( "
" )
						.appendTo( $body )
						.load( fileUrl + ' [data-role="page"]', function() {
							// TODO: test this (avoids querying the dom for new element):
//							var newPage = jQuery( this ).find( ".ui-page" ).eq( 0 )
//								.attr( "id", url );
//							jQuery( this ).replaceWith( newPage );
//							setPageRole( newPage );
//							mobilize( newPage );
//							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
							jQuery( this ).replaceWith(
								jQuery( this ).find( '[data-role="page"]' ).eq( 0 ).attr( "id", fileUrl ) );
							var newPage = jQuery( "[id='" + fileUrl + "']" );
							setPageRole( newPage );
							mobilize( newPage );
							newPage = jQuery( "[id='" + url + "']" );
							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
						});

						setBaseURL();
				}
			} else {
				// either we've backed up to the root page url
				// or it's the first page load with no hash present
				var currentPage = jQuery( ".ui-page-active" );
				if ( currentPage.length && !startPage.is( ".ui-page-active" ) ) {
					changePage( currentPage, startPage, transition, back );
				} else {
					startPage.addClass( activePageClass );
					jQuery.fixedToolbars.show();
					pageLoading( true );
				}
			}
		});

		hideBrowserChrome();
	});

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta - these will need to be configurable somehow.
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />'+
		'<link rel="apple-touch-icon" href="images/homeicon.png" />'+
		'<link rel="apple-touch-startup-image" href="images/startscreen.png" />'+
		'<meta name="apple-mobile-web-app-capable" content="yes" />' +
        '<meta name="apple-mobile-web-app-status-bar-style" content="default" />'+
        '<base  href="" id="ui-base" />');

    //set base href to pathname
    resetBaseURL();

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	jQuery.fn.animationComplete = function(callback){
		if(jQuery.support.WebKitAnimationEvent){
			return jQuery(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.not('[data-mobilized]').each(function(){

			var $el = $(this);

			//hide no-js content
			$el.find('[data-role="nojs"]').addClass('ui-nojs');

			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			$el.find('button, input[type=submit], input[type=reset], input[type=image]').customButton();
			$el.find('input[type=text],input[type=number],input[type=password],textarea').customTextInput();
			$el.find("input, select").filter('[data-role="slider"]').slider();
			$el.find('select').not('[data-role="slider"]').customSelect();

			/*temporary datepicker workaround */
			$el.find('input[type=date]').hide().after( $('
').datepicker() );
			$('.ui-datepicker-header').addClass('ui-body-c ui-corner-top').removeClass('ui-corner-all');
			$('.ui-datepicker-prev').buttonMarkup({iconPos: 'notext', icon: 'arrow-l', shadow: true, corners: true});
			$('.ui-datepicker-next').buttonMarkup({iconPos: 'notext', icon: 'arrow-r', shadow: true, corners: true});
			$('.ui-datepicker-calendar thead').remove();
			$('.ui-datepicker-calendar .ui-btn-text span').attr('class','');
			$('.ui-datepicker-calendar td').addClass('ui-btn-up-c');
			$('.ui-datepicker-calendar a').buttonMarkup({corners: false, shadow: false});
			/* //temporary datepicker workaround */

			//pre-find data els
			var $dataEls = $el.find('[data-role]').andSelf().each(function() {
				var $this = $(this),
					role = $this.attr("data-role");
				switch(role) {
				case 'header':
				case 'footer':
					$this.addClass('ui-bar-' + ($(this).data('theme') ? $(this).data('theme') : 'a'));
				case 'page':
				case 'content':
					$this.addClass('ui-' + role);
					break;
				case 'collapsible':
				case 'fieldcontain':
				case 'globalnav':
				case 'listview':
				case 'dialog':
				case 'ajaxform':
					$this[role]();
					break;
				case 'controlgroup':
					// FIXME for some reason this has to come before the form control stuff (see above)
					$this.controlgroup({
						direction: $this.attr("data-type")
					});
					break;
				}
			});

			//fix toolbars
			$el.fixHeaderFooter();

			//add back buttons to headers that don't have them
			// FIXME make that optional?
			// TODO don't do that on devices that have a native back button?
			var backBtn = $el.find('.ui-header a.ui-back');
			if(!backBtn.length){
				backBtn = jQuery('').appendTo($el.find('.ui-header'));
			}

			//buttons from links in headers,footers,bars, or with data-role
			$dataEls.filter('[data-role="button"]').add('.ui-header a, .ui-footer a, .ui-bar a').not('.ui-btn').buttonMarkup();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//make all back buttons mimic the back button (pre-js, these links are usually "home" links)
			backBtn
				.click(function(){
					// FIXME if you navigated from some page directly to subpage (ala .../#_form-controls.html), back button will actually go to the previous page, instead of the parent page
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);

			$el.attr('data-mobilized', true);
		});
	};

	jQuery.extend({
		mobilize: mobilize,
		pageLoading: pageLoading,
		changePage: changePage,
		hideBrowserChrome: hideBrowserChrome
	});

	//dom-ready
	jQuery(function(){

		//set up active page - mobilize it!
		startPage = jQuery('[data-role="page"]:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//mobilize all pages present
		mobilize(jQuery('[data-role="page"]'));

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( jQuery.event.special.orientationchange.orientation( $window ) );

		//some debug stuff for the events pages
		jQuery('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			jQuery('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});
})( jQuery, this );
<|endoftext|>


<commit_before>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( $, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !$.support.display || !$.support.position || !$.support.overflow || !$.support.floatclear ) {
		return;
	}

	var $window = $(window),
		$html = $('html'),
		$head = $('head'),
		$body,
		$loader = $('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionSpecified = false,
		currentTransition = 'slide',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [location.hash.replace(/^#/,'')];

	/*
		add some core behavior,events
	*/

	//test whether a CSS media type or query applies (adapted from work by Scott Jehl & Paul Irish: http://gist.github.com/557891)
	$.media = (function(){
		/*
		note: once support improves, try window.matchMedia here
		if ( window.matchMedia ){
			//use native support if available
			return window.matchMedia;
		}
		*/
		var cache = {},
	        testDiv = $('

'),
	        fakeBody = $('').append(testDiv);
	    return function(q){
	      if (cache[q] === undefined) {
	        var styleBlock = $('');
	        var cssrule = '@media '+q+' { #jq-mediatest { position: absolute; } }';
	        if (styleBlock[0].styleSheet){
	            styleBlock[0].styleSheet.cssText = cssrule;
	        }
	        else {
	            styleBlock.text(cssrule);
	        }
	        $html.prepend(fakeBody).prepend(styleBlock);
	        cache[q] = ((window.getComputedStyle ? window.getComputedStyle(testDiv[0],null) : testDiv[0].currentStyle)['position'] == 'absolute');
	        fakeBody.add(styleBlock).remove();
	      }
	      return cache[q];
	    };
	})();

	//hide Address bar
	function hideBrowserChrome(){
		$.event.special.scrollstart.enabled = false;
		window.scrollTo(0,0);
		setTimeout(function(){
			$.event.special.scrollstart.enabled = true;
		}, 150);
	}

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta (any other metas needed? webapp? iPhone icon? etc)
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />');

	//send a link through hash tracking
	$.fn.ajaxClick = function(){
		var href = $(this).attr( "href" ),
			transitionAttr = $(this).attr('data-transition');

		if(transitionAttr){
			currentTransition = transitionAttr;
			transitionSpecified = true;
		}
		else{
			transitionSpecified = false;
		}

		location.hash = href;

		//note: if it's a non-local-anchor and Ajax is not supported, go to page
		if(href.match(/^[^#]/) && !$.support.ajax){
			window.location = href;
		}
		else {
			return false;
		}
	};

	//ajaxify all navigable links
	$('a:not([href="#"])').live('click',function(e){
		if ($(this).is('[target=_blank]') || $(this).is('[rel=external]')){ return true; }
		$(this).ajaxClick();
		return false;
	});

	//turn on/off page loading message.. also hides the ui-content div
	function pageLoading(done){
		if(done){
			//remove loading msg
			$html.removeClass('ui-loading');
			//fade in page content, remove loading msg
			$('.ui-page-active .ui-content')//.addClass('dissolve in');
		}
		else{
			$html.addClass('ui-loading');
			$loader.appendTo($body).addClass('dissolve in');
		}
	};

	//transition between pages - based on transitions from jQtouch
	function changePage(from,to,back){
		hideBrowserChrome();
		if(!back && !transitionSpecified){ currentTransition = 'slide'; }

		//kill keyboard (thx jQtouch :) )
		$(document.activeElement).blur();

		//animate in / out
		from.addClass(currentTransition + ' out ' + (back ? 'reverse':''));
		to.appendTo('body').addClass(activePageClass + ' ' + currentTransition + ' in ' + (back ? 'reverse':''));

		//callback - remove classes, etc
		to.animationComplete(function(){
			from.add(to).removeClass(' out in reverse '+ transitions);
			from.removeClass(activePageClass);
			pageLoading(true);
			$.fixedToolbars.show();
		});
		if(back){
			currentTransition = 'slide';
		}
	};

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	$.fn.animationComplete = function(callback){
		if($.support.WebKitAnimationEvent){
			return $(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.each(function(){
			//checkboxes, radios
			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			//custom buttons
			$el.find('button, input[type=submit]').customButton();
			//custom text inputs
			$el.find('input[type=text],input[type=password],textarea').customTextInput();
			//collapsible groupings
			$el.find('[data-role="collapsible"]').collapsible();
			//single-field separators
			$el.find('.field').fieldcontain();
			//selects
			$el.find('select').customSelect();
			//fix toolbars
			$el.fixHeaderFooter();
			//buttons from links in headers,footers,bars, or with data-role
			$el.find('.ui-header a, .ui-footer a, .ui-bar a, [data-role="button"]').not('.ui-btn').buttonMarkup();
			//vertical controlgroups
			$el.find('[data-role="controlgroup"]:not([data-type="horizontal"])').controlgroup();
			//horizontal controlgroups
			$el.find('[data-role="controlgroup"][data-type="horizontal"]').controlgroup({direction: 'horizontal'});
			//listview from data role
			$el.find('[data-role="listview"]').listview();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//rewrite "home" links to mimic the back button (pre-js, these links are usually "home" links)
			var backBtn = $el.find('.ui-header a.ui-back');
			if(!backBtn.length){
				backBtn = $('').appendTo($el.find('.ui-header')).buttonMarkup();
			}
			backBtn
				.click(function(){
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);
		});
	};

	//dom-ready
	$(function(){
		//set the page loader up
		pageLoading();

		//define body
		$body = $('body');

		//hashchange for page state tracking - needs to be bound at domready (for IE6)
		//When document.location.hash changes, find or load content, make it active
		$window.bind( "hashchange", function(e){
			var url = location.hash.replace(/^#/,''),
				back = (url === urlStack[urlStack.length-2]);

			//if the new href is the same as the previous one
			if(back){
				urlStack.pop();
			}
			else {
				urlStack.push(url);
			}

			if(url){
				//see if content is present already
				var localDiv = $('[id="'+url+'"]');
				if(localDiv.length){
					changePage($('.ui-page-active'), localDiv, back);
				}
				else { //ajax it in
					pageLoading();
					var newPage = $('
')
						.appendTo($body)
						.load(url + ' .ui-page',function(){
							$(this).replaceWith( $(this).find('.ui-page:eq(0)').attr('id', url) );
							var newPage = $('[id="'+url+'"]');
							mobilize(newPage);
							changePage($('.ui-page-active'), newPage, back);
						});
				}
			}
			else{
				//either...
				// we've backed up to the root page url,
				// it's a plugin state
				//or it's the first page load with no hash present
				var currentPage = $('.ui-page-active');
				if( currentPage.length && !startPage.is('.ui-page-active')){
					changePage(currentPage, startPage, back);
				}
				else{
					$.fixedToolbars.show();
					startPage.addClass(activePageClass);
					pageLoading(true);
				}
			}
		});

		hideBrowserChrome();

		//global nav
		$('.ui-globalnav').globalnav();

		//mobilize all pages present
		mobilize($('.ui-page'));

		//set up active page - mobilize it!
		startPage = $('body > .ui-page:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( $.event.special.orientationchange.orientation( $window ) );

		//swipe right always triggers a back
		$body.bind('swiperight.jqm',function(){
			history.go(-1);
			return false;
		});

		//some debug stuff for the events pages
		$('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			$('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});
})( jQuery, this );
<commit_msg>clarified comment<commit_after>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( $, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !$.support.display || !$.support.position || !$.support.overflow || !$.support.floatclear ) {
		return;
	}

	var $window = $(window),
		$html = $('html'),
		$head = $('head'),
		$body,
		$loader = $('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionSpecified = false,
		currentTransition = 'slide',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [location.hash.replace(/^#/,'')];

	/*
		add some core behavior,events
	*/

	//test whether a CSS media type or query applies (adapted from work by Scott Jehl & Paul Irish: http://gist.github.com/557891)
	$.media = (function(){
		/*
		note: once support improves, try window.matchMedia here
		if ( window.matchMedia ){
			//use native support if available
			return window.matchMedia;
		}
		*/
		var cache = {},
	        testDiv = $('

'),
	        fakeBody = $('').append(testDiv);
	    return function(q){
	      if (cache[q] === undefined) {
	        var styleBlock = $('');
	        var cssrule = '@media '+q+' { #jq-mediatest { position: absolute; } }';
	        if (styleBlock[0].styleSheet){
	            styleBlock[0].styleSheet.cssText = cssrule;
	        }
	        else {
	            styleBlock.text(cssrule);
	        }
	        $html.prepend(fakeBody).prepend(styleBlock);
	        cache[q] = ((window.getComputedStyle ? window.getComputedStyle(testDiv[0],null) : testDiv[0].currentStyle)['position'] == 'absolute');
	        fakeBody.add(styleBlock).remove();
	      }
	      return cache[q];
	    };
	})();

	//hide Address bar
	function hideBrowserChrome(){
		$.event.special.scrollstart.enabled = false;
		window.scrollTo(0,0);
		setTimeout(function(){
			$.event.special.scrollstart.enabled = true;
		}, 150);
	}

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta (any other metas needed? webapp? iPhone icon? etc)
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />');

	//send a link through hash tracking
	$.fn.ajaxClick = function(){
		var href = $(this).attr( "href" ),
			transitionAttr = $(this).attr('data-transition');

		if(transitionAttr){
			currentTransition = transitionAttr;
			transitionSpecified = true;
		}
		else{
			transitionSpecified = false;
		}

		location.hash = href;

		//note: if it's a non-local-anchor and Ajax is not supported, go to page
		if(href.match(/^[^#]/) && !$.support.ajax){
			window.location = href;
		}
		else {
			return false;
		}
	};

	//ajaxify all navigable links
	$('a:not([href="#"])').live('click',function(e){
		if ($(this).is('[target=_blank]') || $(this).is('[rel=external]')){ return true; }
		$(this).ajaxClick();
		return false;
	});

	//turn on/off page loading message.. also hides the ui-content div
	function pageLoading(done){
		if(done){
			//remove loading msg
			$html.removeClass('ui-loading');
			//fade in page content, remove loading msg
			$('.ui-page-active .ui-content')//.addClass('dissolve in');
		}
		else{
			$html.addClass('ui-loading');
			$loader.appendTo($body).addClass('dissolve in');
		}
	};

	//transition between pages - based on transitions from jQtouch
	function changePage(from,to,back){
		hideBrowserChrome();
		if(!back && !transitionSpecified){ currentTransition = 'slide'; }

		//kill keyboard (thx jQtouch :) )
		$(document.activeElement).blur();

		//animate in / out
		from.addClass(currentTransition + ' out ' + (back ? 'reverse':''));
		to.appendTo('body').addClass(activePageClass + ' ' + currentTransition + ' in ' + (back ? 'reverse':''));

		//callback - remove classes, etc
		to.animationComplete(function(){
			from.add(to).removeClass(' out in reverse '+ transitions);
			from.removeClass(activePageClass);
			pageLoading(true);
			$.fixedToolbars.show();
		});
		if(back){
			currentTransition = 'slide';
		}
	};

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	$.fn.animationComplete = function(callback){
		if($.support.WebKitAnimationEvent){
			return $(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.each(function(){
			//checkboxes, radios
			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			//custom buttons
			$el.find('button, input[type=submit]').customButton();
			//custom text inputs
			$el.find('input[type=text],input[type=password],textarea').customTextInput();
			//collapsible groupings
			$el.find('[data-role="collapsible"]').collapsible();
			//single-field separators
			$el.find('.field').fieldcontain();
			//selects
			$el.find('select').customSelect();
			//fix toolbars
			$el.fixHeaderFooter();
			//buttons from links in headers,footers,bars, or with data-role
			$el.find('.ui-header a, .ui-footer a, .ui-bar a, [data-role="button"]').not('.ui-btn').buttonMarkup();
			//vertical controlgroups
			$el.find('[data-role="controlgroup"]:not([data-type="horizontal"])').controlgroup();
			//horizontal controlgroups
			$el.find('[data-role="controlgroup"][data-type="horizontal"]').controlgroup({direction: 'horizontal'});
			//listview from data role
			$el.find('[data-role="listview"]').listview();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//rewrite "home" links to mimic the back button (pre-js, these links are usually "home" links)
			var backBtn = $el.find('.ui-header a.ui-back');
			if(!backBtn.length){
				backBtn = $('').appendTo($el.find('.ui-header')).buttonMarkup();
			}
			backBtn
				.click(function(){
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);
		});
	};

	//dom-ready
	$(function(){
		//set the page loader up
		pageLoading();

		//define body
		$body = $('body');

		//hashchange for page state tracking - needs to be bound at domready (for IE6)
		//When document.location.hash changes, find or load content, make it active
		$window.bind( "hashchange", function(e){
			var url = location.hash.replace(/^#/,''),
				back = (url === urlStack[urlStack.length-2]);

			//if the new href is the same as the previous one
			if(back){
				urlStack.pop();
			}
			else {
				urlStack.push(url);
			}

			if(url){
				//see if content is present already
				var localDiv = $('[id="'+url+'"]');
				if(localDiv.length){
					changePage($('.ui-page-active'), localDiv, back);
				}
				else { //ajax it in
					pageLoading();
					var newPage = $('
')
						.appendTo($body)
						.load(url + ' .ui-page',function(){
							$(this).replaceWith( $(this).find('.ui-page:eq(0)').attr('id', url) );
							var newPage = $('[id="'+url+'"]');
							mobilize(newPage);
							changePage($('.ui-page-active'), newPage, back);
						});
				}
			}
			else{
				//either we've backed up to the root page url, or it's the first page load with no hash present
				var currentPage = $('.ui-page-active');
				if( currentPage.length && !startPage.is('.ui-page-active')){
					changePage(currentPage, startPage, back);
				}
				else{
					$.fixedToolbars.show();
					startPage.addClass(activePageClass);
					pageLoading(true);
				}
			}
		});

		hideBrowserChrome();

		//global nav
		$('.ui-globalnav').globalnav();

		//mobilize all pages present
		mobilize($('.ui-page'));

		//set up active page - mobilize it!
		startPage = $('body > .ui-page:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( $.event.special.orientationchange.orientation( $window ) );

		//swipe right always triggers a back
		$body.bind('swiperight.jqm',function(){
			history.go(-1);
			return false;
		});

		//some debug stuff for the events pages
		$('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			$('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});
})( jQuery, this );
<|endoftext|>


<commit_before>"use strict";
var wrap = require("word-wrap");
var s = require("string-tools");
var a = require("array-tools");
var o = require("object-tools");

module.exports = columns;

function columns(data, options){
    options = options || {};
    if (data.options && data.data){
        options = o.extend(data.options, options);
        data = data.data;
    }
    var columns = options.columns;
    return data.reduce(function(prev, row){
        return prev + Object.keys(row).reduce(function(prev, col){
            var colValue = wrap(String(row[col] || ""), { width: getWidth(columns, col), trim: true }).split(/\n/);
            if (prev !== null){
                var prevSplit = prev.split(/\n/);
                for (var i = 0; i < Math.max(colValue.length, prevSplit.length); i++){
                    colValue[i] = getVal(columns, col, prevSplit[i]) + getVal(columns, col, colValue[i]);
                }
            }
            return colValue.join("\n");
        }, null) + "\n";
    }, "");
}

function getVal(columns, col, val){
    return s.padRight(val || "", getWidth(columns, col) + 2);
}

function getWidth(columns, col){
    var column = a.findWhere(columns, { name: col });
    return (column && column.width) || 40;
}<commit_msg>refactor<commit_after>"use strict";
var wrap = require("word-wrap");
var s = require("string-tools");
var a = require("array-tools");
var o = require("object-tools");

module.exports = columnLayout;

function columnLayout(data, options){
    var cols = Columns(data, options);
    return cols.toString();
}

function Columns(data, options){
    if (!(this instanceof Columns)) return new Columns(data, options);
    options = options || {};
    if (!Array.isArray(data)){
        if (data.options && data.data){
            options = o.extend(data.options, options);
            data = data.data;
        } else {
            throw new Error("Invalid input data");
        }
    }
    this.options = options;
    this.data = data;
}

Columns.prototype.toString = function(){
    var self = this;
    return this.data.reduce(function(prev, row){
        return prev + Object.keys(row).reduce(function(prev, col){
            var colValue = wrap(String(row[col] || ""), { width: self.getWidth(col), trim: true }).split(/\n/);
            if (prev !== null){
                var prevSplit = prev.split(/\n/);
                for (var i = 0; i < Math.max(colValue.length, prevSplit.length); i++){
                    colValue[i] = self.getVal(col, prevSplit[i]) + self.getVal(col, colValue[i]);
                }
            }
            return colValue.join("\n");
        }, null) + "\n";
    }, "");
};

Columns.prototype.getVal = function(col, val){
    return s.padRight(val || "", this.getWidth(col) + 2);
};

Columns.prototype.getWidth = function(col){
    var column = a.findWhere(this.options.columns, { name: col });
    return (column && column.width) || 40;
};
<|endoftext|>