<commit_before>/*
 * Copyright (c) 2016 Untu, Inc.
 * This code is licensed under Eclipse Public License - v 1.0.
 * The full license text can be found in LICENSE.txt file and
 * on the Eclipse official site (https://www.eclipse.org/legal/epl-v10.html).
 */

'use strict';

var actors = require('../index');
var expect = require('chai').expect;
var P = require('bluebird');

var system;

describe('Resource injection', function() {
  afterEach(P.coroutine(function*() {
    if (system) {
      yield system.destroy();
    }

    system = null;
  }));

  it('should inject resource into an in-memory actor', P.coroutine(function*() {
    /**
     * Test resource.
     */
    class MessageResource {
      static getName() {
        return 'message-text';
      }

      getResource() {
        return 'Hi there!';
      }
    }

    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['message-text'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: [MessageResource]
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should inject resource into a forked actor', P.coroutine(function*() {
    /**
     * Test resource.
     */
    class MessageResource {
      static getName() {
        return 'message-text';
      }

      getResource() {
        return 'Hi there!';
      }
    }

    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['message-text'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: [MessageResource]
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor, { mode: 'forked' }));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should run resource lifecycle hooks for used resources', P.coroutine(function*() {
    var messageResourceInitialized = false;
    var messageResourceDestroyed = false;
    var unusedResourceInitialized = false;
    var unusedResourceDestroyed = false;

    /**
     * Test resource.
     */
    class MessageResource {
      static getName() {
        return 'message-text';
      }

      initialize() {
        this.text = 'Hi there!';
        messageResourceInitialized = true;
      }

      destroy() {
        messageResourceDestroyed = true;
      }

      getResource() {
        return this.text;
      }
    }

    /**
     * Test unused resource.
     */
    class UnusedResource {
      static getName() {
        return 'unused';
      }

      initialize() {
        unusedResourceInitialized = true;
      }

      destroy() {
        unusedResourceDestroyed = true;
      }
    }

    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['message-text'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    var system = actors({
      test: true,
      resources: [MessageResource, UnusedResource]
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');

    yield system.destroy();

    expect(messageResourceInitialized).to.be.equal(true);
    expect(messageResourceDestroyed).to.be.equal(true);
    expect(unusedResourceInitialized).to.be.equal(false);
    expect(unusedResourceDestroyed).to.be.equal(false);
  }));

  it('should support module-defined resources for in-memory actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-message-resource']
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should support module-defined resources for forked actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-message-resource']
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor, { mode: 'forked' }));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should support TypeScript module-defined resources for in-memory actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-typescript-message-resource'],
      additionalRequires: 'ts-node/register'
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should support TypeScript module-defined resources for forked actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-typescript-message-resource'],
      additionalRequires: 'ts-node/register'
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor, { mode: 'forked' }));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));
});<commit_msg>(saymon) Added failing test for plain-object resources.<commit_after>/*
 * Copyright (c) 2016 Untu, Inc.
 * This code is licensed under Eclipse Public License - v 1.0.
 * The full license text can be found in LICENSE.txt file and
 * on the Eclipse official site (https://www.eclipse.org/legal/epl-v10.html).
 */

'use strict';

var actors = require('../index');
var expect = require('chai').expect;
var P = require('bluebird');
var _ = require('underscore');

var system;

describe('Resource injection', function() {
  afterEach(P.coroutine(function*() {
    if (system) {
      yield system.destroy();
    }

    system = null;
  }));

  it('should inject resource into an in-memory actor', P.coroutine(function*() {
    /**
     * Test resource.
     */
    class MessageResource {
      static getName() {
        return 'message-text';
      }

      getResource() {
        return 'Hi there!';
      }
    }

    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['message-text'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: [MessageResource]
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should inject resource into a forked actor', P.coroutine(function*() {
    /**
     * Test resource.
     */
    class MessageResource {
      static getName() {
        return 'message-text';
      }

      getResource() {
        return 'Hi there!';
      }
    }

    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['message-text'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: [MessageResource]
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor, { mode: 'forked' }));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should run resource lifecycle hooks for used resources', P.coroutine(function*() {
    var messageResourceInitialized = false;
    var messageResourceDestroyed = false;
    var unusedResourceInitialized = false;
    var unusedResourceDestroyed = false;

    /**
     * Test resource.
     */
    class MessageResource {
      static getName() {
        return 'message-text';
      }

      initialize() {
        this.text = 'Hi there!';
        messageResourceInitialized = true;
      }

      destroy() {
        messageResourceDestroyed = true;
      }

      getResource() {
        return this.text;
      }
    }

    /**
     * Test unused resource.
     */
    class UnusedResource {
      static getName() {
        return 'unused';
      }

      initialize() {
        unusedResourceInitialized = true;
      }

      destroy() {
        unusedResourceDestroyed = true;
      }
    }

    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['message-text'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    var system = actors({
      test: true,
      resources: [MessageResource, UnusedResource]
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');

    yield system.destroy();

    expect(messageResourceInitialized).to.be.equal(true);
    expect(messageResourceDestroyed).to.be.equal(true);
    expect(unusedResourceInitialized).to.be.equal(false);
    expect(unusedResourceDestroyed).to.be.equal(false);
  }));

  it('should support module-defined resources for in-memory actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-message-resource']
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should support module-defined resources for forked actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-message-resource']
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor, { mode: 'forked' }));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should support TypeScript module-defined resources for in-memory actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-typescript-message-resource'],
      additionalRequires: 'ts-node/register'
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should support TypeScript module-defined resources for forked actor', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['MessageResource'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: ['/test-resources/test-typescript-message-resource'],
      additionalRequires: 'ts-node/register'
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor, { mode: 'forked' }));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));

  it('should support plain-object resources', P.coroutine(function*() {
    /**
     * Test actor, that uses test resource.
     */
    class MyActor {
      static inject() {
        return ['message-text'];
      }

      constructor(message) {
        this.message = message;
      }

      hello() {
        return this.message;
      }
    }

    system = actors({
      test: true,
      resources: [{
        getName: _.constant('message-text'),
        getResource: _.constant('Hi there!')
      }]
    });

    var actor = yield system.rootActor().then(rootActor => rootActor.createChild(MyActor));

    var response = yield actor.sendAndReceive('hello');

    expect(response).to.be.equal('Hi there!');
  }));
});<|endoftext|>


<commit_before>'use strict';

