<commit_before>const test = require('tape')
const sinon = require('sinon')
const proxyquire = require('proxyquire')

function stubPaneImporterExporter () {
  return proxyquire('../../components/pane-importer-exporter', {
    '../lib/utils': {
      paneIsOnScreenEdge: (pane) => pane.onScreenEdge,
      paneOccupiesSameSpaceAtDirection: (pane) => pane.occupiesSameSpaceAtDirection
    }
  })
}

test('generalMode.canImport(win): returns true if gaps found in grid', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const grid = {findGaps: () => ['I am a gap']}
    const { canImport } = paneImporterExporter.generalMode({grid})
    t.ok(canImport('I am a window'), 'can import pane when there is room in grid')
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.canImport(win): returns false if gaps not found in grid', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const grid = {findGaps: () => []}
    const { canImport } = paneImporterExporter.generalMode({grid})
    t.notOk(canImport('I am a window'), 'cannot import pane when there is no room in grid')
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.importPane(win, direction): imports pane into a gap equal to the window if available', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const winBounds = {x: 500, y: 500, width: 100, height: 100}
    const win = {getBounds: () => winBounds}
    const grid = {
      findGaps: () => [
        {x: 0, y: 0, width: 100, height: 100},
        {x: 100, y: 0, width: 10, height: 10}
      ]
    }
    const sGrid = {
      createWindow: sinon.spy()
    }
    const id = 1
    const { importPane } = paneImporterExporter.generalMode({grid, sGrid, id})
    importPane(win)
    t.ok(
      sGrid.createWindow.calledWith(1, win, {x: 0, y: 0, width: 100, height: 100}),
      'window created in the size and location of appropriate gap'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.importPane(win, direction): imports pane by changing its size into smaller gap if none large enough are available', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const winBounds = {x: 500, y: 500, width: 100, height: 100}
    const win = {getBounds: () => winBounds}
    const grid = {
      findGaps: () => [
        {x: 0, y: 0, width: 50, height: 50},
        {x: 50, y: 0, width: 10, height: 10}
      ]
    }
    const sGrid = {
      createWindow: sinon.spy()
    }
    const id = 1
    const { importPane } = paneImporterExporter.generalMode({grid, sGrid, id})
    importPane(win)
    t.ok(
      sGrid.createWindow.calledWith(1, win, {x: 0, y: 0, width: 50, height: 50}),
      'window created in the size and location of appropriate gap'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.export(win): expels pane from grid', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const grid = {expel: sinon.spy()}
    const { exportPane } = paneImporterExporter.generalMode({grid})
    exportPane('I am a paneId')
    t.ok(grid.expel.calledWith('I am a paneId'), 'expel method of grid called')
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.canImport(win): returns true', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const { canImport } = paneImporterExporter.tmuxMode()
    t.ok(canImport('I am a window'), 'can always import pane')
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane horizontally when importing left', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 20}
      ]
    }
    const sGrid = {splitCurrentWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'left')
    t.ok(
      sGrid.splitCurrentWindow.calledWith(1, win, 2, 'horizontal', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane horizontally when importing right', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 20}
      ]
    }
    const sGrid = {splitCurrentWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'right')
    t.ok(
      sGrid.splitCurrentWindow.calledWith(1, win, 2, 'horizontal', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane vertically when importing up', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 20}
      ]
    }
    const sGrid = {splitCurrentWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'up')
    t.ok(
      sGrid.splitCurrentWindow.calledWith(1, win, 2, 'vertical', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane vertically when importing down', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 20}
      ]
    }
    const sGrid = {splitCurrentWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'down')
    t.ok(
      sGrid.splitCurrentWindow.calledWith(1, win, 2, 'vertical', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.exportPane(paneId): returns pane, maxes all windows and fills gaps', t => {
  t.plan(5)
  try {
    const win = 'I am a window'
    const grid = {
      maxAllPanes: sinon.spy(),
      expel: (paneId) => paneId === 1 ? win : 'I am not win!',
      findGaps: () => [{gap: 'gap1'}, {gap: 'gap2'}]
    }
    const sGrid = {createWindow: sinon.spy()}
    const id = 1
    const winOpts = {winOpts: 'I am some winOpts!'}
    const TerminalWindow = 'I am a TerminalWindow'
    const paneImporterExporter = stubPaneImporterExporter()
    const { exportPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts, TerminalWindow})
    const exportedWin = exportPane(1)
    const firstCall = sGrid.createWindow.getCall(0)
    const secondCall = sGrid.createWindow.getCall(1)
    t.equals(exportedWin, win, 'win returned from export method')
    t.ok(grid.maxAllPanes.calledOnce, 'max all panes called when window exported')
    t.ok(grid.maxAllPanes.calledBefore(sGrid.createWindow), 'max all panes called before filling gaps')
    t.ok(
      firstCall.calledWith(1, TerminalWindow, {gap: 'gap1', winOpts: 'I am some winOpts!'}),
      'first gap filled'
    )
    t.ok(
      secondCall.calledWith(1, TerminalWindow, {gap: 'gap2', winOpts: 'I am some winOpts!'}),
      'second gap filled'
    )
  } catch (e) {
    t.fail(e)
  }
})
<commit_msg>test: split specific window on import<commit_after>const test = require('tape')
const sinon = require('sinon')
const proxyquire = require('proxyquire')

function stubPaneImporterExporter () {
  return proxyquire('../../components/pane-importer-exporter', {
    '../lib/utils': {
      paneIsOnScreenEdge: (pane) => pane.onScreenEdge,
      paneOccupiesSameSpaceAtDirection: (pane) => pane.occupiesSameSpaceAtDirection
    }
  })
}