module.exports = {
	tabList: {
		title: '付费情况',
		subList: [
			{
				title: '充值收入',
				key: 'count',
				data: [
					{
						'meta_id': '2819',
						'name': '充值收入 ',
						'self_weidu': 'ALL'
					}
				]
			},
			{
				title: '付费角色',
				key: 'cash',
				data: [
					{
						'meta_id': '2818',
						'name': '付费角色 '
					},
					{
						'meta_id': '2914',
						'name': '首次付费角色数 '
					}
				]
			},
			{
				title: '付费率',
				key: 'cash_percent',
				role: 'true',
				data: [
					{
						'meta_id': '2874',
						'name': '角色付费率 ',
						'num_type': 'percent'
					},
					{
						'meta_id': '2816',
						'name': '登录角色'
					},
				]
			},
			{
				title: 'ARRPU',
				key: 'arrpu',
				role: 'true',
				data: [
					{
						'meta_id': '2816',
						'name': '角色ARPU',
						'num_type': 'fixed_2'
					},
					{
						'meta_id': '2875',
						'name': '角色ARPPU',
						'num_type': 'fixed_2'
					},
				]
			},
			{
				title: 'VIP等级分布',
				key: 'vip',
				dimensionName: 'VIP等级',
				data: [
					{
						'meta_id': '2916',
						'name': '收入'
					},
					{
						'meta_id': '2917',
						'name': 'VIP人数'
					},
				]
			},
			{
				title: '充值额度分布',
				key: 'cash_limit',
				dimensionName: '充值项目',
				data: [
					{
						'meta_id': '2918',
						'name': '现金'
					},
					{
						'meta_id': '2919',
						'name': '角色数'
					},
					{
						'meta_id': '2920',
						'name': '元宝'
					},
					{
						'meta_id': '2921',
						'name': '充值次数'
					},
				]
			},
			{
				title: '单位时间价格',
				key: 'price_date',
				data: [
					{
						'meta_id': '2922',
						'name': '单位时间（价格/小时）',
						'num_type': 'fixed_2'
					},
					{
						'meta_id': '2923',
						'name': '7日平均',
						'num_type': 'fixed_2'
					},
					{
						'meta_id': '2924',
						'name': '30日平均',
						'num_type': 'fixed_2'
					},
				]
			}
		]
	},
	playUser: {
		title: '玩家分析',
		subList: [
			// {
			// 	title: '各服务器玩家分布',
			// 	key: 'play_map',
			// 	dimensionName: '服务器',
			// 	stockItem: ['登录角色数', '新增角色数'],
			// 	data: [
			// 		{
			// 			'meta_id': '2925',
			// 			'name': '新增角色数'
			// 		},
			// 		{
			// 			'meta_id': '2926',
			// 			'name': '登录角色数'
			// 		}
			// 	]
			// },
			{
				title: '平均启动次数',
				key: 'start_num',
				lineItem: ['平均启动次数'],
				stockItem: ['平均启动次数'],
				chartShow: 'single',
				data: [
					{
						'meta_id': '2927',
						'name': '平均启动次数',
						'num_type': 'fixed_2'
					}
				]
			},
			{
				title: '等级分布',
				key: 'level',
				dimensionName: '等级',
				lineItem: ['等级分布'],
				stockItem: ['等级分布'],
				chartShow: 'single',
				data: [
					{
						'meta_id': '2928',
						'name': '等级分布',

					}
				]
			}
		]
	},
	channelAnalysis: {
		title: '渠道分析',
		role: true,
		subList: [
			{
				title: '登录',
				key: 'play_login',
				dimensionName: '渠道',
				showRange: ['登录角色', '新增角色数'],
				stockItem: ['登录角色', '新增角色数'],
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			},
			{
				title: '付费',
				key: 'play_cash',
				dimensionName: '渠道',
				stockItem: ['充值总金额'],
				lineItem: ['付费率', '渠道充值占比'],
				percent: true,
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			},
			{
				title: 'ARPU',
				key: 'play_arpu',
				dimensionName: '渠道',
				stockItem: ['ARPU', 'ARPPU'],
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			},
			{
				title: '留存',
				key: 'play_save',
				dimensionName: '渠道',
				stockItem: ['新增角色数'],
				lineItem: ['次日留存', '第3日留存', '第7日留存'],
				chartShow:'line',
				percent: true,
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			}
		]
	},
	economy: {
		title: '经济情况',
		subList: [
			{
				title: '元宝回收产出比',
				key: 'gold_back',
				stockItem: ['元宝回收量', '元宝产出量'],
				lineItem: ['元宝回收产出比'],
				data: [
					{
						'meta_id': '2939',
						'name': '元宝回收量',
					},
					{
						'meta_id': '2940',
						'name': '元宝产出量',
					},
					{
						'meta_id': '2941',
						'num_type':'fixed_2',
						'name': '元宝回收产出比',
					}
				]
			},
			{
				title: '元宝产出途径',
				key: 'gold_product',
				dimensionName: '途径',
				stockItem: ['产出元宝数量'],
				lineItem: ['产出元宝角色数', '产出元宝次数'],
				chartShow:'line',
				data: [
					{
						'meta_id': '2942',
						'name': '产出元宝数量',
					},
					{
						'meta_id': '2943',
						'name': '产出元宝次数',
					},
					{
						'meta_id': '2944',
						'name': '产出元宝角色数',
					}
				]
			},
			{
				title: '元宝回收途径',
				key: 'gold_back_approach',
				dimensionName: '途径',
				stockItem: ['回收元宝数量'],
				lineItem: ['回收元宝次数', '回收元宝角色数'],
				chartShow:'line',
				data: [
					{
						'meta_id': '2945',
						'name': '回收元宝数量',
					},
					{
						'meta_id': '2946',
						'name': '回收元宝次数',
					},
					{
						'meta_id': '2947',
						'name': '回收元宝角色数',
					}
				]
			}
		]
	}
};
<commit_msg>fixed<commit_after>'use strict';

module.exports = {
	tabList: {
		title: '付费情况',
		subList: [
			{
				title: '充值收入',
				key: 'count',
				data: [
					{
						'meta_id': '2819',
						'name': '充值收入 ',
						'self_weidu': 'ALL'
					}
				]
			},
			{
				title: '付费角色',
				key: 'cash',
				data: [
					{
						'meta_id': '2818',
						'name': '付费角色 '
					},
					{
						'meta_id': '2914',
						'name': '首次付费角色数 '
					}
				]
			},
			{
				title: '付费率',
				key: 'cash_percent',
				role: 'true',
				data: [
					{
						'meta_id': '2874',
						'name': '角色付费率 ',
						'num_type': 'percent'
					},
					{
						'meta_id': '2816',
						'name': '登录角色'
					},
				]
			},
			{
				title: 'ARRPU',
				key: 'arrpu',
				role: 'true',
				data: [
					{
						'meta_id': '2816',
						'name': '角色ARPU',
						'num_type': 'fixed_2'
					},
					{
						'meta_id': '2875',
						'name': '角色ARPPU',
						'num_type': 'fixed_2'
					},
				]
			},
			{
				title: 'VIP等级分布',
				key: 'vip',
				dimensionName: 'VIP等级',
				data: [
					{
						'meta_id': '2916',
						'name': '收入'
					},
					{
						'meta_id': '2917',
						'name': 'VIP人数'
					},
				]
			},
			{
				title: '充值额度分布',
				key: 'cash_limit',
				dimensionName: '充值项目',
				data: [
					{
						'meta_id': '2918',
						'name': '现金'
					},
					{
						'meta_id': '2919',
						'name': '角色数'
					},
					{
						'meta_id': '2920',
						'name': '元宝'
					},
					{
						'meta_id': '2921',
						'name': '充值次数'
					},
				]
			},
			{
				title: '单位时间价格',
				key: 'price_date',
				data: [
					{
						'meta_id': '2922',
						'name': '单位时间（价格/小时）',
						'num_type': 'fixed_2'
					},
					{
						'meta_id': '2923',
						'name': '7日平均',
						'num_type': 'fixed_2'
					},
					{
						'meta_id': '2924',
						'name': '30日平均',
						'num_type': 'fixed_2'
					},
				]
			}
		]
	},
	playUser: {
		title: '玩家分析',
		subList: [
			// {
			// 	title: '各服务器玩家分布',
			// 	key: 'play_map',
			// 	dimensionName: '服务器',
			// 	stockItem: ['登录角色数', '新增角色数'],
			// 	data: [
			// 		{
			// 			'meta_id': '2925',
			// 			'name': '新增角色数'
			// 		},
			// 		{
			// 			'meta_id': '2926',
			// 			'name': '登录角色数'
			// 		}
			// 	]
			// },
			{
				title: '平均启动次数',
				key: 'start_num',
				lineItem: ['平均启动次数'],
				stockItem: ['平均启动次数'],
				chartShow: 'single',
				data: [
					{
						'meta_id': '2927',
						'name': '平均启动次数',
						'num_type': 'fixed_2'
					}
				]
			},
			{
				title: '等级分布',
				key: 'level',
				dimensionName: '等级',
				lineItem: ['等级分布'],
				stockItem: ['等级分布'],
				chartShow: 'single',
				data: [
					{
						'meta_id': '2928',
						'name': '等级分布',

					}
				]
			}
		]
	},
	channelAnalysis: {
		title: '渠道分析',
		role: true,
		subList: [
			{
				title: '登录',
				key: 'play_login',
				dimensionName: '渠道',
				showRange: ['登录', '新增'],
				stockItem: ['登录', '新增'],
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			},
			{
				title: '付费',
				key: 'play_cash',
				dimensionName: '渠道',
				stockItem: ['充值总金额'],
				lineItem: ['付费率', '渠道充值占比'],
				percent: true,
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			},
			{
				title: 'ARPU',
				key: 'play_arpu',
				dimensionName: '渠道',
				stockItem: ['ARPU', 'ARPPU'],
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			},
			{
				title: '留存',
				key: 'play_save',
				dimensionName: '渠道',
				stockItem: ['新增角色数'],
				lineItem: ['次日留存', '第3日留存', '第7日留存'],
				chartShow:'line',
				percent: true,
				data: [
					{
						'meta_id': '2929',
						'name': '登录角色'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2931',
						'name': '付费率',
						'num_type': 'percent'
					},
					{
						'meta_id': '2932',
						'name': '渠道充值占比',
						'num_type': 'percent'
					},
					{
						'meta_id': '2933',
						'name': '充值总金额'
					},
					{
						'meta_id': '2934',
						'name': 'ARPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2935',
						'name': 'ARPPU',
						'num_type': 'fix_2'
					},
					{
						'meta_id': '2930',
						'name': '新增角色数'
					},
					{
						'meta_id': '2936',
						'name': '次日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2937',
						'name': '第3日留存',
						'num_type': 'percent'
					},
					{
						'meta_id': '2938',
						'name': '第7日留存',
						'num_type': 'percent'
					}
				]
			}
		]
	},
	economy: {
		title: '经济情况',
		subList: [
			{
				title: '元宝回收产出比',
				key: 'gold_back',
				stockItem: ['元宝回收量', '元宝产出量'],
				lineItem: ['元宝回收产出比'],
				data: [
					{
						'meta_id': '2939',
						'name': '元宝回收量',
					},
					{
						'meta_id': '2940',
						'name': '元宝产出量',
					},
					{
						'meta_id': '2941',
						'num_type':'fixed_2',
						'name': '元宝回收产出比',
					}
				]
			},
			{
				title: '元宝产出途径',
				key: 'gold_product',
				dimensionName: '途径',
				stockItem: ['产出元宝数量'],
				lineItem: ['产出元宝角色数', '产出元宝次数'],
				chartShow:'line',
				data: [
					{
						'meta_id': '2942',
						'name': '产出元宝数量',
					},
					{
						'meta_id': '2943',
						'name': '产出元宝次数',
					},
					{
						'meta_id': '2944',
						'name': '产出元宝角色数',
					}
				]
			},
			{
				title: '元宝回收途径',
				key: 'gold_back_approach',
				dimensionName: '途径',
				stockItem: ['回收元宝数量'],
				lineItem: ['回收元宝次数', '回收元宝角色数'],
				chartShow:'line',
				data: [
					{
						'meta_id': '2945',
						'name': '回收元宝数量',
					},
					{
						'meta_id': '2946',
						'name': '回收元宝次数',
					},
					{
						'meta_id': '2947',
						'name': '回收元宝角色数',
					}
				]
			}
		]
	}
};
<|endoftext|>