test('generalMode.canImport(win): returns true if gaps found in grid', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const grid = {findGaps: () => ['I am a gap']}
    const { canImport } = paneImporterExporter.generalMode({grid})
    t.ok(canImport('I am a window'), 'can import pane when there is room in grid')
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.canImport(win): returns false if gaps not found in grid', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const grid = {findGaps: () => []}
    const { canImport } = paneImporterExporter.generalMode({grid})
    t.notOk(canImport('I am a window'), 'cannot import pane when there is no room in grid')
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.importPane(win, direction): imports pane into a gap equal to the window if available', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const winBounds = {x: 500, y: 500, width: 100, height: 100}
    const win = {getBounds: () => winBounds}
    const grid = {
      findGaps: () => [
        {x: 0, y: 0, width: 100, height: 100},
        {x: 100, y: 0, width: 10, height: 10}
      ]
    }
    const sGrid = {
      createWindow: sinon.spy()
    }
    const id = 1
    const { importPane } = paneImporterExporter.generalMode({grid, sGrid, id})
    importPane(win)
    t.ok(
      sGrid.createWindow.calledWith(1, win, {x: 0, y: 0, width: 100, height: 100}),
      'window created in the size and location of appropriate gap'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.importPane(win, direction): imports pane by changing its size into smaller gap if none large enough are available', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const winBounds = {x: 500, y: 500, width: 100, height: 100}
    const win = {getBounds: () => winBounds}
    const grid = {
      findGaps: () => [
        {x: 0, y: 0, width: 50, height: 50},
        {x: 50, y: 0, width: 10, height: 10}
      ]
    }
    const sGrid = {
      createWindow: sinon.spy()
    }
    const id = 1
    const { importPane } = paneImporterExporter.generalMode({grid, sGrid, id})
    importPane(win)
    t.ok(
      sGrid.createWindow.calledWith(1, win, {x: 0, y: 0, width: 50, height: 50}),
      'window created in the size and location of appropriate gap'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('generalMode.export(win): expels pane from grid', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const grid = {expel: sinon.spy()}
    const { exportPane } = paneImporterExporter.generalMode({grid})
    exportPane('I am a paneId')
    t.ok(grid.expel.calledWith('I am a paneId'), 'expel method of grid called')
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.canImport(win): returns true', t => {
  t.plan(1)
  try {
    const paneImporterExporter = stubPaneImporterExporter()
    const { canImport } = paneImporterExporter.tmuxMode()
    t.ok(canImport('I am a window'), 'can always import pane')
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane horizontally when importing left', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 20}
      ]
    }
    const sGrid = {splitWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'left')
    t.ok(
      sGrid.splitWindow.calledWith(1, win, 2, 'horizontal', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane horizontally when importing right', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, height: 20}
      ]
    }
    const sGrid = {splitWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'right')
    t.ok(
      sGrid.splitWindow.calledWith(1, win, 2, 'horizontal', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane vertically when importing up', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 20}
      ]
    }
    const sGrid = {splitWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'up')
    t.ok(
      sGrid.splitWindow.calledWith(1, win, 2, 'vertical', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.importPane(win, direction): splits largest pane vertically when importing down', t => {
  t.plan(1)
  try {
    const grid = {
      panes: [
        {id: 1, onScreenEdge: false},
        {id: 2, onScreenEdge: true, occupiesSameSpaceAtDirection: false},
        {id: 3, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 10},
        {id: 4, onScreenEdge: true, occupiesSameSpaceAtDirection: true, width: 20}
      ]
    }
    const sGrid = {splitWindow: sinon.spy()}
    const id = 1
    const winOpts = 2
    const paneImporterExporter = stubPaneImporterExporter()
    const win = {id: 'I am a window', getBounds: () => {}}
    const { importPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts})
    importPane(win, 'down')
    t.ok(
      sGrid.splitWindow.calledWith(1, win, 2, 'vertical', 4),
      'current window split with imported window inserted'
    )
  } catch (e) {
    t.fail(e)
  }
})

test('tmuxMode.exportPane(paneId): returns pane, maxes all windows and fills gaps', t => {
  t.plan(5)
  try {
    const win = 'I am a window'
    const grid = {
      maxAllPanes: sinon.spy(),
      expel: (paneId) => paneId === 1 ? win : 'I am not win!',
      findGaps: () => [{gap: 'gap1'}, {gap: 'gap2'}]
    }
    const sGrid = {createWindow: sinon.spy()}
    const id = 1
    const winOpts = {winOpts: 'I am some winOpts!'}
    const TerminalWindow = 'I am a TerminalWindow'
    const paneImporterExporter = stubPaneImporterExporter()
    const { exportPane } = paneImporterExporter.tmuxMode({grid, sGrid, id, winOpts, TerminalWindow})
    const exportedWin = exportPane(1)
    const firstCall = sGrid.createWindow.getCall(0)
    const secondCall = sGrid.createWindow.getCall(1)
    t.equals(exportedWin, win, 'win returned from export method')
    t.ok(grid.maxAllPanes.calledOnce, 'max all panes called when window exported')
    t.ok(grid.maxAllPanes.calledBefore(sGrid.createWindow), 'max all panes called before filling gaps')
    t.ok(
      firstCall.calledWith(1, TerminalWindow, {gap: 'gap1', winOpts: 'I am some winOpts!'}),
      'first gap filled'
    )
    t.ok(
      secondCall.calledWith(1, TerminalWindow, {gap: 'gap2', winOpts: 'I am some winOpts!'}),
      'second gap filled'
    )
  } catch (e) {
    t.fail(e)
  }
})
<|endoftext|>


<commit_before>var dom = require('src/dom');
var MVVM = require('mvvm');

describe("v-bind >", function() {
	var element;

	beforeEach(function() {
		element = document.createElement('div');
		document.body.appendChild(element);
	});

	afterEach(function() {
		document.body.removeChild(element);
	});

	function equalClass(classString, stringClass) {
		var fronts = classString.split(' ');
		var backs = stringClass.split(' ');

		if (fronts.length !== backs.length) {
			return false;
		}

		for (var i = 0; i < fronts.length; i++) {
			if (backs.indexOf(fronts[i]) === -1) {
				return false;
			}
		}

		return true;
	}

	it('test equalClass', function() {
		expect(equalClass('a', 'a b')).toBeFalsy();
		expect(equalClass('a c', 'a b')).toBeFalsy();

		expect(equalClass('a', 'a')).toBeTruthy();
		expect(equalClass('a b', 'b a')).toBeTruthy();
		expect(equalClass('a b c', 'c b a')).toBeTruthy();
		expect(equalClass('a b c def ghi', 'def b ghi a c')).toBeTruthy();
	});

	it('class single', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'cls': ''
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.hasAttr(div, 'class')).toBe(false);

		data.cls = 'classA';
		expect(div.className).toBe('classA');
		expect(dom.hasClass(div, 'classA')).toBe(true);

		// remove class
		data.cls = '';
		expect(div.className).toBe('');
		expect(dom.hasAttr(div, 'class')).toBe(false);

		// add new class and will remove old class
		data.cls = 'classB';
		expect(div.className).toBe('classB');
		expect(dom.hasClass(div, 'classA')).toBe(false);
	});

	it('class single with static', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'cls': 'xxdk'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'static1 static2 xxdk')).toBe(true);

		data.cls = 'txgc';
		expect(equalClass(div.className, 'static1 static2 txgc')).toBe(true);

		data.cls = '';
		expect(equalClass(div.className, 'static1 static2')).toBe(true);
	});

	it('class array', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'cls1': 'aaa',
			'cls2': 'bbb'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'static1 aaa bbb static2')).toBe(true);

		data.cls1 = 'AAA';
		data.cls2 = 'BBB';
		expect(equalClass(div.className, 'static1 AAA BBB static2')).toBe(true);

		data.cls1 = '';
		data.cls2 = '';
		expect(equalClass(div.className, 'static1 static2')).toBe(true);
	});

	it('class object', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'obj': {
				'aaa': true,
				'bbb': false,
				'ccc': true
			}
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'aaa ccc')).toBe(true);

		data.obj.bbb = true;
		data.obj.ccc = false;
		expect(equalClass(div.className, 'aaa bbb')).toBe(true);

		// cover obj
		data.obj = {
			'ddd': true,
			'eee': true,
			'fff': false
		}
		expect(equalClass(div.className, 'ddd eee')).toBe(true);

		data.obj.fff = true;
		data.obj.ddd = false;
		data.obj.eee = false;
		expect(equalClass(div.className, 'fff')).toBe(true);
	});

	it('class json', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'isA': true,
			'isB': false,
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'classA')).toBe(true);

		data.isB = true;
		expect(equalClass(div.className, 'classA classB')).toBe(true);

		data.isA = false;
		data.isB = false;
		expect(div.className).toBe('');
		expect(dom.hasAttr(div, 'class')).toBe(false);
	});

	it('style object', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'obj': {
				'color': 'red',
				'margin-top': '10px'
			}
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(div.style.color).toBe('red');
		expect(div.style.marginTop).toBe('10px');

		data.obj.color = 'black';
		expect(div.style.color).toBe('black');

		data.obj['margin-top'] = '15px';
		expect(div.style.marginTop).toBe('15px');

		// cover obj
		data.obj = {
			'border': '1px solid red',
			'font-size': '15px'
		}
		// should remove old style
		expect(div.style.color).toBe('');
		expect(div.style.marginTop).toBe('');
		// should add new style
		expect(div.style.border).toBe('1px solid red');
		expect(div.style.fontSize).toBe('15px');

		data.obj['font-size'] = '24px';
		expect(div.style.fontSize).toBe('24px');
	});

	it('style json', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'color': 'red',
			'size': '12px'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(div.style.color).toBe('red');
		expect(div.style.fontSize).toBe('12px');

		data.color = 'green';
		expect(div.style.color).toBe('green');

		data.size = '111px';
		expect(div.style.fontSize).toBe('111px');
	});

	it('attribute normal', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'vid': 'xxdk'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');

		data.vid = 'txgc';
		expect(dom.getAttr(div, 'id')).toBe('txgc');
	});

	it('attribute json', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'vid': 'xxdk',
			'dtype': 'aaa'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');
		expect(dom.getAttr(div, 'data-type')).toBe('aaa');

		data.vid = 'txgc';
		expect(dom.getAttr(div, 'id')).toBe('txgc');

		data.dtype = 'bbb';
		expect(dom.getAttr(div, 'data-type')).toBe('bbb');
	});

	it('attribute and classObject and styleObject', function() {
		element.innerHTML =
			'

';

		var vm = new MVVM(element, {
			'vid': 'xxdk',
			'clsObj': {
				'classA': true,
				'classB': true
			},
			'styObj': {
				'color': 'red',
				'font-size': '13px'
			}
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');
		expect(div.className).toBe('classA classB');
		expect(div.style.color).toBe('red');
		expect(div.style.fontSize).toBe('13px');

		data.vid = 'txgc';
		expect(dom.getAttr(div, 'id')).toBe('txgc');

		data.styObj.color = 'green';
		expect(div.style.color).toBe('green');

		data.clsObj.classB = false;
		expect(div.className).toBe('classA');

		// cover
		data.styObj = {'margin-top': '15px'};
		expect(div.style.color).toBe('');
		expect(div.style.fontSize).toBe('');
		expect(div.style.marginTop).toBe('15px');

		data.styObj['margin-top'] = '50px';
		expect(div.style.marginTop).toBe('50px');

		data.clsObj = {'aaa': true, 'bbb': false};
		expect(div.className).toBe('aaa');

		data.clsObj.bbb = true;
		expect(div.className).toBe('aaa bbb');
	});

	it('attribute and classArray', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'vid': 'xxdk',
			'cls1': 'aaa',
			'cls2': 'bbb'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');
		expect(div.className).toBe('aaa bbb');

		data.cls1 = 'xxdk';
		expect(equalClass(div.className, 'xxdk bbb')).toBe(true);

		data.cls2 = '';
		expect(div.className).toBe('xxdk');
	});
});<commit_msg>Update test case for v-bind.<commit_after>var dom = require('src/dom');
var MVVM = require('mvvm');