<commit_before>window.d3AreaChart = (function (root) {

  var d3 = root.d3;

  var BUFFER_SIZE = 62;

  function generateBuffer(data) {
    var buffer = [];
    for (var i = 0; i < BUFFER_SIZE; i++, buffer.push(data));
    return buffer;
  }

  function appendClipPath(defs, width, height) {
    defs
      .attr("id", "clip")
      .append("rect")
        .attr("width", width)
        .attr("height", height);
  }

  /**
   * Path Object
   */
  var Path = function(groupe, options, area, xScale, timeAxis, xAxis, timeScale) {

    var color = options.color;

    var newData = 0;
    var buffer = generateBuffer(newData);

    var path = null;

    var transition = d3.select({})
      .transition()
        .duration(1000)
        .ease("linear");

    function update() {

      var now = new Date();
      timeScale.domain([now - 60000, now]);

      buffer.push(newData);

      // redraw the area
      path
        .attr("d", area)
        .attr("transform", null);

      // slide time scale left
      timeAxis.call(xAxis);

      // slide the are left
      path
        .transition()
          .attr("transform", "translate(" + xScale(0) + ", 0)");

      buffer.shift();
    }

    function tick() {
      transition = transition
        .each(update)
        .transition()
          .each("start", tick);
    }

    this.draw = function() {
      path = groupe.append("path")
        .attr("class", "chart-area " + color)
        .datum(buffer);
      return this;
    };

    this.setData = function(nd) {
      newData = nd;
      return this;
    };

    this.startTick = function() {
      tick();
      return this;
    };
  };

  /**
   * LineChart Object
   */
  var AreaChart = function(el) {

    var width = el.clientWidth - 20;
    var height = el.clientHeight - 20;

    var period = 60;      // 60sec
    var dimension = 100;  // 100%

    var xScale = d3.scale.linear()
      .range([0, width])
      .domain([1, period]);

    var yScale = d3.scale.linear()
      .range([0, height])
      .domain([0, dimension]);

    var now = new Date();
    var timeScale = d3.time.scale()
      .range([0, width])
      .domain([now - 60000, now]);

    var xAxis = d3.svg.axis()
      .scale(timeScale)
      .orient("bottom")

    var area = d3.svg.area()
      .x(function (d, i) { return xScale(i); })
      .y0(height)
      .y1(function (d) { return yScale(dimension - d); })
      .interpolate("basis");

    var svg = d3.select(el).append("svg")
      .attr("width", width)
      .attr("height", height);

    var groupe = svg.append("g");

    var defs = groupe.append("defs");
    defs.call(appendClipPath, width, height);

    var timeAxis = groupe.append("g")
      .attr("transform", "translate(0, " + (height - 20) + ")")
      .call(xAxis);

    var areaGroupe = groupe.append("g")
      .attr("clip-path", "url(#clip)")
      .attr("transform", "translate(0, " + -20 +  ")");

    this.appendChart = function (options) {
      return new Path(areaGroupe, options, area, xScale, timeAxis, xAxis, timeScale);
    };
  };

  return AreaChart;
})(window);<commit_msg>work in progress<commit_after>window.d3AreaChart = (function (root) {

  var d3 = root.d3;

  var BUFFER_SIZE = 62;

  function generateBuffer(data) {
    var buffer = [];
    for (var i = 0; i < BUFFER_SIZE; i++, buffer.push(data));
    return buffer;
  }

  function appendClipPath(defs, width, height) {
    defs
      .attr("id", "clip")
      .append("rect")
        .attr("width", width)
        .attr("height", height);
  }

  /**
   * Path Object
   */
  var Path = function(groupe, options, area, xScale, timeAxis, xAxis, timeScale) {

    var color = options.color;

    var newData = 0;
    var buffer = generateBuffer(newData);

    var path = null;

    var transition = d3.select({})
      .transition()
        .duration(1000)
        .ease("linear");

    function update() {
      var now = new Date();
      timeScale.domain([now - 60000, now]);

      buffer.push(newData);

      // redraw the area
      path
        .attr("d", area)
        .attr("transform", null);

      // slide time scale left
      timeAxis.call(xAxis).each(function() {
        var g = d3.select(this).selectAll("g.tick");
        g.each(function() {
          var n = d3.select(this);
          var t = d3.transform(n.attr("transform"));
          if (t.translate[0] < 0) {
            n.remove();
          }
        });
      });

      // slide the are left
      path
        .transition()
          .attr("transform", "translate(" + xScale(0) + ", 0)");

      buffer.shift();
    }

    function tick() {
      transition = transition
        .each(update)
        .transition()
          .each("start", tick);
    }

    this.draw = function() {
      path = groupe.append("path")
        .attr("class", "chart-area " + color)
        .datum(buffer);
      return this;
    };

    this.setData = function(nd) {
      newData = nd;
      return this;
    };

    this.startTick = function() {
      tick();
      return this;
    };
  };

  /**
   * LineChart Object
   */
  var AreaChart = function(el) {

    var width = el.clientWidth - 20;
    var height = el.clientHeight - 20;

    var period = 60;      // 60sec
    var dimension = 100;  // 100%

    var xScale = d3.scale.linear()
      .range([0, width])
      .domain([1, period]);

    var yScale = d3.scale.linear()
      .range([0, height])
      .domain([0, dimension]);

    var now = new Date();
    var timeScale = d3.time.scale()
      .range([0, width])
      .domain([now - 60000, now]);

    var xAxis = d3.svg.axis()
      .scale(timeScale)
      .orient("bottom")

    var area = d3.svg.area()
      .x(function (d, i) { return xScale(i); })
      .y0(height)
      .y1(function (d) { return yScale(dimension - d); })
      .interpolate("basis");

    var svg = d3.select(el).append("svg")
      .attr("width", width)
      .attr("height", height);

    var groupe = svg.append("g");

    var defs = groupe.append("defs");
    defs.call(appendClipPath, width, height);

    var timeAxis = groupe.append("g")
      .attr("transform", "translate(0, " + (height - 20) + ")")
      .call(xAxis);

    var areaGroupe = groupe.append("g")
      .attr("clip-path", "url(#clip)")
      .attr("transform", "translate(0, " + -20 +  ")");

    this.appendChart = function (options) {
      return new Path(areaGroupe, options, area, xScale, timeAxis, xAxis, timeScale);
    };
  };

  return AreaChart;
})(window);<|endoftext|>


<commit_before>/* global require console process describe it */

var should = require('should')
var viewer = require('../.')
var path    = require('path')
var rootdir = path.normalize(__dirname)

var _ = require('lodash')
var superagent = require('superagent')
var config_okay = require('config_okay')

var views = ['_design/imputedchecks/_view/missing_wim_neighbors'
            ,'_design/vdsml/_view/mainline'
            ,'_design/properties/_view/segment_length_ml']

var config={}

function create_tempdb(cb){
    var date = new Date()
    // var test_db_unique = [config.couchdb.db,
    //                       date.getHours(),
    //                       date.getMinutes(),
    //                       date.getSeconds(),
    //                       date.getMilliseconds()].join('-')
    // config.couchdb.db = test_db_unique
    var cdb =
        [config.couchdb.url+':'+config.couchdb.port
        ,config.couchdb.db].join('/')

    console.log('testing couchdb='+cdb)

    superagent.put(cdb)
    .type('json')
    .auth(config.couchdb.auth.username
         ,config.couchdb.auth.password)
    .end(function(err,result){
        if(result.error){
            // do not delete if we didn't create
            config.delete_db=false
        }else{
            config.delete_db=true
        }
        cb()
    })
    return null
}

/**
 * fixme, I should really create a test db, and populate it with data
 * and with a view
 *
 * Instead, because I want to get some real work done, this test is
 * going to just use an exsiting db that I have with existing views
 *
 * If your name is not James E. Marca, you should change this
 *
 */

describe('query view 1',function(){

before(function(done){
    var config_file = rootdir+'/../test.config.json'

    config_okay(config_file,function(err,c){
        if(!c.couchdb.db){ throw new Error('need valid db defined in test.config.json')}
        config = c
        create_tempdb(done)
        return null
    })
    return null
})

after(function(done){
    console.log('after')

    var cdb =
        config.couchdb.url+':'+config.couchdb.port
             + '/'+ config.couchdb.db
    if(config.delete_db){
        superagent.del(cdb)
        .type('json')
        .auth(config.couchdb.auth.username
             ,config.couchdb.auth.password)
        .end(function(e,r){
            return done()
        })
        return null
    }else{
        console.log("not deleting what I didn't create:" + cdb)
        return done()
    }
})
    it('should get all missing wim neighbors in district 3, reducing all'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'startkey':[2007, 3]
                  ,'endkey':[2007,3,{}]
                  ,'couchdb': config.couchdb.url
                  ,'port':config.couchdb.port
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.should.eql({"rows":[
                          {"key":null,"value":294}
                      ]})
                      return done()
                  })
       })
    it('should get all missing wim neighbors in district 3, no reduce'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'startkey':[2007, 3,5]
                  ,'endkey':[2007,3,5,{}]
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',42)
                      _.each(docs.rows,function(doc){
                          doc.key.should.eql([2007,3,5])
                      });
                      return done()
                  })
       })
    it('should get all missing wim neighbors in district 3, no reduce, using key'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'key':[2007, 3,5]
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',42)
                      _.each(docs.rows,function(doc){
                          doc.key.should.eql([2007,3,5])
                      });
                      return done()
                  })
       })
    it('should get all missing wim neighbors in district 3, no reduce, using keys'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'keys':[[2007, 3,5],[2008,3,5]]
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',82)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/200(7|8)/)
                          doc.key[1].should.eql(3)
                          doc.key[2].should.eql(5)
                      });
                      return done()
                  })
       })
    it('should get 10 missing wim neighbors in district 3, no reduce, using keys'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'keys':[[2007, 3,5],[2008,3,5]]
                  ,'limit':10
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',10)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/200(7|8)/)
                          doc.key[1].should.eql(3)
                          doc.key[2].should.eql(5)
                      });
                      return done()
                  })
       })
    it('should get 1 missing wim neighbors in district 3, no reduce, using keys, descending'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'startkey':[2007,3,{}] // year, district, freeway
                  ,'limit':1
                  ,'descending':true
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',1)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/2007/)
                          doc.key[1].should.eql(3)
                          doc.key[2].should.eql(275) // the last freeway in my testdb
                      });
                      return done()
                  })
       })
    it('should get 1 missing wim neighbors in district 3, no reduce, using keys, descending false'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'startkey':[2007,3,{}] // year, district, freeway
                  ,'limit':1
                  ,'descending':false
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',1)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/2007/)
                          doc.key[1].should.eql(4) // next one after 3 in my testdb
                          doc.key[2].should.eql(1) // the first freeway in d4 in my testdb
                      });
                      return done()
                  })
       })
    it('should get docs with include doc'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'key':[2007,3,5]
                  ,'reduce':false
                  ,'include_docs':true
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',42)
                      _.each(docs.rows,function(doc){
                          doc.key.should.eql([2007,3,5])
                          doc.should.have.property('doc')
                          var docdoc = doc.doc
                          docdoc.should.have.property(2007)
                          docdoc[2007].should.have.property('properties')
                          docdoc[2007]['properties'][0].should.have.property('geojson')
                      });
                      return done()
                  })
       })
})
<commit_msg>take out some console.log statements<commit_after>/* global require console process describe it */

var should = require('should')
var viewer = require('../.')
var path    = require('path')
var rootdir = path.normalize(__dirname)

var _ = require('lodash')
var superagent = require('superagent')
var config_okay = require('config_okay')

var views = ['_design/imputedchecks/_view/missing_wim_neighbors'
            ,'_design/vdsml/_view/mainline'
            ,'_design/properties/_view/segment_length_ml']

var config={}

function create_tempdb(cb){
    var date = new Date()
    // var test_db_unique = [config.couchdb.db,
    //                       date.getHours(),
    //                       date.getMinutes(),
    //                       date.getSeconds(),
    //                       date.getMilliseconds()].join('-')
    // config.couchdb.db = test_db_unique
    var cdb =
        [config.couchdb.url+':'+config.couchdb.port
        ,config.couchdb.db].join('/')

    superagent.put(cdb)
    .type('json')
    .auth(config.couchdb.auth.username
         ,config.couchdb.auth.password)
    .end(function(err,result){
        if(result.error){
            // do not delete if we didn't create
            config.delete_db=false
        }else{
            config.delete_db=true
        }
        cb()
    })
    return null
}

/**
 * fixme, I should really create a test db, and populate it with data
 * and with a view
 *
 * Instead, because I want to get some real work done, this test is
 * going to just use an exsiting db that I have with existing views
 *
 * If your name is not James E. Marca, you should change this
 *
 */

describe('query view 1',function(){

before(function(done){
    var config_file = rootdir+'/../test.config.json'

    config_okay(config_file,function(err,c){
        if(!c.couchdb.db){ throw new Error('need valid db defined in test.config.json')}
        config = c
        create_tempdb(done)
        return null
    })
    return null
})

after(function(done){

    var cdb =
        config.couchdb.url+':'+config.couchdb.port
             + '/'+ config.couchdb.db
    if(config.delete_db){
        superagent.del(cdb)
        .type('json')
        .auth(config.couchdb.auth.username
             ,config.couchdb.auth.password)
        .end(function(e,r){
            return done()
        })
        return null
    }else{
        console.log("not deleting what I didn't create:" + cdb)
        return done()
    }
})
    it('should get all missing wim neighbors in district 3, reducing all'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'startkey':[2007, 3]
                  ,'endkey':[2007,3,{}]
                  ,'couchdb': config.couchdb.url
                  ,'port':config.couchdb.port
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.should.eql({"rows":[
                          {"key":null,"value":294}
                      ]})
                      return done()
                  })
       })
    it('should get all missing wim neighbors in district 3, no reduce'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'startkey':[2007, 3,5]
                  ,'endkey':[2007,3,5,{}]
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',42)
                      _.each(docs.rows,function(doc){
                          doc.key.should.eql([2007,3,5])
                      });
                      return done()
                  })
       })
    it('should get all missing wim neighbors in district 3, no reduce, using key'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'key':[2007, 3,5]
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',42)
                      _.each(docs.rows,function(doc){
                          doc.key.should.eql([2007,3,5])
                      });
                      return done()
                  })
       })
    it('should get all missing wim neighbors in district 3, no reduce, using keys'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'keys':[[2007, 3,5],[2008,3,5]]
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',82)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/200(7|8)/)
                          doc.key[1].should.eql(3)
                          doc.key[2].should.eql(5)
                      });
                      return done()
                  })
       })
    it('should get 10 missing wim neighbors in district 3, no reduce, using keys'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'keys':[[2007, 3,5],[2008,3,5]]
                  ,'limit':10
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',10)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/200(7|8)/)
                          doc.key[1].should.eql(3)
                          doc.key[2].should.eql(5)
                      });
                      return done()
                  })
       })
    it('should get 1 missing wim neighbors in district 3, no reduce, using keys, descending'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'startkey':[2007,3,{}] // year, district, freeway
                  ,'limit':1
                  ,'descending':true
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',1)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/2007/)
                          doc.key[1].should.eql(3)
                          doc.key[2].should.eql(275) // the last freeway in my testdb
                      });
                      return done()
                  })
       })
    it('should get 1 missing wim neighbors in district 3, no reduce, using keys, descending false'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'doc':'doc1'
                  ,'view':views[0]
                  ,'startkey':[2007,3,{}] // year, district, freeway
                  ,'limit':1
                  ,'descending':false
                  ,'reduce':false
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',1)
                      _.each(docs.rows,function(doc){
                          doc.key[0].should.match(/2007/)
                          doc.key[1].should.eql(4) // next one after 3 in my testdb
                          doc.key[2].should.eql(1) // the first freeway in d4 in my testdb
                      });
                      return done()
                  })
       })
    it('should get docs with include doc'
      ,function(done){
           viewer({'db':config.couchdb.db
                  ,'view':views[0]
                  ,'key':[2007,3,5]
                  ,'reduce':false
                  ,'include_docs':true
                  }
                 ,function(err,docs){
                      should.not.exist(err)
                      docs.rows.should.have.property('length',42)
                      _.each(docs.rows,function(doc){
                          doc.key.should.eql([2007,3,5])
                          doc.should.have.property('doc')
                          var docdoc = doc.doc
                          docdoc.should.have.property(2007)
                          docdoc[2007].should.have.property('properties')
                          docdoc[2007]['properties'][0].should.have.property('geojson')
                      });
                      return done()
                  })
       })
})
<|endoftext|>