describe("v-bind >", function() {
	var element;

	beforeEach(function() {
		element = document.createElement('div');
		document.body.appendChild(element);
	});

	afterEach(function() {
		document.body.removeChild(element);
	});

	function equalClass(classString, stringClass) {
		var fronts = classString.split(' ');
		var backs = stringClass.split(' ');

		if (fronts.length !== backs.length) {
			return false;
		}

		for (var i = 0; i < fronts.length; i++) {
			if (backs.indexOf(fronts[i]) === -1) {
				return false;
			}
		}

		return true;
	}

	it('test equalClass', function() {
		expect(equalClass('a', 'a b')).toBeFalsy();
		expect(equalClass('a c', 'a b')).toBeFalsy();

		expect(equalClass('a', 'a')).toBeTruthy();
		expect(equalClass('a b', 'b a')).toBeTruthy();
		expect(equalClass('a b c', 'c b a')).toBeTruthy();
		expect(equalClass('a b c def ghi', 'def b ghi a c')).toBeTruthy();
	});

	it('class single', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'cls': ''
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.hasAttr(div, 'class')).toBe(false);

		data.cls = 'classA';
		expect(div.className).toBe('classA');
		expect(dom.hasClass(div, 'classA')).toBe(true);

		// remove class
		data.cls = '';
		expect(div.className).toBe('');
		expect(dom.hasAttr(div, 'class')).toBe(false);

		// add new class and will remove old class
		data.cls = 'classB';
		expect(div.className).toBe('classB');
		expect(dom.hasClass(div, 'classA')).toBe(false);
	});

	it('class single with static', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'cls': 'xxdk'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'static1 static2 xxdk')).toBe(true);

		data.cls = 'txgc';
		expect(equalClass(div.className, 'static1 static2 txgc')).toBe(true);

		data.cls = '';
		expect(equalClass(div.className, 'static1 static2')).toBe(true);
	});

	it('class array', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'cls1': 'aaa',
			'cls2': 'bbb'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'static1 aaa bbb static2')).toBe(true);

		data.cls1 = 'AAA';
		data.cls2 = 'BBB';
		expect(equalClass(div.className, 'static1 AAA BBB static2')).toBe(true);

		data.cls1 = '';
		data.cls2 = '';
		expect(equalClass(div.className, 'static1 static2')).toBe(true);
	});

	it('class object', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'obj': {
				'aaa': true,
				'bbb': false,
				'ccc': true
			}
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'aaa ccc')).toBe(true);

		data.obj.bbb = true;
		data.obj.ccc = false;
		expect(equalClass(div.className, 'aaa bbb')).toBe(true);

		// cover obj
		data.obj = {
			'ddd': true,
			'eee': true,
			'fff': false
		}
		expect(equalClass(div.className, 'ddd eee')).toBe(true);

		data.obj.fff = true;
		data.obj.ddd = false;
		data.obj.eee = false;
		expect(equalClass(div.className, 'fff')).toBe(true);
	});

	it('class json', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'isA': true,
			'isB': false,
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(equalClass(div.className, 'classA')).toBe(true);

		data.isB = true;
		expect(equalClass(div.className, 'classA classB')).toBe(true);

		data.isA = false;
		data.isB = false;
		expect(div.className).toBe('');
		expect(dom.hasAttr(div, 'class')).toBe(false);
	});

	it('style object', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'obj': {
				'color': 'red',
				'margin-top': '10px'
			}
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(div.style.color).toBe('red');
		expect(div.style.marginTop).toBe('10px');

		data.obj.color = 'black';
		expect(div.style.color).toBe('black');

		data.obj['margin-top'] = '15px';
		expect(div.style.marginTop).toBe('15px');

		// cover obj
		data.obj = {
			'border': '1px solid red',
			'font-size': '15px'
		}
		// should remove old style
		expect(div.style.color).toBe('');
		expect(div.style.marginTop).toBe('');
		// should add new style
		expect(div.style.border).toBe('1px solid red');
		expect(div.style.fontSize).toBe('15px');

		data.obj['font-size'] = '24px';
		expect(div.style.fontSize).toBe('24px');
	});

	it('style json', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'color': 'red',
			'size': '12px'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(div.style.color).toBe('red');
		expect(div.style.fontSize).toBe('12px');

		data.color = 'green';
		expect(div.style.color).toBe('green');

		data.size = '111px';
		expect(div.style.fontSize).toBe('111px');
	});

	it('attribute normal', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'vid': 'xxdk'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');

		data.vid = 'txgc';
		expect(dom.getAttr(div, 'id')).toBe('txgc');
	});

	it('attribute json', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'vid': 'xxdk',
			'dtype': 'aaa'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');
		expect(dom.getAttr(div, 'data-type')).toBe('aaa');

		data.vid = 'txgc';
		expect(dom.getAttr(div, 'id')).toBe('txgc');

		data.dtype = 'bbb';
		expect(dom.getAttr(div, 'data-type')).toBe('bbb');
	});

	it('attribute and classObject and styleObject', function() {
		element.innerHTML =
			'

';

		var vm = new MVVM(element, {
			'vid': 'xxdk',
			'clsObj': {
				'classA': true,
				'classB': true
			},
			'styObj': {
				'color': 'red',
				'font-size': '13px'
			}
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');
		expect(div.className).toBe('classA classB');
		expect(div.style.color).toBe('red');
		expect(div.style.fontSize).toBe('13px');

		data.vid = 'txgc';
		expect(dom.getAttr(div, 'id')).toBe('txgc');

		data.styObj.color = 'green';
		expect(div.style.color).toBe('green');

		data.clsObj.classB = false;
		expect(div.className).toBe('classA');

		// cover
		data.styObj = {'margin-top': '15px'};
		expect(div.style.color).toBe('');
		expect(div.style.fontSize).toBe('');
		expect(div.style.marginTop).toBe('15px');

		data.styObj['margin-top'] = '50px';
		expect(div.style.marginTop).toBe('50px');

		data.clsObj = {'aaa': true, 'bbb': false};
		expect(div.className).toBe('aaa');

		data.clsObj.bbb = true;
		expect(div.className).toBe('aaa bbb');
	});

	it('attribute and classArray', function() {
		element.innerHTML = '

';

		var vm = new MVVM(element, {
			'vid': 'xxdk',
			'cls1': 'aaa',
			'cls2': 'bbb'
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.getAttr(div, 'id')).toBe('xxdk');
		expect(div.className).toBe('aaa bbb');

		data.cls1 = 'xxdk';
		expect(equalClass(div.className, 'xxdk bbb')).toBe(true);

		data.cls2 = '';
		expect(div.className).toBe('xxdk');
	});

	it('attribute has no value', function() {
		element.innerHTML = '<input v-bind:disabled="dis">';

		var vm = new MVVM(element, {
			'dis': true
		});
		var data = vm.get();
		var div = element.childNodes[0];

		expect(dom.hasAttr(div, 'disabled')).toBe(true);

		// change data
		data.dis = false;
		expect(dom.hasAttr(div, 'disabled')).toBe(false);
	});
});<|endoftext|>


<commit_before>/**
* jQuery Youtube API Feed Plugin
* @author John Hoover <john@defvayne23.com>
*
*/
(function($) {
	$.extend({
		jTube: function(options){
			var options = $.extend({
				author: '',
				search: '',
				order: 'published',
				limit: 5,
				page: 1,
				success: function(videos){}
			}, options);
			var youtubeUrl = 'http://gdata.youtube.com/feeds/';
			var videoElem = this;
			var imageUrl = ''

			if(options.author != '')
				youtubeUrl += 'users/'+options.author+'/uploads?';
			else if(options.search != '')
				youtubeUrl += 'api/videos?q='+options.search+'&';

			youtubeUrl += 'alt=json';
			youtubeUrl += '&max-results='+options.limit;
			youtubeUrl += '&start-index='+(((options.page * options.limit) - options.limit) + 1);
			youtubeUrl += '&orderby='+options.order;

			$.ajax({
				url: youtubeUrl,
				dataType: 'json',
				success: function(data) {
					var videos = [];

					$(data.feed.entry).each(function(){
						videoId = $(this.id.$t.split('/'));
						videoId = videoId[videoId.length - 1];

						//Create a clean category array
						var categories = [];
						$(this.category).each(function(index){
							if(index != 0) {
								categories[index - 1] = this.term
							}
						});

						var video = {
							title: this.title.$t,
							description: this.media$group.media$description.$t,
							published: this.published.$t,
							link: this.link[0].href,
							categories: categories,
							author: {
								name: this.author[0].name.$t,
								link: this.author[0].uri.$t
							},
							videos: this.media$group.media$content,
							thumbnails: this.media$group.media$thumbnail
						};

						if(this.yt$statistics)
							video.views = this.yt$statistics.viewCount;

						videos[videos.length] = video;
					});

					options.success(videos);
				}
			});

			return this;
		}
	});
})(jQuery);<commit_msg>Add time option<commit_after>/**
* jQuery Youtube API Feed Plugin
* @author John Hoover <john@defvayne23.com>
*
*/
(function($) {
	$.extend({
		jTube: function(options){
			var options = $.extend({
				author: '',
				search: '',
				order: 'published',
				time: 'all_time',
				limit: 5,
				page: 1,
				success: function(videos){}
			}, options);
			var youtubeUrl = 'http://gdata.youtube.com/feeds/';
			var videoElem = this;
			var imageUrl = ''

			if(options.author != '')
				youtubeUrl += 'users/'+options.author+'/uploads?';
			else if(options.search != '')
				youtubeUrl += 'api/videos?q='+options.search+'&';

			youtubeUrl += 'alt=json';
			youtubeUrl += '&max-results='+options.limit;
			youtubeUrl += '&start-index='+(((options.page * options.limit) - options.limit) + 1);
			youtubeUrl += '&orderby='+options.order;
			youtubeUrl += '&time='+options.time;

			$.ajax({
				url: youtubeUrl,
				dataType: 'json',
				success: function(data) {
					var videos = [];

					$(data.feed.entry).each(function(){
						videoId = $(this.id.$t.split('/'));
						videoId = videoId[videoId.length - 1];

						//Create a clean category array
						var categories = [];
						$(this.category).each(function(index){
							if(index != 0) {
								categories[index - 1] = this.term
							}
						});

						var video = {
							title: this.title.$t,
							description: this.media$group.media$description.$t,
							published: this.published.$t,
							link: this.link[0].href,
							categories: categories,
							author: {
								name: this.author[0].name.$t,
								link: this.author[0].uri.$t
							},
							videos: this.media$group.media$content,
							thumbnails: this.media$group.media$thumbnail
						};

						if(this.yt$statistics)
							video.views = this.yt$statistics.viewCount;

						videos[videos.length] = video;
					});

					options.success(videos);
				}
			});

			return this;
		}
	});
})(jQuery);<|endoftext|>


<commit_before>// Peity jQuery plugin version 3.2.1
// (c) 2016 Ben Pickles
//
// http://benpickles.github.io/peity
//
// Released under MIT license.
(function($, document, Math, undefined) {
  var peity = $.fn.peity = function(type, options) {
    if (svgSupported) {
      this.each(function() {
        var $this = $(this)
        var chart = $this.data('_peity')

        if (chart) {
          if (type) chart.type = type
          $.extend(chart.opts, options)
        } else {
          chart = new Peity(
            $this,
            type,
            $.extend({},
              peity.defaults[type],
              $this.data('peity'),
              options)
          )

          $this
            .change(function() { chart.draw() })
            .data('_peity', chart)
        }

        chart.draw()
      });
    }

    return this;
  };

  var Peity = function($el, type, opts) {
    this.$el = $el
    this.type = type
    this.opts = opts
  }

  var PeityPrototype = Peity.prototype

  var svgElement = PeityPrototype.svgElement = function(tag, attrs) {
    return $(
      document.createElementNS('http://www.w3.org/2000/svg', tag)
    ).attr(attrs)
  }

  // https://gist.github.com/madrobby/3201472
  var svgSupported = 'createElementNS' in document && svgElement('svg', {})[0].createSVGRect

  PeityPrototype.draw = function() {
    var opts = this.opts
    peity.graphers[this.type].call(this, opts)
    if (opts.after) opts.after.call(this, opts)
  }

  PeityPrototype.fill = function() {
    var fill = this.opts.fill

    return $.isFunction(fill)
      ? fill
      : function(_, i) { return fill[i % fill.length] }
  }

  PeityPrototype.prepare = function(width, height) {
    if (!this.$svg) {
      this.$el.hide().after(
        this.$svg = svgElement('svg', {
          "class": "peity"
        })
      )
    }

    return this.$svg
      .empty()
      .data('peity', this)
      .attr({
        height: height,
        width: width
      })
  }

  PeityPrototype.values = function() {
    return $.map(this.$el.text().split(this.opts.delimiter), function(value) {
      return parseFloat(value)
    })
  }

  peity.defaults = {}
  peity.graphers = {}

  peity.register = function(type, defaults, grapher) {
    this.defaults[type] = defaults
    this.graphers[type] = grapher
  }

  peity.register(
    'pie',
    {
      fill: ['#ff9900', '#fff4dd', '#ffc66e'],
      radius: 8
    },
    function(opts) {
      if (!opts.delimiter) {
        var delimiter = this.$el.text().match(/[^0-9\.]/)
        opts.delimiter = delimiter ? delimiter[0] : ","
      }

      var values = $.map(this.values(), function(n) {
        return n > 0 ? n : 0
      })

      if (opts.delimiter == "/") {
        var v1 = values[0]
        var v2 = values[1]
        values = [v1, Math.max(0, v2 - v1)]
      }

      var i = 0
      var length = values.length
      var sum = 0

      for (; i < length; i++) {
        sum += values[i]
      }

      if (!sum) {
        length = 2
        sum = 1
        values = [0, 1]
      }

      var diameter = opts.radius * 2

      var $svg = this.prepare(
        opts.width || diameter,
        opts.height || diameter
      )

      var width = $svg.width()
        , height = $svg.height()
        , cx = width / 2
        , cy = height / 2

      var radius = Math.min(cx, cy)
        , innerRadius = opts.innerRadius

      if (this.type == 'donut' && !innerRadius) {
        innerRadius = radius * 0.5
      }

      var pi = Math.PI
      var fill = this.fill()

      var scale = this.scale = function(value, radius) {
        var radians = value / sum * pi * 2 - pi / 2

        return [
          radius * Math.cos(radians) + cx,
          radius * Math.sin(radians) + cy
        ]
      }

      var cumulative = 0

      for (i = 0; i < length; i++) {
        var value = values[i]
          , portion = value / sum
          , $node

        if (portion == 0) continue

        if (portion == 1) {
          if (innerRadius) {
            var x2 = cx - 0.01
              , y1 = cy - radius
              , y2 = cy - innerRadius

            $node = svgElement('path', {
              d: [
                'M', cx, y1,
                'A', radius, radius, 0, 1, 1, x2, y1,
                'L', x2, y2,
                'A', innerRadius, innerRadius, 0, 1, 0, cx, y2
              ].join(' ')
            })
          } else {
            $node = svgElement('circle', {
              cx: cx,
              cy: cy,
              r: radius
            })
          }
        } else {
          var cumulativePlusValue = cumulative + value

          var d = ['M'].concat(
            scale(cumulative, radius),
            'A', radius, radius, 0, portion > 0.5 ? 1 : 0, 1,
            scale(cumulativePlusValue, radius),
            'L'
          )

          if (innerRadius) {
            d = d.concat(
              scale(cumulativePlusValue, innerRadius),
              'A', innerRadius, innerRadius, 0, portion > 0.5 ? 1 : 0, 0,
              scale(cumulative, innerRadius)
            )
          } else {
            d.push(cx, cy)
          }

          cumulative += value

          $node = svgElement('path', {
            d: d.join(" ")
          })
        }

        $node.attr('fill', fill.call(this, value, i, values))

        $svg.append($node)
      }
    }
  )

  peity.register(
    'donut',
    $.extend(true, {}, peity.defaults.pie),
    function(opts) {
      peity.graphers.pie.call(this, opts)
    }
  )

  peity.register(
    "line",
    {
      delimiter: ",",
      fill: "#c6d9fd",
      height: 16,
      min: 0,
      stroke: "#4d89f9",
      strokeWidth: 1,
      width: 32
    },
    function(opts) {
      var values = this.values()
      if (values.length == 1) values.push(values[0])
      var max = Math.max.apply(Math, opts.max == undefined ? values : values.concat(opts.max))
        , min = Math.min.apply(Math, opts.min == undefined ? values : values.concat(opts.min))

      var $svg = this.prepare(opts.width, opts.height)
        , strokeWidth = opts.strokeWidth
        , width = $svg.width()
        , height = $svg.height() - strokeWidth
        , diff = max - min

      var xScale = this.x = function(input) {
        return input * (width / (values.length - 1))
      }

      var yScale = this.y = function(input) {
        var y = height

        if (diff) {
          y -= ((input - min) / diff) * height
        }

        return y + strokeWidth / 2
      }

      var zero = yScale(Math.max(min, 0))
        , coords = [0, zero]

      for (var i = 0; i < values.length; i++) {
        coords.push(
          xScale(i),
          yScale(values[i])
        )
      }

      coords.push(width, zero)

      if (opts.fill) {
        $svg.append(
          svgElement('polygon', {
            fill: opts.fill,
            points: coords.join(' ')
          })
        )
      }

      if (strokeWidth) {
        $svg.append(
          svgElement('polyline', {
            fill: 'none',
            points: coords.slice(2, coords.length - 2).join(' '),
            stroke: opts.stroke,
            'stroke-width': strokeWidth,
            'stroke-linecap': 'square'
          })
        )
      }
    }
  );

  peity.register(
    'bar',
    {
      delimiter: ",",
      fill: ["#4D89F9"],
      height: 16,
      min: 0,
      padding: 0.1,
      width: 32
    },
    function(opts) {
      var values = this.values()
        , max = Math.max.apply(Math, opts.max == undefined ? values : values.concat(opts.max))
        , min = Math.min.apply(Math, opts.min == undefined ? values : values.concat(opts.min))

      var $svg = this.prepare(opts.width, opts.height)
        , width = $svg.width()
        , height = $svg.height()
        , diff = max - min
        , padding = opts.padding
        , fill = this.fill()

      var xScale = this.x = function(input) {
        return input * width / values.length
      }

      var yScale = this.y = function(input) {
        return height - (
          diff
            ? ((input - min) / diff) * height
            : 1
        )
      }

      for (var i = 0; i < values.length; i++) {
        var x = xScale(i + padding)
          , w = xScale(i + 1 - padding) - x
          , value = values[i]
          , valueY = yScale(value)
          , y1 = valueY
          , y2 = valueY
          , h

        if (!diff) {
          h = 1
        } else if (value < 0) {
          y1 = yScale(Math.min(max, 0))
        } else {
          y2 = yScale(Math.max(min, 0))
        }

        h = y2 - y1

        if (h == 0) {
          h = 1
          if (max > 0 && diff) y1--
        }

        $svg.append(
          svgElement('rect', {
            fill: fill.call(this, value, i, values),
            x: x,
            y: y1,
            width: w,
            height: h
          })
        )
      }
    }
  );
})(jQuery, document, Math);
<commit_msg>Fix reference to the Peity chart object from its <svg>.<commit_after>// Peity jQuery plugin version 3.2.1
// (c) 2016 Ben Pickles
//
// http://benpickles.github.io/peity
//
// Released under MIT license.
(function($, document, Math, undefined) {
  var peity = $.fn.peity = function(type, options) {
    if (svgSupported) {
      this.each(function() {
        var $this = $(this)
        var chart = $this.data('_peity')

        if (chart) {
          if (type) chart.type = type
          $.extend(chart.opts, options)
        } else {
          chart = new Peity(
            $this,
            type,
            $.extend({},
              peity.defaults[type],
              $this.data('peity'),
              options)
          )

          $this
            .change(function() { chart.draw() })
            .data('_peity', chart)
        }

        chart.draw()
      });
    }

    return this;
  };

  var Peity = function($el, type, opts) {
    this.$el = $el
    this.type = type
    this.opts = opts
  }

  var PeityPrototype = Peity.prototype

  var svgElement = PeityPrototype.svgElement = function(tag, attrs) {
    return $(
      document.createElementNS('http://www.w3.org/2000/svg', tag)
    ).attr(attrs)
  }

  // https://gist.github.com/madrobby/3201472
  var svgSupported = 'createElementNS' in document && svgElement('svg', {})[0].createSVGRect

  PeityPrototype.draw = function() {
    var opts = this.opts
    peity.graphers[this.type].call(this, opts)
    if (opts.after) opts.after.call(this, opts)
  }

  PeityPrototype.fill = function() {
    var fill = this.opts.fill

    return $.isFunction(fill)
      ? fill
      : function(_, i) { return fill[i % fill.length] }
  }

  PeityPrototype.prepare = function(width, height) {
    if (!this.$svg) {
      this.$el.hide().after(
        this.$svg = svgElement('svg', {
          "class": "peity"
        })
      )
    }

    return this.$svg
      .empty()
      .data('_peity', this)
      .attr({
        height: height,
        width: width
      })
  }

  PeityPrototype.values = function() {
    return $.map(this.$el.text().split(this.opts.delimiter), function(value) {
      return parseFloat(value)
    })
  }

  peity.defaults = {}
  peity.graphers = {}

  peity.register = function(type, defaults, grapher) {
    this.defaults[type] = defaults
    this.graphers[type] = grapher
  }

  peity.register(
    'pie',
    {
      fill: ['#ff9900', '#fff4dd', '#ffc66e'],
      radius: 8
    },
    function(opts) {
      if (!opts.delimiter) {
        var delimiter = this.$el.text().match(/[^0-9\.]/)
        opts.delimiter = delimiter ? delimiter[0] : ","
      }

      var values = $.map(this.values(), function(n) {
        return n > 0 ? n : 0
      })

      if (opts.delimiter == "/") {
        var v1 = values[0]
        var v2 = values[1]
        values = [v1, Math.max(0, v2 - v1)]
      }

      var i = 0
      var length = values.length
      var sum = 0

      for (; i < length; i++) {
        sum += values[i]
      }

      if (!sum) {
        length = 2
        sum = 1
        values = [0, 1]
      }

      var diameter = opts.radius * 2

      var $svg = this.prepare(
        opts.width || diameter,
        opts.height || diameter
      )

      var width = $svg.width()
        , height = $svg.height()
        , cx = width / 2
        , cy = height / 2

      var radius = Math.min(cx, cy)
        , innerRadius = opts.innerRadius

      if (this.type == 'donut' && !innerRadius) {
        innerRadius = radius * 0.5
      }

      var pi = Math.PI
      var fill = this.fill()

      var scale = this.scale = function(value, radius) {
        var radians = value / sum * pi * 2 - pi / 2

        return [
          radius * Math.cos(radians) + cx,
          radius * Math.sin(radians) + cy
        ]
      }

      var cumulative = 0

      for (i = 0; i < length; i++) {
        var value = values[i]
          , portion = value / sum
          , $node

        if (portion == 0) continue

        if (portion == 1) {
          if (innerRadius) {
            var x2 = cx - 0.01
              , y1 = cy - radius
              , y2 = cy - innerRadius

            $node = svgElement('path', {
              d: [
                'M', cx, y1,
                'A', radius, radius, 0, 1, 1, x2, y1,
                'L', x2, y2,
                'A', innerRadius, innerRadius, 0, 1, 0, cx, y2
              ].join(' ')
            })
          } else {
            $node = svgElement('circle', {
              cx: cx,
              cy: cy,
              r: radius
            })
          }
        } else {
          var cumulativePlusValue = cumulative + value

          var d = ['M'].concat(
            scale(cumulative, radius),
            'A', radius, radius, 0, portion > 0.5 ? 1 : 0, 1,
            scale(cumulativePlusValue, radius),
            'L'
          )

          if (innerRadius) {
            d = d.concat(
              scale(cumulativePlusValue, innerRadius),
              'A', innerRadius, innerRadius, 0, portion > 0.5 ? 1 : 0, 0,
              scale(cumulative, innerRadius)
            )
          } else {
            d.push(cx, cy)
          }

          cumulative += value

          $node = svgElement('path', {
            d: d.join(" ")
          })
        }

        $node.attr('fill', fill.call(this, value, i, values))

        $svg.append($node)
      }
    }
  )

  peity.register(
    'donut',
    $.extend(true, {}, peity.defaults.pie),
    function(opts) {
      peity.graphers.pie.call(this, opts)
    }
  )

  peity.register(
    "line",
    {
      delimiter: ",",
      fill: "#c6d9fd",
      height: 16,
      min: 0,
      stroke: "#4d89f9",
      strokeWidth: 1,
      width: 32
    },
    function(opts) {
      var values = this.values()
      if (values.length == 1) values.push(values[0])
      var max = Math.max.apply(Math, opts.max == undefined ? values : values.concat(opts.max))
        , min = Math.min.apply(Math, opts.min == undefined ? values : values.concat(opts.min))

      var $svg = this.prepare(opts.width, opts.height)
        , strokeWidth = opts.strokeWidth
        , width = $svg.width()
        , height = $svg.height() - strokeWidth
        , diff = max - min

      var xScale = this.x = function(input) {
        return input * (width / (values.length - 1))
      }

      var yScale = this.y = function(input) {
        var y = height

        if (diff) {
          y -= ((input - min) / diff) * height
        }

        return y + strokeWidth / 2
      }

      var zero = yScale(Math.max(min, 0))
        , coords = [0, zero]

      for (var i = 0; i < values.length; i++) {
        coords.push(
          xScale(i),
          yScale(values[i])
        )
      }

      coords.push(width, zero)

      if (opts.fill) {
        $svg.append(
          svgElement('polygon', {
            fill: opts.fill,
            points: coords.join(' ')
          })
        )
      }

      if (strokeWidth) {
        $svg.append(
          svgElement('polyline', {
            fill: 'none',
            points: coords.slice(2, coords.length - 2).join(' '),
            stroke: opts.stroke,
            'stroke-width': strokeWidth,
            'stroke-linecap': 'square'
          })
        )
      }
    }
  );

  peity.register(
    'bar',
    {
      delimiter: ",",
      fill: ["#4D89F9"],
      height: 16,
      min: 0,
      padding: 0.1,
      width: 32
    },
    function(opts) {
      var values = this.values()
        , max = Math.max.apply(Math, opts.max == undefined ? values : values.concat(opts.max))
        , min = Math.min.apply(Math, opts.min == undefined ? values : values.concat(opts.min))

      var $svg = this.prepare(opts.width, opts.height)
        , width = $svg.width()
        , height = $svg.height()
        , diff = max - min
        , padding = opts.padding
        , fill = this.fill()

      var xScale = this.x = function(input) {
        return input * width / values.length
      }

      var yScale = this.y = function(input) {
        return height - (
          diff
            ? ((input - min) / diff) * height
            : 1
        )
      }

      for (var i = 0; i < values.length; i++) {
        var x = xScale(i + padding)
          , w = xScale(i + 1 - padding) - x
          , value = values[i]
          , valueY = yScale(value)
          , y1 = valueY
          , y2 = valueY
          , h

        if (!diff) {
          h = 1
        } else if (value < 0) {
          y1 = yScale(Math.min(max, 0))
        } else {
          y2 = yScale(Math.max(min, 0))
        }

        h = y2 - y1

        if (h == 0) {
          h = 1
          if (max > 0 && diff) y1--
        }

        $svg.append(
          svgElement('rect', {
            fill: fill.call(this, value, i, values),
            x: x,
            y: y1,
            width: w,
            height: h
          })
        )
      }
    }
  );
})(jQuery, document, Math);
<|endoftext|>


<commit_before>/// map.js --- Mapping collection handling
//
// Copyright (c) 2011 Quildreen Motta
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/// Module khaos.collection.map

//// -- Aliases --------------------------------------------------------
var _keys        = Object.keys
var clone        = Object.create
var proto        = Object.getPrototypeOf
var has_own_prop = {}.hasOwnProperty

//// -- Traversable ----------------------------------------------------

///// Function each
// Applies the iterator function to each key/value pair in the map.
//
// each :: {k -> e}, (e, k, {k -> e} -> Ignored) -> Undefined
function each(map, iterator) {
  var i, len
  var props = keys(map)
  for (i = 0, len = props.length; i < len; ++i)
    iterator(map[props[i]], props[i], map) }

//// -- Manipulation ---------------------------------------------------

///// Function put
// Assigns a value for the given key.
//
// put! :: {k -> e}*, k, e -> {k -> e}
function put(map, key, value) {
  Object(map)[key] = value
  return map }

///// Function remove
// Removes the given key and its associated value from the map.
//
// remove! :: {k -> e}*, k -> {k -> e}
function remove(map, key) {
  delete Object(map)[key]
  return map }

///// Function replace
// Replaces all the occurrences of `value' by the given `replacement'.
//
// This only replaces values that are directly set in the map
// itself.
//
// replace! :: {k -> e}*, e, e -> {k -> e}
function replace(map, value, replacement) {
  var key
  map = Object(map)
  for (key in map)
    if (has_key_p(map, key) && map[key] === value)
      map[key] = replacement

  return map }

///// Function clear
// Removes all key/value pairs from the map.
//
// clear! :: {k -> e}* -> {}
function clear(map) {
  each(map, function(value, key){
              delete map[key] })
  return map }

//// -- Inspection -----------------------------------------------------

///// Function at
// Retrieves the value for the given key.
//
// at :: {k -> e}, k, default:e -> e
// at :: {k -> e}, k -> Maybe e
function at(map, key, _default) {
  map = Object(map)

  return key in map?      map[key]
  :      /* otherwise */  _default }

///// Function size
// Returns the number of key/value pairs in the map.
//
// size :: {k -> e} -> Number
function size(map) {
  return keys(map).length }

///// Function empty_p
// Does the map have any key/value pairs?
//
// empty_p :: {k -> e} -> Bool
function empty_p(map) {
  return !size(map) }

///// Function has_key_p
// Does the map have the given key?
//
// has_key_p :: {k -> e}, k -> Bool
function has_key_p(map, key) {
  return map != null?     has_own_prop.call(map, key)
  :      /* otherwise */  false }

//// -- Decomposition --------------------------------------------------

///// Function keys
// Extracts the keys from the given map.
//
// keys :: {k -> e} -> [k]
function keys(map) {
  return _keys(Object(map)) }

///// Function values
// Extracts the values from the given map.
//
// values :: {k -> e} -> [e]
function values(map) {
  return reduce(map, [], function(acc, item, key) {
                           acc.push(item)
                           return acc })}

///// Function items
// Extracts the key/value pairs from the given map.
//
// items :: {k -> e} -> [(k, e)]
function items(map) {
  return reduce(map, [], function(acc, item, key) {
                           acc.push([key, item])
                           return acc })}

//// -- Folding --------------------------------------------------------

///// Function reduce
// Applies a function to each key/value pair in the map, returning the
// accumulated value.
//
// reduce :: {k -> e}, Any?, (Any, e, k, {k -> e} -> Any) -> Any
function reduce(map, value, folder) {
  var props, i, len
  map   = Object(map)
  props = keys(map)
  if (arguments.length == 2) {
    folder = value
    value  = map[props.shift()] }

  for (i = 0, len = props.length; i < len; ++i)
    value = folder(value, map[props[i]], props[i], map)

  return value }

///// Function every
// Does the predicate succeeds for each key/value pair in the map?
//
// every :: {k -> e}, (e, k, {k -> e} -> Bool) -> Bool
function every(map, predicate) {
  var i, props
  map   = Object(map)
  props = keys(map)
  i     = props.length
  while (i--)
    if (!predicate(map[props[i]], props[i], map))  return false
  return true }

///// Function some
// Does the predicate succeeds for any key/value pair in the map?
//
// some :: {k -> e}, (e, k, {k -> e} -> Bool) -> Bool
function some(map, predicate) {
  var i, props
  map   = Object(map)
  props = keys(map)
  i     = props.length
  while (i--)
    if (predicate(map[props[i]], props[i], map))  return true
  return false }

///// Function filter
// Yields a new map keeping only the own properties that pass the given
// filter.
//
// The map's prototype chain is maintained, but non-enumerable
// properties are discarded.
//
// filter :: {k -> e}, (e, k, {k -> e} -> Bool) -> {k -> e}
function filter(map, predicate) {
  var key
  var props  = keys(map)
  var i      = props.length
  var result = clone(proto(map))
  while (i--) {
    key = props[i]
    if (predicate(map[key], key, map))  result[key] = map[key] }
  return result }

///// Function map
// Yields a new map, where the values of each key/value pair are
// transformed by the given mapper function.
//
// The map's prototype chain is maintained, but non-enumerable
// properties are discarded.
//
// map :: {k -> e}, (e, k, {k -> e} -> e) -> {k -> e}
function map(map, mapper) {
  var key
  var props  = keys(map)
  var i      = props.length
  var result = clone(proto(map))
  while (i--) {
    key = props[i]
    result[key] = mapper(map[key], key, map) }
  return result }

//// -- Exports --------------------------------------------------------
module.exports = { each      : each

                 , put       : put
                 , remove    : remove
                 , replace   : replace
                 , clear     : clear

                 , at        : at
                 , size      : size
                 , empty_p   : empty_p
                 , has_key_p : has_key_p

                 , keys      : keys
                 , values    : values
                 , items     : items

                 , reduce    : reduce
                 , every     : every
                 , some      : some
                 , filter    : filter
                 , map       : map
                 }
<commit_msg>Adds summary to collection.map module.<commit_after>/// map.js --- Mapping collection handling
//
// Copyright (c) 2011 Quildreen Motta
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/// Module khaos.collection.map
//
// Provides services to work with objects as Maps, from `String' to
// `Any' kind of data.
//
// This includes basic traversing, manipulation, inspection,
// decomposition (getting keys, values or both from a Map) and the basic
// folding operations.
//
// There are a few things worth noting when working with this library:
//
// First, all functions that do something with the set of keys or set of
// values in an map, do so *at the map object level*. That is, they
// don't bother with the objects such a map delegates to — no messing
// with things in the prototype chain.
//
// Second, All of the pure functions that return a new Map in this
// module are extremely dangerous — they don't copy non-enumerable
// properties. This is per-design, as the library is to be compatible
// with ES3 environments, which don't provide any ways to list
// non-enumerable properties in an object.
//
// Another thing to keep in mind is that all pure functions do *only* a
// shallow clone of the object, such that any structural changes won't
// be noticed in other things that refer to those nested objects. Some
// of the functions do no cloning at all (reduce), and just expect the
// given callback function to not mutate the original object. Wishful
// thinking all the way.
//

//// -- Aliases --------------------------------------------------------
var _keys        = Object.keys
var clone        = Object.create
var proto        = Object.getPrototypeOf
var has_own_prop = {}.hasOwnProperty

//// -- Traversable ----------------------------------------------------

///// Function each
// Applies the iterator function to each key/value pair in the map.
//
// each :: {k -> e}, (e, k, {k -> e} -> Ignored) -> Undefined
function each(map, iterator) {
  var i, len
  var props = keys(map)
  for (i = 0, len = props.length; i < len; ++i)
    iterator(map[props[i]], props[i], map) }

//// -- Manipulation ---------------------------------------------------

///// Function put
// Assigns a value for the given key.
//
// put! :: {k -> e}*, k, e -> {k -> e}
function put(map, key, value) {
  Object(map)[key] = value
  return map }

///// Function remove
// Removes the given key and its associated value from the map.
//
// remove! :: {k -> e}*, k -> {k -> e}
function remove(map, key) {
  delete Object(map)[key]
  return map }

///// Function replace
// Replaces all the occurrences of `value' by the given `replacement'.
//
// This only replaces values that are directly set in the map
// itself.
//
// replace! :: {k -> e}*, e, e -> {k -> e}
function replace(map, value, replacement) {
  var key
  map = Object(map)
  for (key in map)
    if (has_key_p(map, key) && map[key] === value)
      map[key] = replacement

  return map }

///// Function clear
// Removes all key/value pairs from the map.
//
// clear! :: {k -> e}* -> {}
function clear(map) {
  each(map, function(value, key){
              delete map[key] })
  return map }

//// -- Inspection -----------------------------------------------------

///// Function at
// Retrieves the value for the given key.
//
// at :: {k -> e}, k, default:e -> e
// at :: {k -> e}, k -> Maybe e
function at(map, key, _default) {
  map = Object(map)

  return key in map?      map[key]
  :      /* otherwise */  _default }

///// Function size
// Returns the number of key/value pairs in the map.
//
// size :: {k -> e} -> Number
function size(map) {
  return keys(map).length }

///// Function empty_p
// Does the map have any key/value pairs?
//
// empty_p :: {k -> e} -> Bool
function empty_p(map) {
  return !size(map) }

///// Function has_key_p
// Does the map have the given key?
//
// has_key_p :: {k -> e}, k -> Bool
function has_key_p(map, key) {
  return map != null?     has_own_prop.call(map, key)
  :      /* otherwise */  false }

//// -- Decomposition --------------------------------------------------

///// Function keys
// Extracts the keys from the given map.
//
// keys :: {k -> e} -> [k]
function keys(map) {
  return _keys(Object(map)) }

///// Function values
// Extracts the values from the given map.
//
// values :: {k -> e} -> [e]
function values(map) {
  return reduce(map, [], function(acc, item, key) {
                           acc.push(item)
                           return acc })}

///// Function items
// Extracts the key/value pairs from the given map.
//
// items :: {k -> e} -> [(k, e)]
function items(map) {
  return reduce(map, [], function(acc, item, key) {
                           acc.push([key, item])
                           return acc })}

//// -- Folding --------------------------------------------------------

///// Function reduce
// Applies a function to each key/value pair in the map, returning the
// accumulated value.
//
// reduce :: {k -> e}, Any?, (Any, e, k, {k -> e} -> Any) -> Any
function reduce(map, value, folder) {
  var props, i, len
  map   = Object(map)
  props = keys(map)
  if (arguments.length == 2) {
    folder = value
    value  = map[props.shift()] }

  for (i = 0, len = props.length; i < len; ++i)
    value = folder(value, map[props[i]], props[i], map)

  return value }

///// Function every
// Does the predicate succeeds for each key/value pair in the map?
//
// every :: {k -> e}, (e, k, {k -> e} -> Bool) -> Bool
function every(map, predicate) {
  var i, props
  map   = Object(map)
  props = keys(map)
  i     = props.length
  while (i--)
    if (!predicate(map[props[i]], props[i], map))  return false
  return true }

///// Function some
// Does the predicate succeeds for any key/value pair in the map?
//
// some :: {k -> e}, (e, k, {k -> e} -> Bool) -> Bool
function some(map, predicate) {
  var i, props
  map   = Object(map)
  props = keys(map)
  i     = props.length
  while (i--)
    if (predicate(map[props[i]], props[i], map))  return true
  return false }

///// Function filter
// Yields a new map keeping only the own properties that pass the given
// filter.
//
// The map's prototype chain is maintained, but non-enumerable
// properties are discarded.
//
// filter :: {k -> e}, (e, k, {k -> e} -> Bool) -> {k -> e}
function filter(map, predicate) {
  var key
  var props  = keys(map)
  var i      = props.length
  var result = clone(proto(map))
  while (i--) {
    key = props[i]
    if (predicate(map[key], key, map))  result[key] = map[key] }
  return result }

///// Function map
// Yields a new map, where the values of each key/value pair are
// transformed by the given mapper function.
//
// The map's prototype chain is maintained, but non-enumerable
// properties are discarded.
//
// map :: {k -> e}, (e, k, {k -> e} -> e) -> {k -> e}
function map(map, mapper) {
  var key
  var props  = keys(map)
  var i      = props.length
  var result = clone(proto(map))
  while (i--) {
    key = props[i]
    result[key] = mapper(map[key], key, map) }
  return result }

//// -- Exports --------------------------------------------------------
module.exports = { each      : each

                 , put       : put
                 , remove    : remove
                 , replace   : replace
                 , clear     : clear

                 , at        : at
                 , size      : size
                 , empty_p   : empty_p
                 , has_key_p : has_key_p

                 , keys      : keys
                 , values    : values
                 , items     : items

                 , reduce    : reduce
                 , every     : every
                 , some      : some
                 , filter    : filter
                 , map       : map
                 }
<|endoftext|>


<commit_before>(function($){
	$.fn.qubit = function( options ) {
		return this.each(function() {
			var qubit = new Qubit(this, options);
		});
	};
	var Qubit = function( el ) {
		var self = this;
		this.scope = el = $(el);
		$('input[type=checkbox]', el).on('change', function(e) {
			self.process(e.target, e);
		});
		$('input[type=checkbox]:checked', el).each(function() {
			self.process(this)
		});
	};
	Qubit.prototype = {
		itemSelector: 'li',
		process: function( checkbox, event ) {
			var checkbox = $(checkbox),
				parentItems = checkbox.parentsUntil(this.scope, this.itemSelector),
				self = this;
			// all children inherit my state
			parentItems.eq(0).find('input[type=checkbox]').each(function() {
				if( !$(this).parent().hasClass('hidden') ) {
					self.setChecked(this, checkbox.prop('checked'), event);
				}
			});
			this.processParents(checkbox);
		},
		processParents: function( checkbox ) {
			checkbox = $(checkbox);
			var parentItems = checkbox.parentsUntil(this.scope, this.itemSelector),
					parent = parentItems.eq(1).children('input[type=checkbox]');
			// check parent is within our scope
			if( !$.contains(this.scope[0], parent[0]) ) return;
			if( parent.length > 0 ) {
				var siblings = this.getSiblings(checkbox, parentItems.eq(1)),
					checked = siblings.filter(':checked'),
					oldValue = this.getValue(parent), parentChecked = null;
				// if all siblings are checked
				if( siblings.length == checked.length ) {
					parentChecked = true;
				}
				// else if some are checked
				else if( checked.length > 0 ||
						// or indeterminate
						siblings.filter(isIndeterminate).length > 0 ) {
					this.setIndeterminate(parent, true);
				}
				// else none are checked
				else {
					parentChecked = false;
				}
				// update the parent
				if( parentChecked !== null ) this.setChecked(parent, parentChecked);
				// and go up the tree if it changed
				if( oldValue !== this.getValue(parent) ) this.processParents(parent);
			}
			function isIndeterminate() {
				return $(this).prop('indeterminate');
			}
		},
		setChecked: function( checkbox, value, event ) {
			$(checkbox).prop({
				checked: value,
				indeterminate: false
			});
		},
		setIndeterminate: function( checkbox, value ) {
			$(checkbox).prop({
				indeterminate: value,
				checked: null
			});
		},
		getSiblings: function( checkbox, listItem ) {
			listItem = listItem || checkbox.parentsUntil(this.itemSelector).get(1);
			return $('> ol > li > input[type=checkbox], > ul > li > input[type=checkbox]', listItem);
		},
		getValue: function( checkbox ) {
			return $(checkbox).prop('indeterminate') ? null : $(checkbox).prop('checked');
		}
	};
}(jQuery));
<commit_msg>Code formatting<commit_after>(function($) {
  $.fn.qubit = function(options) {
    return this.each(function() {
      var qubit = new Qubit(this, options);
    });
  };
  var Qubit = function(el) {
    var self = this;
    this.scope = el = $(el);
    $('input[type=checkbox]', el).on('change', function(e) {
      self.process(e.target, e);
    });
    $('input[type=checkbox]:checked', el).each(function() {
      self.process(this)
    });
  };
  Qubit.prototype = {
    itemSelector: 'li',
    process: function(checkbox, event) {
      var checkbox = $(checkbox),
          parentItems = checkbox.parentsUntil(this.scope, this.itemSelector),
          self = this;
      // all children inherit my state
      parentItems.eq(0).find('input[type=checkbox]').each(function() {
        if (!$(this).parent().hasClass('hidden')) {
          self.setChecked(this, checkbox.prop('checked'), event);
        }
      });
      this.processParents(checkbox);
    },
    processParents: function(checkbox) {
      checkbox = $(checkbox);
      var parentItems = checkbox.parentsUntil(this.scope, this.itemSelector),
          parent = parentItems.eq(1).children('input[type=checkbox]');
      // check parent is within our scope
      if (!$.contains(this.scope[0], parent[0])) return;
      if (parent.length > 0) {
        var siblings = this.getSiblings(checkbox, parentItems.eq(1)),
            checked = siblings.filter(':checked'),
            oldValue = this.getValue(parent), parentChecked = null;
        // if all siblings are checked
        if (siblings.length == checked.length) {
          parentChecked = true;
        }
        // else if some are checked
        else if (checked.length > 0 ||
            // or indeterminate
          siblings.filter(isIndeterminate).length > 0) {
          this.setIndeterminate(parent, true);
        }
        // else none are checked
        else {
          parentChecked = false;
        }
        // update the parent
        if (parentChecked !== null) this.setChecked(parent, parentChecked);
        // and go up the tree if it changed
        if (oldValue !== this.getValue(parent)) this.processParents(parent);
      }
      function isIndeterminate() {
        return $(this).prop('indeterminate');
      }
    },
    setChecked: function(checkbox, value, event) {
      $(checkbox).prop({
        checked: value,
        indeterminate: false
      });
    },
    setIndeterminate: function(checkbox, value) {
      $(checkbox).prop({
        indeterminate: value,
        checked: null
      });
    },
    getSiblings: function(checkbox, listItem) {
      listItem = listItem || checkbox.parentsUntil(this.itemSelector).get(1);
      return $('> ol > li > input[type=checkbox], > ul > li > input[type=checkbox]', listItem);
    },
    getValue: function(checkbox) {
      return $(checkbox).prop('indeterminate') ? null : $(checkbox).prop('checked');
    }
  };
}(jQuery));
<|endoftext|>


<commit_before>exports.run = (client, msg) => {
  if (msg.author.equals(client.user)) {
    return;
  }

  if (msg.content % 1 !== 0 || parseInt(msg.content, 10) !== client.count) {
    return msg.delete();
  }

  if (client.count % 100 === 0) {
    const embed = {
      color: msg.member.displayColor,
      title: `${client.count} milestone achieved!`,
      description: `${msg.author} brought the count up to ${client.count}! 🎉`,
      timestamp: new Date(),
      footer: {
        icon_url: msg.guild.iconURL,
        text: client.config.teemoQuotes[Math.floor(Math.random() * client.config.teemoQuotes.length)]
      }
    };
    msg.channel.send({ embed });
  }

  client.count += 1;
};

exports.channel = require('../../config.json').countChannelID;
<commit_msg>Add stricter integer checks for count.<commit_after>exports.run = (client, msg) => {
  if (msg.author.equals(client.user)) {
    return;
  }

  if (msg.content % 1 !== 0 || parseInt(msg.content, 10) !== client.count || parseInt(msg.content, 10).toString() !== msg.content) {
    return msg.delete();
  }

  if (client.count % 100 === 0) {
    const embed = {
      color: msg.member.displayColor,
      title: `${client.count} milestone achieved!`,
      description: `${msg.author} brought the count up to ${client.count}! 🎉`,
      timestamp: new Date(),
      footer: {
        icon_url: msg.guild.iconURL,
        text: client.config.teemoQuotes[Math.floor(Math.random() * client.config.teemoQuotes.length)]
      }
    };
    msg.channel.send({ embed });
  }

  client.count += 1;
};

exports.channel = require('../../config.json').countChannelID;
<|endoftext|>


<commit_before>/**
 * jQuery Tally v0.1.0
 *
 * Copyright 2011, Brian Beck
 * http://github.com/exogen/jquery-tally
 *
 */
(function($) {

    $.tally = {};

    // If jquery.numberformatter.js is present, use that.
    if ($.formatNumber && $.parseNumber) {
        $.tally.defaults = {};
        $.tally.parseNumber = $.parseNumber;
        $.tally.formatNumber = $.formatNumber;
    }
    else {
        $.tally.defaults = {
            decimalMark: '.',
            thousandsSeparator: ',',
            digitGroup: /(\d{1,3})(?=(?:\d{3})+$)/g,
            nonNumeric: /[^\d.-]/g
        };
        $.tally.parseNumber = function(string, options) {
            if (options !== $.tally.defaults)
                options = $.extend({}, $.tally.defaults, options);

            var _ = string.split(options.decimalMark, 2),
                string = _[0].replace(options.nonNumeric, ''),
                fraction = _[1];

            if (fraction)
                string += '.' + fraction;

            return parseFloat(string);
        };
        $.tally.formatNumber = function(number, options) {
            if (options !== $.tally.defaults)
                options = $.extend({}, $.tally.defaults, options);

            var _ = ('' + number).split('.', 2),
                replacement = '$1' + options.thousandsSeparator,
                string = _[0].replace(options.digitGroup, replacement),
                fraction = _[1];

            if (fraction)
                string += options.decimalMark + fraction;

            return string;
        };
    }

    $.fn.tally = function(number, duration, easing, complete) {
        return this.animate({tally: number}, duration, easing, complete);
    };

    // Add a fake CSS property to $.cssHooks to allow animation.
    $.cssHooks.tally = {
        get: function(elem) {
            var value = $.tally.parseNumber($(elem).text(), $.tally.defaults);
            return isNaN(value) ? 0 : +value;
        },
        set: function(elem, value) {
            $(elem).text($.tally.formatNumber(value, $.tally.defaults));
        }
    };

    // Enable animation by adding to $.fx.step.
    $.fx.step.tally = function(fx) {
        var now = fx.now;
        // Only show integers between fx.start and fx.end.
        if (fx.start < fx.end)
            now = Math.min(Math.ceil(now), fx.end);
        else if (fx.start > fx.end)
            now = Math.max(Math.floor(now), fx.end);
        $.cssHooks.tally.set(fx.elem, now);
    };

    // Prevent jQuery from adding 'px' to the value.
    $.cssNumber.tally = true;

})(jQuery);
<commit_msg>Clarified comment.<commit_after>/**
 * jQuery Tally v0.1.0
 *
 * Copyright 2011, Brian Beck
 * http://github.com/exogen/jquery-tally
 *
 */
(function($) {

    $.tally = {};

    // If jquery.numberformatter.js is present, use that.
    if ($.formatNumber && $.parseNumber) {
        $.tally.defaults = {};
        $.tally.parseNumber = $.parseNumber;
        $.tally.formatNumber = $.formatNumber;
    }
    else {
        $.tally.defaults = {
            decimalMark: '.',
            thousandsSeparator: ',',
            digitGroup: /(\d{1,3})(?=(?:\d{3})+$)/g,
            nonNumeric: /[^\d.-]/g
        };
        $.tally.parseNumber = function(string, options) {
            if (options !== $.tally.defaults)
                options = $.extend({}, $.tally.defaults, options);

            var _ = string.split(options.decimalMark, 2),
                string = _[0].replace(options.nonNumeric, ''),
                fraction = _[1];

            if (fraction)
                string += '.' + fraction;

            return parseFloat(string);
        };
        $.tally.formatNumber = function(number, options) {
            if (options !== $.tally.defaults)
                options = $.extend({}, $.tally.defaults, options);

            var _ = ('' + number).split('.', 2),
                replacement = '$1' + options.thousandsSeparator,
                string = _[0].replace(options.digitGroup, replacement),
                fraction = _[1];

            if (fraction)
                string += options.decimalMark + fraction;

            return string;
        };
    }

    $.fn.tally = function(number, duration, easing, complete) {
        return this.animate({tally: number}, duration, easing, complete);
    };

    // Enable reading and writing by adding a property to $.cssHooks.
    $.cssHooks.tally = {
        get: function(elem) {
            var value = $.tally.parseNumber($(elem).text(), $.tally.defaults);
            return isNaN(value) ? 0 : +value;
        },
        set: function(elem, value) {
            $(elem).text($.tally.formatNumber(value, $.tally.defaults));
        }
    };

    // Enable animation by adding to $.fx.step.
    $.fx.step.tally = function(fx) {
        var now = fx.now;
        // Only show integers between fx.start and fx.end.
        if (fx.start < fx.end)
            now = Math.min(Math.ceil(now), fx.end);
        else if (fx.start > fx.end)
            now = Math.max(Math.floor(now), fx.end);
        $.cssHooks.tally.set(fx.elem, now);
    };

    // Prevent jQuery from adding 'px' to the value.
    $.cssNumber.tally = true;

})(jQuery);
<|endoftext|>


<commit_before>/**
 * Mouse commands
 * All coordinates are relative to scrolled position (viewport).
 * If coordinates are out of viewport - events will not be dispatched.
 */

const seleniumCommand = require('selenium-webdriver/lib/command');
const input = require('selenium-webdriver/lib/input');
const TargetManager = require('../target-manager');
const modifiers = require('./keyboard/modifiers');

exports.commands = {
  [seleniumCommand.Name.CLICK_ELEMENT]: clickElement,
  [seleniumCommand.Name.CLICK]: click,
  [seleniumCommand.Name.DOUBLE_CLICK]: doubleClick,
  [seleniumCommand.Name.MOVE_TO]: moveTo,
};

// map selenium buttons to debugger buttons
const BUTTONS = {
  [input.Button.LEFT]: 'left',
  [input.Button.MIDDLE]: 'middle',
  [input.Button.RIGHT]: 'right',
};

// store last moveTo coords as selenium does not send it in 'click' command
let lastMoveTo = {
  x: 0,
  y: 0,
};

function clickElement(params) {
  return Promise.resolve()
     // .then(() => getScrollXY())
    // temp: scroll to top for correct coords
    // todo: scroll to element in center layout
    // todo: check element is visible!
    // see also: getNodeForLocation
    .then(() => scrollToXY(0, 0))
    .then(() => getElementCenter(params.id))
    .then(center => scrollToXY(center.x, center.y))
    .then(() => getElementCenter(params.id))
    //.then(() => new Promise(r => setTimeout(r, 1000)))
    .then(center => {
      return Promise.resolve()
        .then(() => highlightClick(center.x, center.y))
        .then(() => moveAndClickXY(center.x, center.y));
    });
}

/**
 * Click particular button on current location
 *
 * @param {Object} params
 * @param {Number} params.button
 * @returns {Promise}
 */
function click(params) {
  const button = BUTTONS[params.button];
  return clickXY(lastMoveTo.x, lastMoveTo.y, button);
}

function doubleClick(params) {
  const button = BUTTONS[params.button];
  return clickXY(lastMoveTo.x, lastMoveTo.y, button, 2);
}

function moveTo(params) {
  // todo: opt_offset
  const position =  params.element
    ? getElementCenter(params.element)
    : Promise.resolve({x: params.x, y: params.y});
  return position
    .then(p => moveToXY(p.x, p.y));
}

// === internal ===

function clickXY(x, y, button = 'left', count = 1) {
  return Promise.resolve()
    .then(() => buttonAction(x, y, 'mousePressed', button, count))
    .then(() => buttonAction(x, y, 'mouseReleased', button, count));
}

function moveToXY(x, y) {
  return dispatchMouseEvent({
    button: 'none',
    clickCount: 0,
    type: 'mouseMoved',
    x: x,
    y: y
  })
  .then(() => lastMoveTo = {x, y});
}

function moveAndClickXY(x, y) {
  return Promise.resolve()
    .then(() => moveToXY(x, y))
    .then(() => clickXY(x, y));
}

function buttonAction(x, y, type, button, count) {
  return dispatchMouseEvent({
    button: button,
    clickCount: count,
    type: type,
    x: x,
    y: y
  });
}

function dispatchMouseEvent(options) {
  options.modifiers = modifiers.get();
  return TargetManager.debugger.sendCommand('Input.dispatchMouseEvent', options);
}

function getElementCenter(id) {
  return TargetManager.debugger.sendCommand('DOM.getBoxModel', {
      nodeId: Number(id)
    })
    .then(res => getQuadCenter(res.model.content));
}

function getQuadCenter(quad) {
  return {
    x: quad[0] + Math.round((quad[2] - quad[0]) / 2),
    y: quad[3] + Math.round((quad[5] - quad[3]) / 2),
  };
}

// function to scroll
// temp as it can be used in several other commands
function scrollToXY(x, y) {
  return TargetManager.debugger.sendCommand('Runtime.evaluate', {
    expression: `window.scrollTo(${x}, ${y})`
  });
}

function highlightXY(x, y, frame) {
  const size = Math.round(5 + 50 * frame);
  return TargetManager.debugger.sendCommand('DOM.highlightRect', {
    x: x - Math.round(size / 2),
    y: y - Math.round(size / 2),
    width: size,
    height: size,
    color: {r: Math.round(255 * (1 - frame)), g: 0, b: 0, a: 1 - frame}
  });
}

function highlightClick(x, y) {
  const duration = 500;
  const interval = 40; // 25 frames per second
  const framesCount = Math.ceil(duration / interval);
  const frames = []; res = Promise.resolve();
  for (let i = 0; i <= framesCount; i++) {
    frames.push(i / framesCount);
  }
  return frames.reduce((res, frame) => {
    return res
      .then(() => highlightXY(x, y, frame))
      .then(() => new Promise(resolve => setTimeout(resolve, interval)))
  }, Promise.resolve());

}

function getScrollXY() {
  return TargetManager.debugger.sendCommand('Runtime.evaluate', {
    // expression: `(function() {return {y: window.scrollY}}())`
    expression: `window.scrollY`
  });
}
<commit_msg>mouse: lastMoveTo use const instead of let<commit_after>/**
 * Mouse commands
 * All coordinates are relative to scrolled position (viewport).
 * If coordinates are out of viewport - events will not be dispatched.
 */

const seleniumCommand = require('selenium-webdriver/lib/command');
const input = require('selenium-webdriver/lib/input');
const TargetManager = require('../target-manager');
const modifiers = require('./keyboard/modifiers');

exports.commands = {
  [seleniumCommand.Name.CLICK_ELEMENT]: clickElement,
  [seleniumCommand.Name.CLICK]: click,
  [seleniumCommand.Name.DOUBLE_CLICK]: doubleClick,
  [seleniumCommand.Name.MOVE_TO]: moveTo,
};

// map selenium buttons to debugger buttons
const BUTTONS = {
  [input.Button.LEFT]: 'left',
  [input.Button.MIDDLE]: 'middle',
  [input.Button.RIGHT]: 'right',
};

// store last moveTo coords as selenium does not send it in 'click' command
const lastMoveTo = {
  x: 0,
  y: 0,
};

function clickElement(params) {
  return Promise.resolve()
     // .then(() => getScrollXY())
    // temp: scroll to top for correct coords
    // todo: scroll to element in center layout
    // todo: check element is visible!
    // see also: getNodeForLocation
    .then(() => scrollToXY(0, 0))
    .then(() => getElementCenter(params.id))
    .then(center => scrollToXY(center.x, center.y))
    .then(() => getElementCenter(params.id))
    //.then(() => new Promise(r => setTimeout(r, 1000)))
    .then(center => {
      return Promise.resolve()
        .then(() => highlightClick(center.x, center.y))
        .then(() => moveAndClickXY(center.x, center.y));
    });
}

/**
 * Click particular button on current location
 *
 * @param {Object} params
 * @param {Number} params.button
 * @returns {Promise}
 */
function click(params) {
  const button = BUTTONS[params.button];
  return clickXY(lastMoveTo.x, lastMoveTo.y, button);
}

function doubleClick(params) {
  const button = BUTTONS[params.button];
  return clickXY(lastMoveTo.x, lastMoveTo.y, button, 2);
}

function moveTo(params) {
  // todo: opt_offset
  const position =  params.element
    ? getElementCenter(params.element)
    : Promise.resolve({x: params.x, y: params.y});
  return position
    .then(p => moveToXY(p.x, p.y));
}

// === internal ===

function clickXY(x, y, button = 'left', count = 1) {
  return Promise.resolve()
    .then(() => buttonAction(x, y, 'mousePressed', button, count))
    .then(() => buttonAction(x, y, 'mouseReleased', button, count));
}

function moveToXY(x, y) {
  return dispatchMouseEvent({
    button: 'none',
    clickCount: 0,
    type: 'mouseMoved',
    x: x,
    y: y
  })
  .then(() => Object.assign(lastMoveTo, {x, y}));
}

function moveAndClickXY(x, y) {
  return Promise.resolve()
    .then(() => moveToXY(x, y))
    .then(() => clickXY(x, y));
}

function buttonAction(x, y, type, button, count) {
  return dispatchMouseEvent({
    button: button,
    clickCount: count,
    type: type,
    x: x,
    y: y
  });
}

function dispatchMouseEvent(options) {
  options.modifiers = modifiers.get();
  return TargetManager.debugger.sendCommand('Input.dispatchMouseEvent', options);
}

function getElementCenter(id) {
  return TargetManager.debugger.sendCommand('DOM.getBoxModel', {
      nodeId: Number(id)
    })
    .then(res => getQuadCenter(res.model.content));
}

function getQuadCenter(quad) {
  return {
    x: quad[0] + Math.round((quad[2] - quad[0]) / 2),
    y: quad[3] + Math.round((quad[5] - quad[3]) / 2),
  };
}

// function to scroll
// temp as it can be used in several other commands
function scrollToXY(x, y) {
  return TargetManager.debugger.sendCommand('Runtime.evaluate', {
    expression: `window.scrollTo(${x}, ${y})`
  });
}

function highlightXY(x, y, frame) {
  const size = Math.round(5 + 50 * frame);
  return TargetManager.debugger.sendCommand('DOM.highlightRect', {
    x: x - Math.round(size / 2),
    y: y - Math.round(size / 2),
    width: size,
    height: size,
    color: {r: Math.round(255 * (1 - frame)), g: 0, b: 0, a: 1 - frame}
  });
}

function highlightClick(x, y) {
  const duration = 500;
  const interval = 40; // 25 frames per second
  const framesCount = Math.ceil(duration / interval);
  const frames = []; res = Promise.resolve();
  for (let i = 0; i <= framesCount; i++) {
    frames.push(i / framesCount);
  }
  return frames.reduce((res, frame) => {
    return res
      .then(() => highlightXY(x, y, frame))
      .then(() => new Promise(resolve => setTimeout(resolve, interval)))
  }, Promise.resolve());

}

function getScrollXY() {
  return TargetManager.debugger.sendCommand('Runtime.evaluate', {
    // expression: `(function() {return {y: window.scrollY}}())`
    expression: `window.scrollY`
  });
}
<|endoftext|>