<commit_before>
(function() {

    var HeaderDropdown = cdb.admin.UserMenu.extend({

      events: {
        'click .order_column': 'orderColumns',
        'click .rename_column': 'renameColumn',
        'click .change_data_type': 'changeType',
        'click .georeference': 'georeference',
        'click .filter_by_this_column': 'filterColumn',
        'click .delete_column': 'deleteColumn'
      },

      setTable: function(table, column) {
        this.table = table;
        this.column = column;
      },

      orderColumns: function(e) { },

      renameColumn: function(e) {
        e.preventDefault();
        this.hide();
        this.trigger('renameColumn');
        return false;
      },

      changeType: function(e) { },

      georeference: function(e) { },

      filterColumn: function(e) { },

      deleteColumn: function(e) {
        e.preventDefault();
        cdb.log.debug("removing column: " + this.column);
        this.hide();
        this.table.deleteColumn(this.column);
        return false;
      }
    });

    /**
     * view used to render each row
     */
    cdb.admin.RowView = cdb.ui.common.RowView.extend({

      initialize: function() {
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
      },
      /**
       * return each cell view
       */
      valueView: function(colName, value) {
        return $('<div>').append(value);
      }
    });

    /**
     * header cell view, manages operations on table columns
     */

    var HeaderView = cdb.admin.HeaderView = cdb.core.View.extend({

      events: {
        'click    .coloptions':      'showColumnOptions',
        'keydown  .col_name_edit':   '_checkEditColnameInput'
      },

      initialize: function() {
        this.column = this.options.column;
        this.table = this.options.table;
        this.template = this.getTemplate('table/views/table_header_view');
        this.editing_name = false;

        HeaderView.colOptions= new HeaderDropdown({
          position: 'position',
          template_base: "table/views/table_header_options"
        });
        HeaderView.colOptions.render();
      },

      render: function() {
        this.$el.html('');
        this.$el.append(this.template({
          col_name: this.column[0],
          col_type: this.column[1],
          editing_name: this.editing_name
        }));
        return this;
      },

      _openColOptions: function(e) {
        var colOptions = HeaderView.colOptions;
        colOptions.off();
        this.$el.append(colOptions.el);

        // set data for column and table currently editing
        colOptions.setTable(this.table, this.column[0]);

        colOptions.bind('renameColumn', this._renameColumn, this);

        // bind the stuff
        colOptions.open(e, e.target);
      },

      _checkEditColnameInput: function(e) {
        if(e.keyCode === 13) {
          this.table.renameColumn(this.column[0], $('.col_name_edit').val());
        }
      },

      _finishEdit: function() {
        this.editing_name = false;
        this.render();
      },

      _renameColumn: function() {
        this.editing_name = true;
        this.render();
      },

      showColumnOptions: function(e) {
        var self = this;
        e.preventDefault();
        var colOptions = HeaderView.colOptions;
        colOptions.hide(function() {
          self._openColOptions(e);
        });
        return false;
      }

    });

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({

      rowView: cdb.admin.RowView,

      initialize: function() {
         var self = this;
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
         this.model.data().bind('newPage', this.newPage, this);
         setInterval(function() {
           var pos = $(this).scrollTop();
           var d = self.model.data();
           if( pos + $(window).height() > $(document).height() + 3) {
             d.setPage(d.getPage() + 1);
           } else if (pos < 0) {
             d.setPage(d.getPage() - 1);
           }
         }, 300);
      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
           cdb.log.debug("removing rows");
         }
      },

      headerView: function(column) {
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({ column: column, table: this.model});
          this.addView(v);
          return v.render().el;
        }
        return '';
      }
    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',

      initialize: function() {
        this.tableView = new cdb.admin.TableView({
          dataModel: this.model.data(),
          model: this.model
        });

      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

})();
<commit_msg>fixed table pagination with mouse<commit_after>
(function() {

    var HeaderDropdown = cdb.admin.UserMenu.extend({

      events: {
        'click .order_column': 'orderColumns',
        'click .rename_column': 'renameColumn',
        'click .change_data_type': 'changeType',
        'click .georeference': 'georeference',
        'click .filter_by_this_column': 'filterColumn',
        'click .delete_column': 'deleteColumn'
      },

      setTable: function(table, column) {
        this.table = table;
        this.column = column;
      },

      orderColumns: function(e) { },

      renameColumn: function(e) {
        e.preventDefault();
        this.hide();
        this.trigger('renameColumn');
        return false;
      },

      changeType: function(e) { },

      georeference: function(e) { },

      filterColumn: function(e) { },

      deleteColumn: function(e) {
        e.preventDefault();
        cdb.log.debug("removing column: " + this.column);
        this.hide();
        this.table.deleteColumn(this.column);
        return false;
      }
    });

    /**
     * view used to render each row
     */
    cdb.admin.RowView = cdb.ui.common.RowView.extend({

      initialize: function() {
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
      },
      /**
       * return each cell view
       */
      valueView: function(colName, value) {
        return $('<div>').append(value);
      }
    });

    /**
     * header cell view, manages operations on table columns
     */

    var HeaderView = cdb.admin.HeaderView = cdb.core.View.extend({

      events: {
        'click    .coloptions':      'showColumnOptions',
        'keydown  .col_name_edit':   '_checkEditColnameInput'
      },

      initialize: function() {
        this.column = this.options.column;
        this.table = this.options.table;
        this.template = this.getTemplate('table/views/table_header_view');
        this.editing_name = false;

        HeaderView.colOptions= new HeaderDropdown({
          position: 'position',
          template_base: "table/views/table_header_options"
        });
        HeaderView.colOptions.render();
      },

      render: function() {
        this.$el.html('');
        this.$el.append(this.template({
          col_name: this.column[0],
          col_type: this.column[1],
          editing_name: this.editing_name
        }));
        return this;
      },

      _openColOptions: function(e) {
        var colOptions = HeaderView.colOptions;
        colOptions.off();
        this.$el.append(colOptions.el);

        // set data for column and table currently editing
        colOptions.setTable(this.table, this.column[0]);

        colOptions.bind('renameColumn', this._renameColumn, this);

        // bind the stuff
        colOptions.open(e, e.target);
      },

      _checkEditColnameInput: function(e) {
        if(e.keyCode === 13) {
          this.table.renameColumn(this.column[0], $('.col_name_edit').val());
        }
      },

      _finishEdit: function() {
        this.editing_name = false;
        this.render();
      },

      _renameColumn: function() {
        this.editing_name = true;
        this.render();
      },

      showColumnOptions: function(e) {
        var self = this;
        e.preventDefault();
        var colOptions = HeaderView.colOptions;
        colOptions.hide(function() {
          self._openColOptions(e);
        });
        return false;
      }

    });

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({

      rowView: cdb.admin.RowView,

      initialize: function() {
         var self = this;
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
         this.model.data().bind('newPage', this.newPage, this);
         setInterval(function() {
           var pos = $(this).scrollTop();
           var d = self.model.data();
           if( pos + $(window).height() >= $(document).height() ) {
             d.setPage(d.getPage() + 1);
           } else if (pos <= 0) {
             d.setPage(d.getPage() - 1);
           }
         }, 300);
      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
           cdb.log.debug("removing rows");
         }
      },

      headerView: function(column) {
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({ column: column, table: this.model});
          this.addView(v);
          return v.render().el;
        }
        return '';
      }
    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',

      initialize: function() {
        this.tableView = new cdb.admin.TableView({
          dataModel: this.model.data(),
          model: this.model
        });

      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

})();
<|endoftext|>


<commit_before>
(function() {

    var HeaderDropdown = cdb.admin.UserMenu.extend({

      events: {
        'click .asc': 'orderColumnsAsc',
        'click .desc': 'orderColumnsDesc',
        'click .rename_column': 'renameColumn',
        'click .change_data_type': 'changeType',
        'click .georeference': 'georeference',
        'click .filter_by_this_column': 'filterColumn',
        'click .delete_column': 'deleteColumn'
      },

      setTable: function(table, column) {
        this.table = table;
        this.column = column;

        //set options for ordering
        if(table.data().options.get('mode') === 'asc') {
          this.$('.asc').addClass('selected');
          this.$('.desc').removeClass('selected');
        } else {
          this.$('.desc').addClass('selected');
          this.$('.asc').removeClass('selected');
        }

      },

      orderColumnsAsc: function(e) {
        e.preventDefault();
        this.table.data().setOptions({
          mode: 'asc',
          order_by: this.column
        });
        this.hide();
        return false;
      },

      orderColumnsDesc: function(e) {
        e.preventDefault();
        this.table.data().setOptions({
          mode: 'des',
          order_by: this.column
        });
        this.hide();
        return false;
      },

      renameColumn: function(e) {
        e.preventDefault();
        this.hide();
        this.trigger('renameColumn');
        return false;
      },

      changeType: function(e) {
        e.preventDefault();
        this.hide();
        this.trigger('changeType');
        return false;
      },

      georeference: function(e) { },

      filterColumn: function(e) { },

      deleteColumn: function(e) {
        e.preventDefault();
        cdb.log.debug("removing column: " + this.column);
        this.hide();
        this.table.deleteColumn(this.column);
        return false;
      }
    });

    /**
     * view used to render each row
     */
    cdb.admin.RowView = cdb.ui.common.RowView.extend({

      initialize: function() {
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
      },
      /**
       * return each cell view
       */
      valueView: function(colName, value) {
        return $('
').append(value);
      }
    });

    /**
     * header cell view, manages operations on table columns
     */

    var HeaderView = cdb.admin.HeaderView = cdb.core.View.extend({

      events: {
        'click    .coloptions':      'showColumnOptions',
        'keydown  .col_name_edit':   '_checkEditColnameInput'
      },

      initialize: function() {
        this.column = this.options.column;
        this.table = this.options.table;
        this.template = this.getTemplate('table/views/table_header_view');
        this.editing_name = false;
        this.changing_type = false;

        HeaderView.colOptions= new HeaderDropdown({
          position: 'position',
          template_base: "table/views/table_header_options"
        });
        HeaderView.colOptions.render();
      },

      render: function() {
        this.$el.html('');

        this.$el.append(this.template({
          col_name: this.column[0],
          col_type: this.column[1],
          editing_name: this.editing_name,
          changing_type: this.changing_type
        }));
        return this;
      },

      _openColOptions: function(e) {
        var colOptions = HeaderView.colOptions;
        colOptions.off();
        this.$el.append(colOptions.el);

        // set data for column and table currently editing
        colOptions.setTable(this.table, this.column[0]);

        colOptions.bind('renameColumn', this._renameColumn, this);
        colOptions.bind('changeType', this._changeType, this);

        // bind the stuff
        colOptions.open(e, e.target);
      },

      _checkEditColnameInput: function(e) {
        if(e.keyCode === 13) {
          this.table.renameColumn(this.column[0], $('.col_name_edit').val());
          this.editing_name = false;
          this.render();
        }
      },

      _finishEdit: function() {
        this.editing_name = false;
        this.render();
      },

      _renameColumn: function() {
        this.editing_name = true;
        this.changing_type = false;
        this.render();
      },

      _changeType: function() {
        this.editing_name = false;
        this.changing_type = true;
        this.render();
      },

      showColumnOptions: function(e) {
        var self = this;
        e.preventDefault();
        var colOptions = HeaderView.colOptions;
        colOptions.hide(function() {
          self._openColOptions(e);
        });
        return false;
      }

    });

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({

      rowView: cdb.admin.RowView,

      initialize: function() {
         var self = this;
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
         this.model.data().bind('newPage', this.newPage, this);
         var topReached = false;
         setInterval(function() {
           if(!self.$el.is(":visible") || self.model.data().isFetchingPage()) {
             return;
           }
           var pos = $(this).scrollTop();
           var d = self.model.data();
           // do not let to fetch previous pages
           // until the user dont scroll back a little bit
           if(pos > 2) {
             topReached = false;
           }
           var pageSize = $(window).height() - self.$el.offset().top;
           var tableHeight = this.$('tbody').height();
           var realPos = pos + pageSize;
           if(realPos > tableHeight) {
              console.log(realPos, tableHeight, tableHeight - realPos);
              d.setPage(d.getPage() + 1);
           } else if (pos <= 0) {
             if(!topReached) {
               d.setPage(d.getPage() - 1);
             }
             topReached = true;
           }

         }, 2000);

        // Moving header when scrolls
        $(window).scroll(function(ev){
          self.$el.find("thead th div div").css({top: $(window).scrollTop() + "px"});
        });

        this.model.data().bind('loadingRows', function(updown) {
          var fn = updown === 'up'? 'prepend': 'append';
          self.$('tbody')[fn]("
LOADING
");
        });

        this.model.data().bind('endLoadingRows', function() {
          self.$('.dataloader').remove();
        });

        this.bind('cellClick', this._editCell, this);

      },

      _editCell: function(e, cell, x, y) {
        var self = this;
        var column = self.model.columnName(x-1);
        var columnType = this.model.getColumnType(column);

        if(this.model.isReservedColumn(column)) {
          return;
        }

        var editors = {
          'string': cdb.admin.EditTextDialog,
          'number': cdb.admin.EditTextDialog,
          'geometry': cbd.admin.EditTextDialog
        };

        var Editor = editors[columnType];

        if(!Editor) {
          cdb.log.error("editor not defined for column type " + columnType);
          return;
        }
        var row = self.model.data().getRowAt(y);

        var dlg = new Editor({
          initial_value: self.model.data().getCell(y, column),
          row: row,
          res: function(val) {
            var update = {};
            update[column] = val;
            row.set(update).save();
          }
        });

        // auto add to body
        var offset = $(e.target).offset();
        offset.top -= $(window).scrollTop();
        offset.left -= $(window).scrollLeft();
        dlg.showAt(offset.left, offset.top);

      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           cdb.log.debug("removing rows: " + d.size() + " " + idx);
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
         }
      },

      headerView: function(column) {
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({ column: column, table: this.model});
          this.addView(v);
          return v.render().el;
        } else {
          return '

';
        }
      }
    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',

      initialize: function() {
        this.tableView = new cdb.admin.TableView({
          dataModel: this.model.data(),
          model: this.model
        });
      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

})();
<commit_msg>fixed pagination when there no results<commit_after>
(function() {

    var HeaderDropdown = cdb.admin.UserMenu.extend({

      events: {
        'click .asc': 'orderColumnsAsc',
        'click .desc': 'orderColumnsDesc',
        'click .rename_column': 'renameColumn',
        'click .change_data_type': 'changeType',
        'click .georeference': 'georeference',
        'click .filter_by_this_column': 'filterColumn',
        'click .delete_column': 'deleteColumn'
      },

      setTable: function(table, column) {
        this.table = table;
        this.column = column;

        //set options for ordering
        if(table.data().options.get('mode') === 'asc') {
          this.$('.asc').addClass('selected');
          this.$('.desc').removeClass('selected');
        } else {
          this.$('.desc').addClass('selected');
          this.$('.asc').removeClass('selected');
        }

      },

      orderColumnsAsc: function(e) {
        e.preventDefault();
        this.table.data().setOptions({
          mode: 'asc',
          order_by: this.column
        });
        this.hide();
        return false;
      },

      orderColumnsDesc: function(e) {
        e.preventDefault();
        this.table.data().setOptions({
          mode: 'des',
          order_by: this.column
        });
        this.hide();
        return false;
      },

      renameColumn: function(e) {
        e.preventDefault();
        this.hide();
        this.trigger('renameColumn');
        return false;
      },

      changeType: function(e) {
        e.preventDefault();
        this.hide();
        this.trigger('changeType');
        return false;
      },

      georeference: function(e) { },

      filterColumn: function(e) { },

      deleteColumn: function(e) {
        e.preventDefault();
        cdb.log.debug("removing column: " + this.column);
        this.hide();
        this.table.deleteColumn(this.column);
        return false;
      }
    });

    /**
     * view used to render each row
     */
    cdb.admin.RowView = cdb.ui.common.RowView.extend({

      initialize: function() {
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
      },
      /**
       * return each cell view
       */
      valueView: function(colName, value) {
        return $('
').append(value);
      }
    });

    /**
     * header cell view, manages operations on table columns
     */

    var HeaderView = cdb.admin.HeaderView = cdb.core.View.extend({

      events: {
        'click    .coloptions':      'showColumnOptions',
        'keydown  .col_name_edit':   '_checkEditColnameInput'
      },

      initialize: function() {
        this.column = this.options.column;
        this.table = this.options.table;
        this.template = this.getTemplate('table/views/table_header_view');
        this.editing_name = false;
        this.changing_type = false;

        HeaderView.colOptions= new HeaderDropdown({
          position: 'position',
          template_base: "table/views/table_header_options"
        });
        HeaderView.colOptions.render();
      },

      render: function() {
        this.$el.html('');

        this.$el.append(this.template({
          col_name: this.column[0],
          col_type: this.column[1],
          editing_name: this.editing_name,
          changing_type: this.changing_type
        }));
        return this;
      },

      _openColOptions: function(e) {
        var colOptions = HeaderView.colOptions;
        colOptions.off();
        this.$el.append(colOptions.el);

        // set data for column and table currently editing
        colOptions.setTable(this.table, this.column[0]);

        colOptions.bind('renameColumn', this._renameColumn, this);
        colOptions.bind('changeType', this._changeType, this);

        // bind the stuff
        colOptions.open(e, e.target);
      },

      _checkEditColnameInput: function(e) {
        if(e.keyCode === 13) {
          this.table.renameColumn(this.column[0], $('.col_name_edit').val());
          this.editing_name = false;
          this.render();
        }
      },

      _finishEdit: function() {
        this.editing_name = false;
        this.render();
      },

      _renameColumn: function() {
        this.editing_name = true;
        this.changing_type = false;
        this.render();
      },

      _changeType: function() {
        this.editing_name = false;
        this.changing_type = true;
        this.render();
      },

      showColumnOptions: function(e) {
        var self = this;
        e.preventDefault();
        var colOptions = HeaderView.colOptions;
        colOptions.hide(function() {
          self._openColOptions(e);
        });
        return false;
      }

    });

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({

      rowView: cdb.admin.RowView,

      initialize: function() {
         var self = this;
         this.constructor.__super__.initialize.apply(this);
         this.options.row_header = true;
         this.model.data().bind('newPage', this.newPage, this);
         var topReached = false;
         setInterval(function() {
           if(!self.$el.is(":visible") || self.model.data().isFetchingPage()) {
             return;
           }
           var pos = $(this).scrollTop();
           var d = self.model.data();
           // do not let to fetch previous pages
           // until the user dont scroll back a little bit
           if(pos > 2) {
             topReached = false;
           }
           var pageSize = $(window).height() - self.$el.offset().top;
           var tableHeight = this.$('tbody').height();
           var realPos = pos + pageSize;
           if(tableHeight < pageSize) return;
           if(realPos > tableHeight) {
              console.log(realPos, tableHeight, tableHeight - realPos);
              d.setPage(d.getPage() + 1);
           } else if (pos <= 0) {
             if(!topReached) {
               d.setPage(d.getPage() - 1);
             }
             topReached = true;
           }

         }, 2000);

        // Moving header when scrolls
        $(window).scroll(function(ev){
          self.$el.find("thead th div div").css({top: $(window).scrollTop() + "px"});
        });

        this.model.data().bind('loadingRows', function(updown) {
          var fn = updown === 'up'? 'prepend': 'append';
          self.$('tbody')[fn]("
LOADING
");
        });

        this.model.data().bind('endLoadingRows', function() {
          self.$('.dataloader').remove();
        });

        this.bind('cellClick', this._editCell, this);

      },

      _editCell: function(e, cell, x, y) {
        var self = this;
        var column = self.model.columnName(x-1);
        var columnType = this.model.getColumnType(column);

        if(this.model.isReservedColumn(column)) {
          return;
        }

        var editors = {
          'string': cdb.admin.EditTextDialog,
          'number': cdb.admin.EditTextDialog,
          'geometry': cdb.admin.EditTextDialog
        };

        var Editor = editors[columnType];

        if(!Editor) {
          cdb.log.error("editor not defined for column type " + columnType);
          return;
        }
        var row = self.model.data().getRowAt(y);

        var dlg = new Editor({
          initial_value: self.model.data().getCell(y, column),
          row: row,
          res: function(val) {
            var update = {};
            update[column] = val;
            row.set(update).save();
          }
        });

        // auto add to body
        var offset = $(e.target).offset();
        offset.top -= $(window).scrollTop();
        offset.left -= $(window).scrollLeft();
        dlg.showAt(offset.left, offset.top);

      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           cdb.log.debug("removing rows: " + d.size() + " " + idx);
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
         }
      },

      headerView: function(column) {
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({ column: column, table: this.model});
          this.addView(v);
          return v.render().el;
        } else {
          return '

';
        }
      }
    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',

      initialize: function() {
        this.tableView = new cdb.admin.TableView({
          dataModel: this.model.data(),
          model: this.model
        });
      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

})();
<|endoftext|>