<commit_before>module.exports = function (grunt) {
  'use strict';

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    banner: '/*!\n' +
    ' * peeracle v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
    ' * Copyright 2015\n' +
    ' * Licensed under <%= pkg.license %>\n' +
    ' */\n',

    karma: {
      unit: {
        configFile: 'karma.conf.js'
      }
    },

    concat: {
      options: {
        banner: '<%= banner %>\n' +
        '\'use strict\';\n' +
        'var Peeracle = {};\n' +
        'Peeracle.Media = {};\n',

        process: function (src, filepath) {
          return src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1')
            .replace(/(^|\n)[ \t]*(var Peeracle = Peeracle \|\| {});?\s*/g, '$1')
            .replace(/module.exports = (.*);/g, 'Peeracle.$1 = $1;');
        },
        stripBanners: false
      },
      dist: {
        src: [
          'src/utils.js',
          'src/file.js',
          'src/media.js',
          'src/media.webm.js',
          'src/metadata.js',
          'src/mediachannel.js',
          'src/signalchannel.js',
          'src/peer.js',
          'src/tracker.js'
        ],
        dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.js'
      }
    },

    preprocess: {
      inline: {
        src: ['dist/*.js'],
        options: {
          inline: true,
          context: {
            DEBUG: false
          }
        }
      }
    },

    uglify: {
      options: {
        preserveComments: 'some'
      },
      build: {
        src: 'dist/<%= pkg.name %>-<%= pkg.version %>.js',
        dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.min.js'
      }
    }
  });

  // Load the plugin that provides the "uglify" task.
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-preprocess');

  // Default task(s).
  grunt.registerTask('default', ['concat', 'preprocess', 'karma', 'uglify']);

  // Build only task
  grunt.registerTask('dist', ['concat', 'preprocess', 'uglify']);
};
<commit_msg>Remove media.js from Gruntfile<commit_after>module.exports = function (grunt) {
  'use strict';

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    banner: '/*!\n' +
    ' * peeracle v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
    ' * Copyright 2015\n' +
    ' * Licensed under <%= pkg.license %>\n' +
    ' */\n',

    karma: {
      unit: {
        configFile: 'karma.conf.js'
      }
    },

    concat: {
      options: {
        banner: '<%= banner %>\n' +
        '\'use strict\';\n' +
        'var Peeracle = {};\n' +
        'Peeracle.Media = {};\n',

        process: function (src, filepath) {
          return src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1')
            .replace(/(^|\n)[ \t]*(var Peeracle = Peeracle \|\| {});?\s*/g, '$1')
            .replace(/module.exports = (.*);/g, 'Peeracle.$1 = $1;');
        },
        stripBanners: false
      },
      dist: {
        src: [
          'src/utils.js',
          'src/file.js',
          'src/media.webm.js',
          'src/metadata.js',
          'src/mediachannel.js',
          'src/signalchannel.js',
          'src/peer.js',
          'src/tracker.js'
        ],
        dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.js'
      }
    },

    preprocess: {
      inline: {
        src: ['dist/*.js'],
        options: {
          inline: true,
          context: {
            DEBUG: false
          }
        }
      }
    },

    uglify: {
      options: {
        preserveComments: 'some'
      },
      build: {
        src: 'dist/<%= pkg.name %>-<%= pkg.version %>.js',
        dest: 'dist/<%= pkg.name %>-<%= pkg.version %>.min.js'
      }
    }
  });

  // Load the plugin that provides the "uglify" task.
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-preprocess');

  // Default task(s).
  grunt.registerTask('default', ['concat', 'preprocess', 'karma', 'uglify']);

  // Build only task
  grunt.registerTask('dist', ['concat', 'preprocess', 'uglify']);
};
<|endoftext|>


<commit_before>module.exports = function(grunt) {

  'use strict';

  // var version = require('./package.json').version;

  grunt.initConfig({

    config: {
      output: 'dist/jasmine-matchers.js',
      sources: {
        src: 'src/**/*.js',
        test: 'test/**/*.js',
        all: [
          '<%= config.sources.src %>',
          '<%= config.sources.test %>'
        ]
      }
    },

    concat: {
      dist: {
        files: [{
          src: [
            'src/wrapper/head.txt',
            'src/wrapper/private.js',
            'src/*.js',
            'src/members/*.js',
            'src/wrapper/add-matchers-adapter.js',
            'src/wrapper/foot.txt'
          ],
          dest: '<%= config.output %>'
        }]
      }
    },

    jsbeautifier: {
      dist: {
        options: {
          js: {
            indentSize: 2
          }
        },
        src: '<%= config.output %>'
      }
    },

    watch: {
      scripts: {
        files: '<%= config.sources.all %>',
        tasks: [
          'build'
        ],
        options: {
          nospawn: true
        }
      }
    },

    jscs: {
      dist: {
        src: '<%= config.sources.src %>'
      }
    },

    jshint: {
      dist: {
        src: '<%= config.sources.src %>'
      }
    },

    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true,
        browsers: [
          'PhantomJS'
        ]
      }
    }

  });

  [
    'grunt-contrib-concat',
    'grunt-contrib-jshint',
    'grunt-contrib-watch',
    'grunt-jsbeautifier',
    'grunt-jscs',
    'grunt-karma'
  ].forEach(grunt.loadNpmTasks);

  grunt.registerTask('build', [
    'jshint',
    'jscs',
    'karma',
    'concat',
    'jsbeautifier'
  ]);

};
<commit_msg>Separate code-quality and build tasks.<commit_after>module.exports = function(grunt) {

  'use strict';

  // var version = require('./package.json').version;

  grunt.initConfig({

    config: {
      output: 'dist/jasmine-matchers.js',
      sources: {
        src: 'src/**/*.js',
        test: 'test/**/*.js',
        all: [
          '<%= config.sources.src %>',
          '<%= config.sources.test %>'
        ]
      }
    },

    concat: {
      dist: {
        files: [{
          src: [
            'src/wrapper/head.txt',
            'src/wrapper/private.js',
            'src/*.js',
            'src/members/*.js',
            'src/wrapper/add-matchers-adapter.js',
            'src/wrapper/foot.txt'
          ],
          dest: '<%= config.output %>'
        }]
      }
    },

    jsbeautifier: {
      dist: {
        options: {
          js: {
            indentSize: 2
          }
        },
        src: '<%= config.output %>'
      }
    },

    watch: {
      scripts: {
        files: '<%= config.sources.all %>',
        tasks: [
          'build'
        ],
        options: {
          nospawn: true
        }
      }
    },

    jscs: {
      dist: {
        src: '<%= config.sources.src %>'
      }
    },

    jshint: {
      dist: {
        src: '<%= config.sources.src %>'
      }
    },

    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true,
        browsers: [
          'PhantomJS'
        ]
      }
    }

  });

  [
    'grunt-contrib-concat',
    'grunt-contrib-jshint',
    'grunt-contrib-watch',
    'grunt-jsbeautifier',
    'grunt-jscs',
    'grunt-karma'
  ].forEach(grunt.loadNpmTasks);

  grunt.registerTask('code-quality', [
    'jshint',
    'jscs',
    'karma'
  ]);

  grunt.registerTask('build', [
    'concat',
    'jsbeautifier'
  ]);

};
<|endoftext|>


<commit_before>import {fireEvent} from '..'

const eventTypes = [
  {
    type: 'Clipboard',
    events: ['copy', 'paste'],
    elementType: 'input',
  },
  {
    type: 'Composition',
    events: ['compositionEnd', 'compositionStart', 'compositionUpdate'],
    elementType: 'input',
  },
  {
    type: 'Keyboard',
    events: ['keyDown', 'keyPress', 'keyUp'],
    elementType: 'input',
  },
  {
    type: 'Focus',
    events: ['focus', 'blur', 'focusIn', 'focusOut'],
    elementType: 'input',
  },
  {
    type: 'Input',
    events: ['change', 'input', 'invalid'],
    elementType: 'input',
  },
  {
    type: 'Form',
    events: ['submit', 'reset'],
    elementType: 'form',
  },
  {
    type: 'Mouse',
    events: [
      'click',
      'contextMenu',
      'dblClick',
      'drag',
      'dragEnd',
      'dragEnter',
      'dragExit',
      'dragLeave',
      'dragOver',
      'dragStart',
      'drop',
      'mouseDown',
      'mouseEnter',
      'mouseLeave',
      'mouseMove',
      'mouseOut',
      'mouseOver',
      'mouseUp',
    ],
    elementType: 'button',
  },
  {
    type: 'Selection',
    events: ['select'],
    elementType: 'input',
  },
  {
    type: 'Touch',
    events: ['touchCancel', 'touchEnd', 'touchMove', 'touchStart'],
    elementType: 'button',
  },
  {
    type: 'UI',
    events: ['scroll'],
    elementType: 'div',
  },
  {
    type: 'Wheel',
    events: ['wheel'],
    elementType: 'div',
  },
  {
    type: 'Media',
    events: [
      'abort',
      'canPlay',
      'canPlayThrough',
      'durationChange',
      'emptied',
      'encrypted',
      'ended',
      'error',
      'loadedData',
      'loadedMetadata',
      'loadStart',
      'pause',
      'play',
      'playing',
      'progress',
      'rateChange',
      'seeked',
      'seeking',
      'stalled',
      'suspend',
      'timeUpdate',
      'volumeChange',
      'waiting',
    ],
    elementType: 'video',
  },
  {
    type: 'Image',
    events: ['load', 'error'],
    elementType: 'img',
  },
  {
    type: 'Animation',
    events: ['animationStart', 'animationEnd', 'animationIteration'],
    elementType: 'div',
  },
  {
    type: 'Transition',
    events: ['transitionEnd'],
    elementType: 'div',
  },
  {
    type: 'Pointer',
    events: [
      'pointerOver',
      'pointerEnter',
      'pointerDown',
      'pointerMove',
      'pointerUp',
      'pointerCancel',
      'pointerOut',
      'pointerLeave',
      'gotPointerCapture',
      'lostPointerCapture',
    ],
    elementType: 'div',
  },
]

eventTypes.forEach(({type, events, elementType}) => {
  describe(`${type} Events`, () => {
    events.forEach(eventName => {
      it(`fires ${eventName}`, () => {
        const node = document.createElement(elementType)
        const spy = jest.fn()
        node.addEventListener(eventName.toLowerCase(), spy)
        fireEvent[eventName](node)
        expect(spy).toHaveBeenCalledTimes(1)
      })
    })
  })
})

describe(`Aliased Events`, () => {
  it(`fires doubleClick`, () => {
    const node = document.createElement('div')
    const spy = jest.fn()
    node.addEventListener('dblclick', spy)
    fireEvent.doubleClick(node)
    expect(spy).toHaveBeenCalledTimes(1)
  })
})

test('assigns target properties', () => {
  const node = document.createElement('input')
  const spy = jest.fn()
  const value = 'a'
  node.addEventListener('change', spy)
  fireEvent.change(node, {target: {value}})
  expect(spy).toHaveBeenCalledTimes(1)
  expect(node.value).toBe(value)
})

test('assigns selection-related target properties', () => {
  const node = document.createElement('input')
  const spy = jest.fn()
  const value = 'ab'
  const selectionStart = 1
  const selectionEnd = 2
  node.addEventListener('change', spy)
  fireEvent.change(node, {target: {value, selectionStart, selectionEnd}})
  expect(node.value).toBe(value)
  expect(node.selectionStart).toBe(selectionStart)
  expect(node.selectionEnd).toBe(selectionEnd)
})

test('assigning a value to a target that cannot have a value throws an error', () => {
  const node = document.createElement('div')
  expect(() =>
    fireEvent.change(node, {target: {value: 'a'}}),
  ).toThrowErrorMatchingInlineSnapshot(
    `"The given element does not have a value setter"`,
  )
})

test('assigning the files property on an input', () => {
  const node = document.createElement('input')
  const file = new document.defaultView.File(['(⌐□_□)'], 'chucknorris.png', {
    type: 'image/png',
  })
  fireEvent.change(node, {target: {files: [file]}})
  expect(node.files).toEqual([file])
})

test('fires events on Window', () => {
  const messageSpy = jest.fn()
  window.addEventListener('message', messageSpy)
  fireEvent(window, new window.MessageEvent('message', {data: 'hello'}))
  expect(messageSpy).toHaveBeenCalledTimes(1)
  window.removeEventListener('message', messageSpy)
})

test('fires shortcut events on Window', () => {
  const clickSpy = jest.fn()
  window.addEventListener('click', clickSpy)
  fireEvent.click(window)
  expect(clickSpy).toHaveBeenCalledTimes(1)
  window.removeEventListener('message', clickSpy)
})

test('throws a useful error message when firing events on non-existent nodes', () => {
  expect(() => fireEvent(undefined, new MouseEvent('click'))).toThrow(
    'Unable to fire a "click" event - please provide a DOM element.',
  )
})

test('throws a useful error message when firing events on non-existent nodes (shortcut)', () => {
  expect(() => fireEvent.click(undefined)).toThrow(
    'Unable to fire a "click" event - please provide a DOM element.',
  )
})

test('throws a useful error message when firing non-events', () => {
  expect(() => fireEvent(document.createElement('div'), undefined)).toThrow(
    'Unable to fire an event - please provide an event object.',
  )
})

test('fires events on Document', () => {
  const keyDownSpy = jest.fn()
  document.addEventListener('keydown', keyDownSpy)
  fireEvent.keyDown(document, {key: 'Escape'})
  expect(keyDownSpy).toHaveBeenCalledTimes(1)
  document.removeEventListener('keydown', keyDownSpy)
})
<commit_msg>test: add bubbling tests (#386)<commit_after>import {fireEvent} from '..'

const eventTypes = [
  {
    type: 'Clipboard',
    events: ['copy', 'paste'],
    elementType: 'input',
  },
  {
    type: 'Composition',
    events: ['compositionEnd', 'compositionStart', 'compositionUpdate'],
    elementType: 'input',
  },
  {
    type: 'Keyboard',
    events: ['keyDown', 'keyPress', 'keyUp'],
    elementType: 'input',
  },
  {
    type: 'Focus',
    events: ['focus', 'blur', 'focusIn', 'focusOut'],
    elementType: 'input',
  },
  {
    type: 'Input',
    events: ['change', 'input', 'invalid'],
    elementType: 'input',
  },
  {
    type: 'Form',
    events: ['submit', 'reset'],
    elementType: 'form',
  },
  {
    type: 'Mouse',
    events: [
      'click',
      'contextMenu',
      'dblClick',
      'drag',
      'dragEnd',
      'dragEnter',
      'dragExit',
      'dragLeave',
      'dragOver',
      'dragStart',
      'drop',
      'mouseDown',
      'mouseEnter',
      'mouseLeave',
      'mouseMove',
      'mouseOut',
      'mouseOver',
      'mouseUp',
    ],
    elementType: 'button',
  },
  {
    type: 'Selection',
    events: ['select'],
    elementType: 'input',
  },
  {
    type: 'Touch',
    events: ['touchCancel', 'touchEnd', 'touchMove', 'touchStart'],
    elementType: 'button',
  },
  {
    type: 'UI',
    events: ['scroll'],
    elementType: 'div',
  },
  {
    type: 'Wheel',
    events: ['wheel'],
    elementType: 'div',
  },
  {
    type: 'Media',
    events: [
      'abort',
      'canPlay',
      'canPlayThrough',
      'durationChange',
      'emptied',
      'encrypted',
      'ended',
      'error',
      'loadedData',
      'loadedMetadata',
      'loadStart',
      'pause',
      'play',
      'playing',
      'progress',
      'rateChange',
      'seeked',
      'seeking',
      'stalled',
      'suspend',
      'timeUpdate',
      'volumeChange',
      'waiting',
    ],
    elementType: 'video',
  },
  {
    type: 'Image',
    events: ['load', 'error'],
    elementType: 'img',
  },
  {
    type: 'Animation',
    events: ['animationStart', 'animationEnd', 'animationIteration'],
    elementType: 'div',
  },
  {
    type: 'Transition',
    events: ['transitionEnd'],
    elementType: 'div',
  },
  {
    type: 'Pointer',
    events: [
      'pointerOver',
      'pointerEnter',
      'pointerDown',
      'pointerMove',
      'pointerUp',
      'pointerCancel',
      'pointerOut',
      'pointerLeave',
      'gotPointerCapture',
      'lostPointerCapture',
    ],
    elementType: 'div',
  },
]

const bubblingEvents = [
  'copy',
  'cut',
  'paste',
  'compositionEnd',
  'compositionStart',
  'compositionUpdate',
  'keyDown',
  'keyPress',
  'keyUp',
  'focusIn',
  'focusOut',
  'change',
  'input',
  'submit',
  'reset',
  'click',
  'contextMenu',
  'dblClick',
  'drag',
  'dragEnd',
  'dragEnter',
  'dragExit',
  'dragLeave',
  'dragOver',
  'dragStart',
  'drop',
  'mouseDown',
  'mouseMove',
  'mouseOut',
  'mouseOver',
  'mouseUp',
  'select',
  'touchCancel',
  'touchEnd',
  'touchMove',
  'touchStart',
  'wheel',
  'animationStart',
  'animationEnd',
  'animationIteration',
  'transitionEnd',
  'pointerOver',
  'pointerDown',
  'pointerMove',
  'pointerUp',
  'pointerCancel',
  'pointerOut',
]

const nonBubblingEvents = [
  'focus',
  'blur',
  'invalid',
  'mouseEnter',
  'mouseLeave',
  'scroll',
  'abort',
  'canPlay',
  'canPlayThrough',
  'durationChange',
  'emptied',
  'encrypted',
  'ended',
  'loadedData',
  'loadedMetadata',
  'loadStart',
  'pause',
  'play',
  'playing',
  'progress',
  'rateChange',
  'seeked',
  'seeking',
  'stalled',
  'suspend',
  'timeUpdate',
  'volumeChange',
  'waiting',
  'load',
  'error',
  'pointerEnter',
  'pointerLeave',
  'gotPointerCapture',
  'lostPointerCapture',
]

eventTypes.forEach(({type, events, elementType}) => {
  describe(`${type} Events`, () => {
    events.forEach(eventName => {
      it(`fires ${eventName}`, () => {
        const node = document.createElement(elementType)
        const spy = jest.fn()
        node.addEventListener(eventName.toLowerCase(), spy)
        fireEvent[eventName](node)
        expect(spy).toHaveBeenCalledTimes(1)
      })
    })
  })
})

describe(`Bubbling Events`, () => {
  bubblingEvents.forEach(event =>
    it(`bubbles ${event}`, () => {
      const node = document.createElement('div')
      const spy = jest.fn()
      node.addEventListener(event.toLowerCase(), spy)

      const innerNode = document.createElement('div')
      node.appendChild(innerNode)

      fireEvent[event](innerNode)
      expect(spy).toHaveBeenCalledTimes(1)
    }),
  )

  nonBubblingEvents.forEach(event =>
    it(`doesn't bubble ${event}`, () => {
      const node = document.createElement('div')
      const spy = jest.fn()
      node.addEventListener(event.toLowerCase(), spy)

      const innerNode = document.createElement('div')
      node.appendChild(innerNode)

      fireEvent[event](innerNode)
      expect(spy).not.toHaveBeenCalled()
    }),
  )
})

describe(`Aliased Events`, () => {
  it(`fires doubleClick`, () => {
    const node = document.createElement('div')
    const spy = jest.fn()
    node.addEventListener('dblclick', spy)
    fireEvent.doubleClick(node)
    expect(spy).toHaveBeenCalledTimes(1)
  })
})

test('assigns target properties', () => {
  const node = document.createElement('input')
  const spy = jest.fn()
  const value = 'a'
  node.addEventListener('change', spy)
  fireEvent.change(node, {target: {value}})
  expect(spy).toHaveBeenCalledTimes(1)
  expect(node.value).toBe(value)
})

test('assigns selection-related target properties', () => {
  const node = document.createElement('input')
  const spy = jest.fn()
  const value = 'ab'
  const selectionStart = 1
  const selectionEnd = 2
  node.addEventListener('change', spy)
  fireEvent.change(node, {target: {value, selectionStart, selectionEnd}})
  expect(node.value).toBe(value)
  expect(node.selectionStart).toBe(selectionStart)
  expect(node.selectionEnd).toBe(selectionEnd)
})

test('assigning a value to a target that cannot have a value throws an error', () => {
  const node = document.createElement('div')
  expect(() =>
    fireEvent.change(node, {target: {value: 'a'}}),
  ).toThrowErrorMatchingInlineSnapshot(
    `"The given element does not have a value setter"`,
  )
})

test('assigning the files property on an input', () => {
  const node = document.createElement('input')
  const file = new document.defaultView.File(['(⌐□_□)'], 'chucknorris.png', {
    type: 'image/png',
  })
  fireEvent.change(node, {target: {files: [file]}})
  expect(node.files).toEqual([file])
})

test('fires events on Window', () => {
  const messageSpy = jest.fn()
  window.addEventListener('message', messageSpy)
  fireEvent(window, new window.MessageEvent('message', {data: 'hello'}))
  expect(messageSpy).toHaveBeenCalledTimes(1)
  window.removeEventListener('message', messageSpy)
})

test('fires shortcut events on Window', () => {
  const clickSpy = jest.fn()
  window.addEventListener('click', clickSpy)
  fireEvent.click(window)
  expect(clickSpy).toHaveBeenCalledTimes(1)
  window.removeEventListener('click', clickSpy)
})

test('throws a useful error message when firing events on non-existent nodes', () => {
  expect(() => fireEvent(undefined, new MouseEvent('click'))).toThrow(
    'Unable to fire a "click" event - please provide a DOM element.',
  )
})

test('throws a useful error message when firing events on non-existent nodes (shortcut)', () => {
  expect(() => fireEvent.click(undefined)).toThrow(
    'Unable to fire a "click" event - please provide a DOM element.',
  )
})

test('throws a useful error message when firing non-events', () => {
  expect(() => fireEvent(document.createElement('div'), undefined)).toThrow(
    'Unable to fire an event - please provide an event object.',
  )
})

test('fires events on Document', () => {
  const keyDownSpy = jest.fn()
  document.addEventListener('keydown', keyDownSpy)
  fireEvent.keyDown(document, {key: 'Escape'})
  expect(keyDownSpy).toHaveBeenCalledTimes(1)
  document.removeEventListener('keydown', keyDownSpy)
})
<|endoftext|>


<commit_before>'use strict';

var remove   = require('es5-ext/lib/Array/prototype/remove')
  , d        = require('es5-ext/lib/Object/descriptor')
  , repeat   = require('es5-ext/lib/String/prototype/repeat')
  , memoize  = require('memoizee')
  , deferred = require('deferred')
  , clc      = require('cli-color')
  , Base     = require('./console').Reporter

  , floor = Math.floor, max = Math.max, min = Math.min
  , now = Date.now
  , nextTick = process.nextTick
  , log = process.stdout.write.bind(process.stdout)

  , Reporter, distColumns, areReportsFullyExposed;

// Distributes reports per column e.g. [6, 6, 7], [2, 3, 3, 3]
distColumns = function (columns, length, max) {
	var base, rest, result, index;
	base = floor(length / columns);
	if (!base) {
		base = 1;
		rest = 0;
	} else if (base >= max) {
		base = max;
		rest = 0;
	} else {
		rest = length - base * columns;
	}

	result = [];

	while (columns--) {
		result.push(base);
	}
	if (rest) {
		index = result.length - 1;
		while (rest--) {
			result[index] = result[index] - 1;
			--index;
		}
	}
	return result;
};

areReportsFullyExposed = function (cells) {
	return cells.every(function (cell) {
		return cell.report.length === cell.length;
	});
};

Reporter = function () {
	Base.apply(this, arguments);

	this.totalWidth = clc.width;
	this.totalHeight = clc.height;
	this.pending = [];
	this.pending.remove = remove;
	this.errored = {};
};
Reporter.prototype = Object.create(Base.prototype, {
	init: d(function () {
		var reports = {}, updateMap = {}, waiting = [];

		this.data.on('change', function (event) {
			if (event.type === 'remove') {
				this.subtract(reports[event.name]);
				delete reports[event.name];
				return;
			} else if (event.type === 'update') {
				this.subtract(reports[event.name]);
			}
			this.add(reports[event.name] = event.report);
		}.bind(this));

		// Reset
		this.log(clc.reset);

		// Bar
		this.log(clc.set(0, 2) + this.emap.bar(repeat.call('-', this.totalWidth)));

		// Status
		this.logStatus();

		// Columns
		this.maxColumns = max(floor(this.totalWidth / 90), 1);
		this.columnHeight = this.totalHeight - 3;
		this.maxPerColumn = floor(this.columnHeight / 6);

		this.data.on('change', function (event) {
			var promise, time;
			time = updateMap[event.name] = now();
			if (event.report && event.report.length) {
				promise = this.normalize(event.report)(function (report) {
					if (time !== updateMap[event.name]) {
						// Race condition, abort
						return;
					}
					if (!report) {
						this.subtract(event.report);
						this.logStatus();
						if (this.errored[event.name]) {
							this.pending.remove(event.name);
							delete this.errored[event.name];
							this.logReports();
						}
					} else {
						if (!this.errored[event.name]) {
							this.pending.push(event.name);
						}
						this.errored[event.name] = event.report;
						this.logReports();
					}
				}.bind(this));
				if (!this.data.resolved) {
					waiting.push(promise);
				}
			} else if (this.errored[event.name]) {
				this.pending.remove(event.name);
				delete this.errored[event.name];
				this.logReports();
			}
			this.logStatus();
		}.bind(this));

		return this.data(function (data) {
			if (this.options.stream) {
				return deferred.map(waiting);
			} else {
				return deferred.map(data, function (report, name) {
					this.add(reports[name] = report);
					if (report.length) {
						return this.normalize(report)(function (nreport) {
							if (!nreport) {
								this.subtract(report);
								delete reports[name];
								return;
							}
							this.logReports();
						}.bind(this));
					}
					this.logStatus();
				}.bind(this));
			}
		}.bind(this))(this.data);
	}),
	redraw: d(function () {
		var dist, columns, count, str, redrawAll, index;

		// Calculate general distribution, e.g. [5, 5, 6]
		dist = distColumns(this.maxColumns, this.pending.length, this.maxPerColumn);
		columns = [];

		index = 0;
		this.pending.sort();

		// Distribute errors
		dist.forEach(function (count) {
			var avgErrCount, cells, name, report, length;
			if (this.pending[index] == null) {
				return;
			}

			columns.push(cells = []);
			avgErrCount = floor((floor(this.columnHeight / count) - 4) / 2);
			while (count-- && (this.pending[index] != null)) {
				name = this.pending[index++];
				report = this.errored[name];
				length = min(avgErrCount, report.length);
				cells.push({ name: name, report: report, length: length,
					height: 2 + length * 2 + ((report.length > length) ? 2 : 1) });
			}
		}, this);

		// improve distribution within columns
		columns.forEach(function (cells, index) {
			var height, diff, cell, column;
			height = cells.reduce(function (sum, cell) {
				return sum + cell.height;
			}, 0);

			diff = this.columnHeight - height;

			// Expand reports there's space left and they're not fully exposed
			while ((diff > 1) && !areReportsFullyExposed(cells)) {
				cells.some(function (cell) {
					if (cell.report.length > cell.length) {
						++cell.length;
						if (cell.report.length === cell.length) {
							++cell.height;
							--diff;
						} else {
							cell.height += 2;
							diff -= 2;
						}
					}
					return (diff < 2);
				});
			}

			// Move reports from next column to this one if that makes sense
			++index;
			while ((diff > 3) && columns[index]) {
				while (columns[index] && (columns[index][0].height < diff)) {
					// Move cell
					cell = columns[index].shift();
					if (!columns[index].length) {
						// No more reports in next column, remove it
						columns.splice(index, 1);
					}
					cells.push(cell);
					diff -= cell.height;
					while ((diff > 1) && (cell.report.length > cell.length)) {
						// Expand not fully exposed report
						++cell.length;
						if (cell.report.length === cell.length) {
							++cell.height;
							--diff;
						} else {
							cell.height += 2;
							diff -= 2;
						}
					}
				}
			}
		}, this);

		if (!this.current || (this.current.length !== columns.length)) {
			redrawAll = true;
			this.columnWidth = floor(this.totalWidth / columns.length);
			if (columns.length > 1) {
				// Draw columns
				for (count = 1; count < columns.length; ++count) {
					this.log(clc.set(count * this.columnWidth - 1, 3));
					this.log(this.emap.bar(' ') + repeat.call(clc.move(-1, 1)
						+ this.emap.bar(' '), this.columnHeight - 1));
				}
			}
		}

		columns.forEach(function (column, index) {
			var top = 0, prevTop = 0
			  , prevColumn = this.current && this.current[index]
			  , width = ((columns.length - 1) === index) ?
					(this.totalWidth - this.columnWidth * index) : (this.columnWidth - 1)
			  , left = this.columnWidth * index;
			column.forEach(function (cell, index) {
				var previous = prevColumn && prevColumn[index];
				if (redrawAll || !previous || (previous.report !== cell.report) ||
						(previous.height !== cell.height) || (top !== prevTop)) {
					// Draw report
					this.logFile(cell.name, cell.report, cell.length,
						{ width: width, top: top + 3, left: left });
				}
				top += cell.height;
				prevTop += cell.height;
			}, this);
		}, this);
	}),
	logReports: d(memoize(function () {
		nextTick(function () {
			this.logReports.clear();
			this.redraw();
			this.log(clc.set(0, this.totalHeight));
		}.bind(this));
	}, { method: 'logReports' })),
	logStatus: d(memoize(function () {
		nextTick(function () {
			this.logStatus.clear();
			this.log(clc.set(0, 0));
			Base.prototype.logStatus.call(this);
			this.log(clc.set(0, this.totalHeight));
		}.bind(this));
	}, { method: 'logStatus' }))
});

module.exports = exports = function (report/*, options*/) {
	deferred.validPromise(report);
	return (new Reporter(report, Object(arguments[1]))).init();
};
exports.returnsPromise = true;
exports.Reporter = Reporter;
<commit_msg>Fix distribution algorithm<commit_after>'use strict';

var remove   = require('es5-ext/lib/Array/prototype/remove')
  , d        = require('es5-ext/lib/Object/descriptor')
  , repeat   = require('es5-ext/lib/String/prototype/repeat')
  , memoize  = require('memoizee')
  , deferred = require('deferred')
  , clc      = require('cli-color')
  , Base     = require('./console').Reporter

  , floor = Math.floor, max = Math.max, min = Math.min
  , now = Date.now
  , nextTick = process.nextTick
  , log = process.stdout.write.bind(process.stdout)

  , Reporter, distColumns, areReportsFullyExposed;

// Distributes reports per column e.g. [6, 6, 7], [2, 3, 3, 3]
distColumns = function (maxColumns, length, maxReports) {
	var base, rest, result, index;
	result = [];

	base = floor(length / maxColumns);

	if (!base) {
		while (length--) {
			result.push(1);
		}
		return result;
	} else if (base >= maxReports) {
		base = maxReports;
		rest = 0;
	} else {
		rest = length - base * maxColumns;
	}

	while (maxColumns--) {
		result.push(base);
	}
	if (rest) {
		index = result.length - 1;
		while (rest--) {
			result[index] = result[index] + 1;
			--index;
		}
	}
	return result;
};

areReportsFullyExposed = function (cells) {
	return cells.every(function (cell) {
		return cell.report.length === cell.length;
	});
};

Reporter = function () {
	Base.apply(this, arguments);

	this.totalWidth = clc.width;
	this.totalHeight = clc.height;
	this.pending = [];
	this.pending.remove = remove;
	this.errored = {};
};
Reporter.prototype = Object.create(Base.prototype, {
	init: d(function () {
		var reports = {}, updateMap = {}, waiting = [];

		this.data.on('change', function (event) {
			if (event.type === 'remove') {
				this.subtract(reports[event.name]);
				delete reports[event.name];
				return;
			} else if (event.type === 'update') {
				this.subtract(reports[event.name]);
			}
			this.add(reports[event.name] = event.report);
		}.bind(this));

		// Reset
		this.log(clc.reset);

		// Bar
		this.log(clc.set(0, 2) + this.emap.bar(repeat.call('-', this.totalWidth)));

		// Status
		this.logStatus();

		// Columns
		this.maxColumns = max(floor(this.totalWidth / 90), 1);
		this.columnHeight = this.totalHeight - 3;
		this.maxPerColumn = floor(this.columnHeight / 6);

		this.data.on('change', function (event) {
			var promise, time;
			time = updateMap[event.name] = now();
			if (event.report && event.report.length) {
				promise = this.normalize(event.report)(function (report) {
					if (time !== updateMap[event.name]) {
						// Race condition, abort
						return;
					}
					if (!report) {
						this.subtract(event.report);
						this.logStatus();
						if (this.errored[event.name]) {
							this.pending.remove(event.name);
							delete this.errored[event.name];
							this.logReports();
						}
					} else {
						if (!this.errored[event.name]) {
							this.pending.push(event.name);
						}
						this.errored[event.name] = event.report;
						this.logReports();
					}
				}.bind(this));
				if (!this.data.resolved) {
					waiting.push(promise);
				}
			} else if (this.errored[event.name]) {
				this.pending.remove(event.name);
				delete this.errored[event.name];
				this.logReports();
			}
			this.logStatus();
		}.bind(this));

		return this.data(function (data) {
			if (this.options.stream) {
				return deferred.map(waiting);
			} else {
				return deferred.map(data, function (report, name) {
					this.add(reports[name] = report);
					if (report.length) {
						return this.normalize(report)(function (nreport) {
							if (!nreport) {
								this.subtract(report);
								delete reports[name];
								return;
							}
							this.logReports();
						}.bind(this));
					}
					this.logStatus();
				}.bind(this));
			}
		}.bind(this))(this.data);
	}),
	redraw: d(function () {
		var dist, columns, count, str, redrawAll, index;

		// Calculate general distribution, e.g. [5, 5, 6]
		dist = distColumns(this.maxColumns, this.pending.length, this.maxPerColumn);
		columns = [];

		index = 0;
		this.pending.sort();

		// Distribute errors
		dist.forEach(function (count) {
			var avgErrCount, cells, name, report, length;
			if (this.pending[index] == null) {
				return;
			}

			columns.push(cells = []);
			avgErrCount = floor((floor(this.columnHeight / count) - 4) / 2);
			while (count-- && (this.pending[index] != null)) {
				name = this.pending[index++];
				report = this.errored[name];
				length = min(avgErrCount, report.length);
				cells.push({ name: name, report: report, length: length,
					height: 2 + length * 2 + ((report.length > length) ? 2 : 1) });
			}
		}, this);

		// improve distribution within columns
		columns.forEach(function (cells, index) {
			var height, diff, cell, column;
			height = cells.reduce(function (sum, cell) {
				return sum + cell.height;
			}, 0);

			diff = this.columnHeight - height;

			// Expand reports there's space left and they're not fully exposed
			while ((diff > 1) && !areReportsFullyExposed(cells)) {
				cells.some(function (cell) {
					if (cell.report.length > cell.length) {
						++cell.length;
						if (cell.report.length === cell.length) {
							++cell.height;
							--diff;
						} else {
							cell.height += 2;
							diff -= 2;
						}
					}
					return (diff < 2);
				});
			}

			// Move reports from next column to this one if that makes sense
			++index;
			while ((diff > 3) && columns[index]) {
				while (columns[index] && (columns[index][0].height < diff)) {
					// Move cell
					cell = columns[index].shift();
					if (!columns[index].length) {
						// No more reports in next column, remove it
						columns.splice(index, 1);
					}
					cells.push(cell);
					diff -= cell.height;
					while ((diff > 1) && (cell.report.length > cell.length)) {
						// Expand not fully exposed report
						++cell.length;
						if (cell.report.length === cell.length) {
							++cell.height;
							--diff;
						} else {
							cell.height += 2;
							diff -= 2;
						}
					}
				}
			}
		}, this);

		if (!this.current || (this.current.length !== columns.length)) {
			redrawAll = true;
			this.columnWidth = floor(this.totalWidth / columns.length);
			if (columns.length > 1) {
				// Draw columns
				for (count = 1; count < columns.length; ++count) {
					this.log(clc.set(count * this.columnWidth - 1, 3));
					this.log(this.emap.bar(' ') + repeat.call(clc.move(-1, 1)
						+ this.emap.bar(' '), this.columnHeight - 1));
				}
			}
		}

		columns.forEach(function (column, index) {
			var top = 0, prevTop = 0
			  , prevColumn = this.current && this.current[index]
			  , width = ((columns.length - 1) === index) ?
					(this.totalWidth - this.columnWidth * index) : (this.columnWidth - 1)
			  , left = this.columnWidth * index;
			column.forEach(function (cell, index) {
				var previous = prevColumn && prevColumn[index];
				if (redrawAll || !previous || (previous.report !== cell.report) ||
						(previous.height !== cell.height) || (top !== prevTop)) {
					// Draw report
					this.logFile(cell.name, cell.report, cell.length,
						{ width: width, top: top + 3, left: left });
				}
				top += cell.height;
				prevTop += cell.height;
			}, this);
		}, this);
	}),
	logReports: d(memoize(function () {
		nextTick(function () {
			this.logReports.clear();
			this.redraw();
			this.log(clc.set(0, this.totalHeight));
		}.bind(this));
	}, { method: 'logReports' })),
	logStatus: d(memoize(function () {
		nextTick(function () {
			this.logStatus.clear();
			this.log(clc.set(0, 0));
			Base.prototype.logStatus.call(this);
			this.log(clc.set(0, this.totalHeight));
		}.bind(this));
	}, { method: 'logStatus' }))
});

module.exports = exports = function (report/*, options*/) {
	deferred.validPromise(report);
	return (new Reporter(report, Object(arguments[1]))).init();
};
exports.returnsPromise = true;
exports.Reporter = Reporter;
<|endoftext|>


<commit_before>/*global $*/
var defaultUrl = 'https://mjsalerno.github.io';
var defaultData = 'scott, shane, michael, mike, paul';

function showResults(response) {
    $('#results').html(response);
}

function leakFailed(xhr) {
    if (xhr.responseText) {
        showResults(xhr.responseText);
    }
    else {
        showResults("Probably ERR_BLOCKED_BY_CLIENT (meaning StopLeak!)");
        console.log(xhr);
    }
}

function leakData(method) {
    var url = $('#leaky-url').val();
    var data = $('#leaky-text').val();

    if (!url) {
        url = defaultUrl;
    }

    if (!data) {
        data = defaultData;
    }

    console.log('Trying to leak to: ' + url);
    console.log('Trying to leak: ' + data);

    var req = $.ajax({
        method: method,
        url: url,
        data: data
    });

    req.done(showResults);
    req.fail(leakFailed);
}

function leakByGet() {
    leakData('GET');
}

function leakByPost() {
    leakData('POST');
}

function addHandlers() {
    $('#leaky-get-button').bind('click', leakByGet);
    $('#leaky-post-button').bind('click', leakByPost);
    $('#leaky-url').text(defaultUrl);
    $('#leaky-text').attr('placeholder', defaultData);
}

$(document).ready(addHandlers);
<commit_msg>fixed syntax<commit_after>/*global $*/
var defaultUrl = 'https://mjsalerno.github.io';
var defaultData = 'scott, shane, michael, mike, paul';

function showResults(response) {
    $('#results').html(response);
}

function leakFailed(xhr) {
    if (xhr.responseText) {
        showResults(xhr.responseText);
    }
    else {
        showResults('Probably ERR_BLOCKED_BY_CLIENT (meaning StopLeak!)');
        console.log(xhr);
    }
}

function leakData(method) {
    var url = $('#leaky-url').val();
    var data = $('#leaky-text').val();

    if (!url) {
        url = defaultUrl;
    }

    if (!data) {
        data = defaultData;
    }

    console.log('Trying to leak to: ' + url);
    console.log('Trying to leak: ' + data);

    var req = $.ajax({
        method: method,
        url: url,
        data: data
    });

    req.done(showResults);
    req.fail(leakFailed);
}

function leakByGet() {
    leakData('GET');
}

function leakByPost() {
    leakData('POST');
}

function addHandlers() {
    $('#leaky-get-button').bind('click', leakByGet);
    $('#leaky-post-button').bind('click', leakByPost);
    $('#leaky-url').text(defaultUrl);
    $('#leaky-text').attr('placeholder', defaultData);
}

$(document).ready(addHandlers);
<|endoftext|>


<commit_before>var makeTap = require('../index')
var fs = require('fs')
var assert = require('assert')

var testOutput = fs.readFileSync(require('path').join(__dirname, 'test1.tap'), 'utf8')
var buff = ''

var testErr = {
  name: 'Error',
  message: 'foobar',
  stack: 'Error: foobar\n    line1\n    line2'
}

var testAssertionError = {
  name: 'AssertionError',
  message: 'foomessage',
  operator: '==',
  expected: 'five',
  actual: 'four',
  stack: 'AssertionError: foomessage\n    line1\n    line2'
}

function testTapPipe (tap) {
  tap
  .on('data', function (d) {
    buff += d
  })
}

var testTap = makeTap()

testTap.writeln('# foo')

testTap.diag('bar')

testTap.plan(6)

testTap.pass()
testTap.pass('foo')
testTap.pass('bar', 'thing')

testTap.fail(testErr)
testTap.fail('foo', testErr)
testTap.fail('bar', 'thing', testAssertionError)
testTap.fail({name: 'not a real error'})

testTap.bail()
testTap.bail('This is a bail reason')

testTap.yaml({
  some: {
    data: {
      foo: 'bar'
    }
  },
  stuff: 1
})

testTap.end()

var testTap2 = makeTap({count: false})

testTap2.pass()
testTap2.pass('foo')

testTap2.end()

testTapPipe(testTap)
testTapPipe(testTap2)
testTap2.on('end', function () {
  process.nextTick(function () {
    require('print-diff')(testOutput, buff)
    assert.equal(testOutput, buff)
    console.log('# success!')
  })
})
<commit_msg>self-tap<commit_after>var makeTap = require('../index')
var fs = require('fs')
var assert = require('assert')

var testOutput = fs.readFileSync(require('path').join(__dirname, 'test1.tap'), 'utf8')
var buff = ''
var exitCode = 0

var testErr = {
  name: 'Error',
  message: 'foobar',
  stack: 'Error: foobar\n    line1\n    line2'
}

var testAssertionError = {
  name: 'AssertionError',
  message: 'foomessage',
  operator: '==',
  expected: 'five',
  actual: 'four',
  stack: 'AssertionError: foomessage\n    line1\n    line2'
}

var testTap = makeTap()

testTap.writeln('# foo')

testTap.diag('bar')

testTap.plan(6)

testTap.pass()
testTap.pass('foo')
testTap.pass('bar', 'thing')

testTap.fail(testErr)
testTap.fail('foo', testErr)
testTap.fail('bar', 'thing', testAssertionError)
testTap.fail({name: 'not a real error'})

testTap.bail()
testTap.bail('This is a bail reason')

testTap.yaml({
  some: {
    data: {
      foo: 'bar'
    }
  },
  stuff: 1
})

testTap.end()

var testTap2 = makeTap({count: false})

testTap2.pass()
testTap2.pass('foo')

testTap2.end()

testTapPipe(testTap)
testTapPipe(testTap2)
testTap2.on('end', function () {
  process.nextTick(function () {
    var mainTap = makeTap()
    testOutput = testOutput.split('\n')
    buff = buff.split('\n')
    mainTap.plan(testOutput.length)
    assertEqual(mainTap, testOutput.length, buff.length)
    testOutput.forEach(function (line, i) {
      assertEqual(mainTap, line, buff[i])
    })
    mainTap.end()
    mainTap.pipe(process.stdout)
    process.nextTick(function () {
      process.exit(exitCode)
    })
  })
})

function testTapPipe (tap) {
  tap
  .on('data', function (d) {
    buff += d
  })
}

function assertEqual (tap, x, y) {
  var name = '"' + x + '" === "' + y + '"'
  try {
    assert.equal(x, y)
    tap.pass(name)
  } catch (e) {
    tap.fail(name, e)
    exitCode = 1
  }
}
<|endoftext|>


<commit_before>module.exports = function (grunt) {
  'use strict';

  var srcDir = 'src/';
  var testDir = 'tests/';
  var testSrcDir = testDir + srcDir;
  var providerDir = 'plugins/provider/';
  var outputFile = 'dist/jsVideoUrlParser.js';
  var minOutputFile = 'dist/jsVideoUrlParser.min.js';
  var testOutputFile = testDir + 'test.js';
  var src = [srcDir + 'urlParser.js', srcDir + 'plugins/*.js'];
  var test = [testSrcDir + '*.js', testSrcDir + 'plugins/*.js'];
  var provider = {
    '--dailymotion': ['Dailymotion.js'],
    '--youtube': ['YouTube.js'],
    '--vimeo': ['Vimeo.js'],
    '--twitch': ['Twitch.js'],
    '--canalplus': ['CanalPlus.js'],
    '--youku': ['Youku.js'],
    '--coub': ['Coub.js'],
    '--template': ['Template.js']
  };
  grunt.option.flags().forEach(function (flag) {
    flag = flag.toLowerCase();
    grunt.log.writeln(flag);
    if (provider.hasOwnProperty(flag)) {
      src.push(srcDir + providerDir + provider[flag]);
      test.push(testSrcDir + '' + providerDir + provider[flag]);
    }
  });
  //add all files when no flags were found
  if (src.length === 2) {
    src.push(srcDir + providerDir + '*.js');
    test.push(testSrcDir + providerDir + '*.js');
    //ignore template files
    src.push('!' + srcDir + providerDir + 'Template.js');
    test.push('!' + testSrcDir + providerDir + 'Template.js');
  }

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      dist: {
        src: src,
        dest: outputFile,
      },
      test: {
        src: test,
        dest: testOutputFile,
      }
    },
    uglify: {
      build: {
        src: outputFile,
        dest: minOutputFile
      }
    },
    jshint: {
      options: {
        jshintrc: '.jshintrc',
      },
      beforeconcat: src.concat(test),
      afterconcat: [outputFile, testOutputFile],
      other: ['Gruntfile.js']
    },
    qunit: {
      all: [testDir + 'index.html']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask('default', ['concat', 'jshint', 'qunit', 'uglify']);
  grunt.registerTask('dist', ['concat:dist', 'uglify']);
  grunt.registerTask('test', ['jshint', 'qunit']);
  grunt.registerTask('build', ['concat']);
};
<commit_msg>Don't print the flag<commit_after>module.exports = function (grunt) {
  'use strict';

  var srcDir = 'src/';
  var testDir = 'tests/';
  var testSrcDir = testDir + srcDir;
  var providerDir = 'plugins/provider/';
  var outputFile = 'dist/jsVideoUrlParser.js';
  var minOutputFile = 'dist/jsVideoUrlParser.min.js';
  var testOutputFile = testDir + 'test.js';
  var src = [srcDir + 'urlParser.js', srcDir + 'plugins/*.js'];
  var test = [testSrcDir + '*.js', testSrcDir + 'plugins/*.js'];
  var provider = {
    '--dailymotion': ['Dailymotion.js'],
    '--youtube': ['YouTube.js'],
    '--vimeo': ['Vimeo.js'],
    '--twitch': ['Twitch.js'],
    '--canalplus': ['CanalPlus.js'],
    '--youku': ['Youku.js'],
    '--coub': ['Coub.js'],
    '--template': ['Template.js']
  };
  grunt.option.flags().forEach(function (flag) {
    flag = flag.toLowerCase();
    if (provider.hasOwnProperty(flag)) {
      src.push(srcDir + providerDir + provider[flag]);
      test.push(testSrcDir + '' + providerDir + provider[flag]);
    }
  });
  //add all files when no flags were found
  if (src.length === 2) {
    src.push(srcDir + providerDir + '*.js');
    test.push(testSrcDir + providerDir + '*.js');
    //ignore template files
    src.push('!' + srcDir + providerDir + 'Template.js');
    test.push('!' + testSrcDir + providerDir + 'Template.js');
  }

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      dist: {
        src: src,
        dest: outputFile,
      },
      test: {
        src: test,
        dest: testOutputFile,
      }
    },
    uglify: {
      build: {
        src: outputFile,
        dest: minOutputFile
      }
    },
    jshint: {
      options: {
        jshintrc: '.jshintrc',
      },
      beforeconcat: src.concat(test),
      afterconcat: [outputFile, testOutputFile],
      other: ['Gruntfile.js']
    },
    qunit: {
      all: [testDir + 'index.html']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask('default', ['concat', 'jshint', 'qunit', 'uglify']);
  grunt.registerTask('dist', ['concat:dist', 'uglify']);
  grunt.registerTask('test', ['jshint', 'qunit']);
  grunt.registerTask('build', ['concat']);
};
<|endoftext|>


<commit_before>var vows = require('vows'),
	assert = require('assert');

var Todoist = require('../todoist'),
	macros = require('../lib/macros');

vows.describe('Todoist').addBatch({
	'The Todoist API': {
		'when instantiated': {
			topic: new Todoist(process.env.TODOIST_EMAIL, process.env.TODOIST_PASS, function (err,resp,data) {
				return;
			}),

			'returns a Todoist' : function (topic) {
				assert.instanceOf (topic, Todoist);
			},
			'has a user property': function (topic) {
				assert.equal(topic.hasOwnProperty('user'), true);
			},
			'has a _getIt property': function (topic) {
				assert.equal(topic.hasOwnProperty('user'), true);
			},
			'and it\'s a function': function (topic) {
				assert.instanceOf (topic._getIt, Function);
			}
		},
		'when properly connected': {
			topic: function(){
				new Todoist(process.env.TODOIST_EMAIL, process.env.TODOIST_PASS, this.callback);
			},

			'doesnt throw an error': function (err, resp, data) {
				assert.isNull(err);
			},
			'responds with a 200 OK response': macros.assertStatusCode(200),
			'responds with a data object': macros.assertDataReceived(),
			'responds with an API token': macros.assertDataHasProperty('token'),
			'and it is valid': function (err, resp, data) {
				assert.equal(data.token === process.env.TODOIST_TOKEN, true);
			}
		},
		'when connecting with an invalid email/password': {
			topic: function () {
				new Todoist('bademail', 'badpass', this.callback);
			},
			'responds with a 200 OK': macros.assertStatusCode(200),
			'responds with a \'LOGIN_ERROR\'': function (err, resp, data) {
				assert.deepEqual(data, 'LOGIN_ERROR');
			}
		},
		'when an email or password isn\'t passed': {
			topic: function () {
				new Todoist('', '', this.callback);
			},
			'errors out': function (err, resp, data){
				assert.deepEqual(err, 'Must call Todoist with an email and password');
			}
		}
	}
}).export(module);
<commit_msg>check for api method, not _getIt<commit_after>var vows = require('vows'),
	assert = require('assert');

var Todoist = require('../todoist'),
	macros = require('../lib/macros');

vows.describe('Todoist').addBatch({
	'The Todoist API': {
		'when instantiated': {
			topic: new Todoist(process.env.TODOIST_EMAIL, process.env.TODOIST_PASS, function (err,resp,data) {
				return;
			}),

			'returns a Todoist' : function (topic) {
				assert.instanceOf (topic, Todoist);
			},
			'has a user property': function (topic) {
				assert.equal(topic.hasOwnProperty('user'), true);
			},
			'has an api property': function (topic) {
				assert.equal(topic.hasOwnProperty('api'), true);
			},
			'and it\'s a function': function (topic) {
				assert.instanceOf (topic.api, Function);
			}
		},
		'when properly connected': {
			topic: function(){
				new Todoist(process.env.TODOIST_EMAIL, process.env.TODOIST_PASS, this.callback);
			},

			'doesnt throw an error': function (err, resp, data) {
				assert.isNull(err);
			},
			'responds with a 200 OK response': macros.assertStatusCode(200),
			'responds with a data object': macros.assertDataReceived(),
			'responds with an API token': macros.assertDataHasProperty('token'),
			'and it is valid': function (err, resp, data) {
				assert.equal(data.token === process.env.TODOIST_TOKEN, true);
			}
		},
		'when connecting with an invalid email/password': {
			topic: function () {
				new Todoist('bademail', 'badpass', this.callback);
			},
			'responds with a 200 OK': macros.assertStatusCode(200),
			'responds with a \'LOGIN_ERROR\'': function (err, resp, data) {
				assert.deepEqual(data, 'LOGIN_ERROR');
			}
		},
		'when an email or password isn\'t passed': {
			topic: function () {
				new Todoist('', '', this.callback);
			},
			'errors out': function (err, resp, data){
				assert.deepEqual(err, 'Must call Todoist with an email and password');
			}
		}
	}
}).export(module);
<|endoftext|>


<commit_before>import "regenerator-runtime/runtime"; // needed for ``await`` support
import $ from "jquery";
import _ from "underscore";
import _t from "@patternslib/patternslib/src/core/i18n";
import Base from "@patternslib/patternslib/src/core/base";
import logging from "@patternslib/patternslib/src/core/logging";
import Parser from "@patternslib/patternslib/src/core/parser";

const log = logging.getLogger("pat-upload");

export const parser = new Parser("upload");
parser.add_argument("concurrent-uploads", "multiple", ["multiple", "single"]); // Only one upload at a time, or multiple ones simultaneously?
parser.add_argument("ajax-upload", true); //boolean: true or false for letting the widget upload the files via ajax. If false the form will act like a normal form. (true)
parser.add_argument("post-upload", true); //boolean: condition value for the file preview in div element to fadeout after file upload is completed. (true)
parser.add_argument("class-name", "upload"); //string: value for class attribute in the form element ('upload')
parser.add_argument("clickable", "true"); //boolean: If you can click on container to also upload (true)
parser.add_argument("current-path"); //string: Current path related items is starting with (null)
parser.add_argument("label", "Drop files here…"); //string: Text to show instead of the default ('Drop files here…')
parser.add_argument("name", "file"); //string: value for name attribute in the file input element ('file')
parser.add_argument("previews-container", ".previews"); //selector: JavaScript selector for file preview in div element. (.previews)
parser.add_argument("timeout", 120000);
parser.add_argument("trigger", "button"); //string: What triggers the upload.  'button' expects user to click upload button, 'auto' starts uploading automatically after the user drags something, and always hides the upload button. ('button')
parser.add_argument("url"); //string: If not used with a form, this option must provide the URL to submit to (null)

export default Base.extend({
    name: "upload",
    trigger: ".pat-upload",
    parser: "patternslib",
    defaults: {
        addRemoveLinks: false,
        maxFiles: null,
        maxFilesize: 99999999, // let's not have a max by default...
        previewTemplate: null,
    },
    template_preview: null,

    async init($el, opts) {
        if (window.__patternslib_import_styles) {
            import("dropzone/dist/basic.css");
        }

        const Dropzone = (await import("dropzone")).default;
        Dropzone.autoDiscover = false; // we do not want this plugin to auto discover
        this.template_preview = (await import("./templates/preview.html")).default;
        const template_upload = (await import("./templates/upload.html")).default;

        this.cfgs = _.extend(_.clone(this.defaults), parser.parse($el, opts, true)[0]);
        this.$el.addClass(this.cfgs.className);
        this.$el.append(
            _.template(template_upload)({ _t: _t, label: _t(this.cfgs.label) })
        );

        if (!this.cfgs.ajaxUpload) {
            // no ajax upload, drop the fallback
            $(".fallback", this.$el).remove();
        }
        try {
            // if init of Dropzone fails it says nothing and
            // it fails silently. Using this block we make sure
            // that if you break it w/ some weird or missing option
            // you can get a proper log of it
            this.dropzone = new Dropzone(
                $(".upload-area", this.$el)[0],
                this.getDzoneOptions()
            );
        } catch (e) {
            log.error(e);
            throw e;
        }
        this.registerHandlers();
    },

    refresh() {
        var $form = this.$el.closest("form");
        if ($form.hasClass("pat-inject")) {
            $form.submit();
        }
    },

    registerHandlers() {
        this.dropzone.on(
            "complete",
            $.proxy(function (file) {
                if (this.cfgs.postUpload) {
                    setTimeout(function () {
                        $(file.previewElement).fadeOut();
                    }, 1000);
                }
                if (
                    _.filter(this.dropzone.files, function (f) {
                        return f.status !== "success";
                    }).length === 0
                ) {
                    this.refresh();
                }
            }, this)
        );
    },

    getUrl() {
        var $form;
        var url = this.cfgs.url;
        if (!url) {
            $form = this.$el.closest("form");
            url = $form.length > 0 ? $form.attr("action") : window.location.href;
        }
        return url;
    },

    getDzoneOptions() {
        if (typeof this.cfgs.clickable === "string") {
            this.cfgs.clickable = this.cfgs.clickable === "true" ? true : false;
        }
        var options = $.extend({}, this.cfgs);
        delete options.postUpload;
        if (this.cfgs.previewsContainer) {
            /* If they have a select but it's not an id, let's make an id selector
             * so we can target the correct container. dropzone is weird here...
             */
            var $preview = this.$el.find(this.cfgs.previewsContainer);
            if ($preview.length > 0) {
                options.previewsContainer = $preview[0];
            }
        }
        options.url = this.getUrl();
        options.uploadMultiple = options.concurrentUploads === "multiple" ? true : false;
        options.previewTemplate = this.template_preview;
        // if our element is a form we should force some values
        // https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone
        return options;
    },
});
<commit_msg>maint: Modernize code.<commit_after>import "regenerator-runtime/runtime"; // needed for ``await`` support
import $ from "jquery";
import _ from "underscore";
import _t from "@patternslib/patternslib/src/core/i18n";
import Base from "@patternslib/patternslib/src/core/base";
import logging from "@patternslib/patternslib/src/core/logging";
import Parser from "@patternslib/patternslib/src/core/parser";

const log = logging.getLogger("pat-upload");

export const parser = new Parser("upload");
parser.add_argument("concurrent-uploads", "multiple", ["multiple", "single"]); // Only one upload at a time, or multiple ones simultaneously?
parser.add_argument("ajax-upload", true); //boolean: true or false for letting the widget upload the files via ajax. If false the form will act like a normal form. (true)
parser.add_argument("post-upload", true); //boolean: condition value for the file preview in div element to fadeout after file upload is completed. (true)
parser.add_argument("class-name", "upload"); //string: value for class attribute in the form element ('upload')
parser.add_argument("clickable", "true"); //boolean: If you can click on container to also upload (true)
parser.add_argument("current-path"); //string: Current path related items is starting with (null)
parser.add_argument("label", "Drop files here…"); //string: Text to show instead of the default ('Drop files here…')
parser.add_argument("name", "file"); //string: value for name attribute in the file input element ('file')
parser.add_argument("previews-container", ".previews"); //selector: JavaScript selector for file preview in div element. (.previews)
parser.add_argument("timeout", 120000);
parser.add_argument("trigger", "button"); //string: What triggers the upload.  'button' expects user to click upload button, 'auto' starts uploading automatically after the user drags something, and always hides the upload button. ('button')
parser.add_argument("url"); //string: If not used with a form, this option must provide the URL to submit to (null)

export default Base.extend({
    name: "upload",
    trigger: ".pat-upload",
    parser: "patternslib",
    defaults: {
        addRemoveLinks: false,
        maxFiles: null,
        maxFilesize: 99999999, // let's not have a max by default...
        previewTemplate: null,
    },
    template_preview: null,

    async init($el, opts) {
        if (window.__patternslib_import_styles) {
            import("dropzone/dist/basic.css");
        }

        const Dropzone = (await import("dropzone")).default;
        Dropzone.autoDiscover = false; // we do not want this plugin to auto discover
        this.template_preview = (await import("./templates/preview.html")).default;
        const template_upload = (await import("./templates/upload.html")).default;

        this.cfgs = _.extend(_.clone(this.defaults), parser.parse($el, opts, true)[0]);
        this.$el.addClass(this.cfgs.className);
        this.$el.append(
            _.template(template_upload)({ _t: _t, label: _t(this.cfgs.label) })
        );

        if (!this.cfgs.ajaxUpload) {
            // no ajax upload, drop the fallback
            $(".fallback", this.$el).remove();
        }
        try {
            // if init of Dropzone fails it says nothing and
            // it fails silently. Using this block we make sure
            // that if you break it w/ some weird or missing option
            // you can get a proper log of it
            this.dropzone = new Dropzone(
                $(".upload-area", this.$el)[0],
                this.getDzoneOptions()
            );
        } catch (e) {
            log.error(e);
            throw e;
        }
        this.registerHandlers();
    },

    refresh() {
        const $form = this.$el.closest("form");
        if ($form.hasClass("pat-inject")) {
            $form.submit();
        }
    },

    registerHandlers() {
        this.dropzone.on(
            "complete",
            $.proxy(function (file) {
                if (this.cfgs.postUpload) {
                    setTimeout(function () {
                        $(file.previewElement).fadeOut();
                    }, 1000);
                }
                if (
                    _.filter(this.dropzone.files, function (f) {
                        return f.status !== "success";
                    }).length === 0
                ) {
                    this.refresh();
                }
            }, this)
        );
    },

    getUrl() {
        let url = this.cfgs.url;
        if (!url) {
            const $form = this.$el.closest("form");
            url = $form.length > 0 ? $form.attr("action") : window.location.href;
        }
        return url;
    },

    getDzoneOptions() {
        if (typeof this.cfgs.clickable === "string") {
            this.cfgs.clickable = this.cfgs.clickable === "true" ? true : false;
        }
        const options = $.extend({}, this.cfgs);
        delete options.postUpload;
        if (this.cfgs.previewsContainer) {
            /* If they have a select but it's not an id, let's make an id selector
             * so we can target the correct container. dropzone is weird here...
             */
            const $preview = this.$el.find(this.cfgs.previewsContainer);
            if ($preview.length > 0) {
                options.previewsContainer = $preview[0];
            }
        }
        options.url = this.getUrl();
        options.uploadMultiple = options.concurrentUploads === "multiple" ? true : false;
        options.previewTemplate = this.template_preview;
        // if our element is a form we should force some values
        // https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone
        return options;
    },
});
<|endoftext|>


<commit_before>define([
    'jquery',
    'd3',
    'base/component'
], function($, d3, Component) {

    function order(a, b) {
        return radius(b) - radius(a);
    }

    function radius(d, indicator) {
        return d.pop;
    }

    var BubbleChart = Component.extend({
        init: function(context, options) {
            var _this = this;
            this.name = 'bubble-chart';
            this.template = 'components/_examples/' + this.name + '/' + this.name;

            //define expected models for this component
            this.model_expects = ["time", "entities", "marker", "data"];

            this._super(context, options);

            this.xScale = null;
            this.yScale = null;
            this.rScale = null;
            this.cScale = d3.scale.category10();

            this.xAxis = d3.svg.axis();
            this.yAxis = d3.svg.axis();

            this.isDataPreprocessed = false;
            this.timeUpdatedOnce = false;
            this.sizeUpdatedOnce = false;

        },

        /**
         * Executes right after the template is in place
         */
        domReady: function() {
            var _this = this;

            // reference elements
            this.graph = this.element.select('.vzb-bc-graph');
            this.yAxisEl = this.graph.select('.vzb-bc-axis-y');
            this.xAxisEl = this.graph.select('.vzb-bc-axis-x');
            this.yTitleEl = this.graph.select('.vzb-bc-axis-y-title');
            this.xTitleEl = this.graph.select('.vzb-bc-axis-x-title');
            this.yearEl = this.graph.select('.vzb-bc-year');
            this.bubbleContainer = this.graph.select('.vzb-bc-bubbles');
            this.bubbles = null;
            this.tooltip = this.element.select('.vzb-tooltip');

            //model events
            this.model.on({
                "change": function(evt) {
                    console.log("Changed!", evt);
                },
                "load_start": function(evt) {
                    console.log("Started to load!", evt);
                },
                "load_end":  function() {
                    console.log("Finished loading!");
                    _this.updateShow();
                    _this.redrawDataPoints();
                },
                "ready": function() {
                    console.log("Model ready!");
//TODO: put here the following and remove it from "load_end" and from redrawDataPoints()
//                    _this.preprocessData();
//                    _this.updateShow();
//                    _this.updateTime();
//                    _this.redrawDataPoints();
                }
            });

            this.model.time.on({
                'change:value': function() {
                    _this.updateTime();
                    _this.redrawDataPoints();
                }
            });

            //component events
            this.on("resize", function() {
                console.log("Ops! Gotta resize...");
                _this.updateSize();
                _this.redrawDataPoints();
            })

        },

        preprocessData: function(){
            this.model.marker.label.getItems().forEach(function(d) {
                d["geo.region"] = d["geo.region"] || "world";
            });
            this.isDataPreprocessed = true;
        },

        /*
         * Updates the component as soon as the model/models change
         */
        modelReady: function(evt) {
            if (!this.isDataPreprocessed) this.preprocessData();
        },

        /*
         * UPDATE SHOW:
         * Ideally should only update when show parameters change or data changes
         */
        updateShow: function() {

            //scales
            this.yScale = this.model.marker.axis_y.getDomain();
            this.xScale = this.model.marker.axis_x.getDomain();
            this.rScale = this.model.marker.size.getDomain();

            var _this = this;
            this.yAxis.tickFormat(function(d) {
                return _this.model.marker.axis_y.getTick(d);
            });
            this.xAxis.tickFormat(function(d) {
                return _this.model.marker.axis_x.getTick(d);
            });

        },

        /*
         * UPDATE TIME:
         * Ideally should only update when time or data changes
         */
        updateTime: function() {
            var _this = this;

            this.time = parseInt(d3.time.format(this.model.time.formatInput)(this.model.time.value), 10);
            this.data = this.model.marker.label.getItems({ time: this.time.toString() });

            this.yearEl.text(this.time);
            this.bubbles = this.bubbleContainer.selectAll('.vzb-bc-bubble')
                .data(this.data);

            this.timeUpdatedOnce = true;
        },

        /*
         * RESIZE:
         * Executed whenever the container is resized
         */
        updateSize: function() {

            if (!this.isDataPreprocessed) return;

            var _this = this,
                margin,
                tick_spacing,
                maxRadius,
                minRadius,
                maxRadiusNormalized = this.model.marker.size.max,
                minRadiusNormalized = this.model.marker.size.min,
                padding = 2;

            switch (this.getLayoutProfile()) {
                case "small":
                    margin = {top: 30, right: 20, left: 40, bottom: 40};
                    tick_spacing = 60;
                    maxRadius = 20;
                    break;
                case "medium":
                    margin = {top: 30, right: 60, left: 60, bottom: 40};
                    tick_spacing = 80;
                    maxRadius = 40;
                    break;
                case "large":
                    margin = {top: 30, right: 60, left: 60, bottom: 40};
                    tick_spacing = 100;
                    maxRadius = 60;
                    break;
            }

            minRadius = maxRadius * minRadiusNormalized;
            maxRadius = maxRadius * maxRadiusNormalized;

            //stage
            var height = parseInt(this.element.style("height"), 10) - margin.top - margin.bottom;
            var width = parseInt(this.element.style("width"), 10) - margin.left - margin.right;

            //graph group is shifted according to margins (while svg element is at 100 by 100%)
            this.graph
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            //center year
            var widthAxisY = this.yAxisEl[0][0].getBBox().width;
            var heightAxisX = this.xAxisEl[0][0].getBBox().height;

            this.yearEl
                .attr("x", "50%")
                .attr("y", "50%")
                .attr("transform", "translate(" + (-1 * widthAxisY) + ", " + (heightAxisX) + ")");

            //update scales to the new range
            if (this.model.marker.axis_y.scale !== "ordinal") {
                this.yScale.range([height, 0]).nice();
            } else {
                this.yScale.rangePoints([height, 0], padding).range();
            }
            if (this.model.marker.axis_x.scale !== "ordinal") {
                this.xScale.range([0, width]).nice();
            } else {
                this.xScale.rangePoints([0, width], padding).range();
            }
            if (this.model.marker.size.scale !== "ordinal") {
                this.rScale.range([minRadius, maxRadius]);
            } else {
                this.rScale.rangePoints([minRadius, maxRadius], 0).range();
            }

            //apply scales to axes and redraw
            this.yAxis.scale(this.yScale)
                .orient("left")
                .tickSize(6, 0)
                .ticks(Math.max(height / tick_spacing, 2));

            this.xAxis.scale(this.xScale)
                .orient("bottom")
                .tickSize(6, 0)
                .ticks(Math.max(width / tick_spacing, 2));

            this.xAxisEl.attr("transform", "translate(0," + height + ")");

            this.yAxisEl.call(this.yAxis);
            this.xAxisEl.call(this.xAxis);

            this.sizeUpdatedOnce = false;
        },

        /*
         * REDRAW DATA POINTS:
         * Here plotting happens
         */
        redrawDataPoints: function() {
            var _this = this;
            if(!this.timeUpdatedOnce) this.updateTime();
            if(!this.sizeUpdatedOnce) this.updateSize();

            //exit selection
            this.bubbles.exit().remove();

            //enter selection -- init circles
            this.bubbles.enter().append("circle")
                .attr("class", "vzb-bc-bubble");

            //update selection
            var speed = this.model.time.speed;
            var some_selected = (_this.model.entities.select.length > 0);

            this.bubbles
                .style("fill", function(d) {
                    return _this.model.marker.color.getValue(d);
                })
                .transition().duration(speed).ease("linear")
                .attr("cy", function(d) {
                    var value = _this.model.marker.axis_y.getValue(d);
                    return _this.yScale(value);
                })
                .attr("cx", function(d) {
                    var value = _this.model.marker.axis_x.getValue(d);
                    return _this.xScale(value);
                })
                .attr("r", function(d) {
                    var value = _this.model.marker.size.getValue(d);
                    return Math.sqrt(_this.rScale(value) / Math.PI) * 10;
                });

            this.bubbles.classed("vzb-bc-selected", function(d) {
                    return some_selected && _this.model.entities.isSelected(d)
                })
            this.bubbles.classed("vzb-bc-unselected", function(d) {
                    return some_selected && !_this.model.entities.isSelected(d)
                });

            /* TOOLTIP */
            //TODO: improve tooltip
            this.bubbles.on("mousemove", function(d, i) {
                    var mouse = d3.mouse(_this.graph.node()).map(function(d) {
                        return parseInt(d);
                    });

                    //position tooltip
                    _this.tooltip.classed("vzb-hidden", false)
                        .attr("style", "left:" + (mouse[0] + 50) + "px;top:" + (mouse[1] + 50) + "px")
                        .html(_this.model.marker.label.getValue(d));

                })
                .on("mouseout", function(d, i) {
                    _this.tooltip.classed("vzb-hidden", true);
                })
                .on("click", function(d, i) {
                    _this.model.entities.selectEntity(d);
                });
        }

    });

    return BubbleChart;
});<commit_msg>updateShow and redraw when data changes #hotfix<commit_after>define([
    'jquery',
    'd3',
    'base/component'
], function($, d3, Component) {

    function order(a, b) {
        return radius(b) - radius(a);
    }

    function radius(d, indicator) {
        return d.pop;
    }

    var BubbleChart = Component.extend({
        init: function(context, options) {
            var _this = this;
            this.name = 'bubble-chart';
            this.template = 'components/_examples/' + this.name + '/' + this.name;

            //define expected models for this component
            this.model_expects = ["time", "entities", "marker", "data"];

            this._super(context, options);

            this.xScale = null;
            this.yScale = null;
            this.rScale = null;
            this.cScale = d3.scale.category10();

            this.xAxis = d3.svg.axis();
            this.yAxis = d3.svg.axis();

            this.isDataPreprocessed = false;
            this.timeUpdatedOnce = false;
            this.sizeUpdatedOnce = false;

        },

        /**
         * Executes right after the template is in place
         */
        domReady: function() {
            var _this = this;

            // reference elements
            this.graph = this.element.select('.vzb-bc-graph');
            this.yAxisEl = this.graph.select('.vzb-bc-axis-y');
            this.xAxisEl = this.graph.select('.vzb-bc-axis-x');
            this.yTitleEl = this.graph.select('.vzb-bc-axis-y-title');
            this.xTitleEl = this.graph.select('.vzb-bc-axis-x-title');
            this.yearEl = this.graph.select('.vzb-bc-year');
            this.bubbleContainer = this.graph.select('.vzb-bc-bubbles');
            this.bubbles = null;
            this.tooltip = this.element.select('.vzb-tooltip');

            //model events
            this.model.on({
                "change": function(evt) {
                    console.log("Changed!", evt);
                },
                "load_start": function(evt) {
                    console.log("Started to load!", evt);
                },
                "load_end":  function() {
                    console.log("Finished loading!");
                },
                "ready": function() {
                    console.log("Model ready!");
//TODO: put here the following and remove it from "load_end" and from redrawDataPoints()
//                    _this.preprocessData();
//                    _this.updateShow();
//                    _this.updateTime();
//                    _this.redrawDataPoints();
                }
            });

            this.model.time.on({
                'change:value': function() {
                    _this.updateTime();
                    _this.redrawDataPoints();
                }
            });

            //component events
            this.on("resize", function() {
                console.log("Ops! Gotta resize...");
                _this.updateSize();
                _this.redrawDataPoints();
            })

        },

        preprocessData: function(){
            this.model.marker.label.getItems().forEach(function(d) {
                d["geo.region"] = d["geo.region"] || "world";
            });
            this.isDataPreprocessed = true;
        },

        /*
         * Updates the component as soon as the model/models change
         */
        modelReady: function(evt) {
            if (!this.isDataPreprocessed) this.preprocessData();
            this.updateShow();
            this.redrawDataPoints();
        },

        /*
         * UPDATE SHOW:
         * Ideally should only update when show parameters change or data changes
         */
        updateShow: function() {

            //scales
            this.yScale = this.model.marker.axis_y.getDomain();
            this.xScale = this.model.marker.axis_x.getDomain();
            this.rScale = this.model.marker.size.getDomain();

            var _this = this;
            this.yAxis.tickFormat(function(d) {
                return _this.model.marker.axis_y.getTick(d);
            });
            this.xAxis.tickFormat(function(d) {
                return _this.model.marker.axis_x.getTick(d);
            });

        },

        /*
         * UPDATE TIME:
         * Ideally should only update when time or data changes
         */
        updateTime: function() {
            var _this = this;

            this.time = parseInt(d3.time.format(this.model.time.formatInput)(this.model.time.value), 10);
            this.data = this.model.marker.label.getItems({ time: this.time.toString() });

            this.yearEl.text(this.time);
            this.bubbles = this.bubbleContainer.selectAll('.vzb-bc-bubble')
                .data(this.data);

            this.timeUpdatedOnce = true;
        },

        /*
         * RESIZE:
         * Executed whenever the container is resized
         */
        updateSize: function() {

            if (!this.isDataPreprocessed) return;

            var _this = this,
                margin,
                tick_spacing,
                maxRadius,
                minRadius,
                maxRadiusNormalized = this.model.marker.size.max,
                minRadiusNormalized = this.model.marker.size.min,
                padding = 2;

            switch (this.getLayoutProfile()) {
                case "small":
                    margin = {top: 30, right: 20, left: 40, bottom: 40};
                    tick_spacing = 60;
                    maxRadius = 20;
                    break;
                case "medium":
                    margin = {top: 30, right: 60, left: 60, bottom: 40};
                    tick_spacing = 80;
                    maxRadius = 40;
                    break;
                case "large":
                    margin = {top: 30, right: 60, left: 60, bottom: 40};
                    tick_spacing = 100;
                    maxRadius = 60;
                    break;
            }

            minRadius = maxRadius * minRadiusNormalized;
            maxRadius = maxRadius * maxRadiusNormalized;

            //stage
            var height = parseInt(this.element.style("height"), 10) - margin.top - margin.bottom;
            var width = parseInt(this.element.style("width"), 10) - margin.left - margin.right;

            //graph group is shifted according to margins (while svg element is at 100 by 100%)
            this.graph
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            //center year
            var widthAxisY = this.yAxisEl[0][0].getBBox().width;
            var heightAxisX = this.xAxisEl[0][0].getBBox().height;

            this.yearEl
                .attr("x", "50%")
                .attr("y", "50%")
                .attr("transform", "translate(" + (-1 * widthAxisY) + ", " + (heightAxisX) + ")");

            //update scales to the new range
            if (this.model.marker.axis_y.scale !== "ordinal") {
                this.yScale.range([height, 0]).nice();
            } else {
                this.yScale.rangePoints([height, 0], padding).range();
            }
            if (this.model.marker.axis_x.scale !== "ordinal") {
                this.xScale.range([0, width]).nice();
            } else {
                this.xScale.rangePoints([0, width], padding).range();
            }
            if (this.model.marker.size.scale !== "ordinal") {
                this.rScale.range([minRadius, maxRadius]);
            } else {
                this.rScale.rangePoints([minRadius, maxRadius], 0).range();
            }

            //apply scales to axes and redraw
            this.yAxis.scale(this.yScale)
                .orient("left")
                .tickSize(6, 0)
                .ticks(Math.max(height / tick_spacing, 2));

            this.xAxis.scale(this.xScale)
                .orient("bottom")
                .tickSize(6, 0)
                .ticks(Math.max(width / tick_spacing, 2));

            this.xAxisEl.attr("transform", "translate(0," + height + ")");

            this.yAxisEl.call(this.yAxis);
            this.xAxisEl.call(this.xAxis);

            this.sizeUpdatedOnce = false;
        },

        /*
         * REDRAW DATA POINTS:
         * Here plotting happens
         */
        redrawDataPoints: function() {
            var _this = this;
            if(!this.timeUpdatedOnce) this.updateTime();
            if(!this.sizeUpdatedOnce) this.updateSize();

            //exit selection
            this.bubbles.exit().remove();

            //enter selection -- init circles
            this.bubbles.enter().append("circle")
                .attr("class", "vzb-bc-bubble");

            //update selection
            var speed = this.model.time.speed;
            var some_selected = (_this.model.entities.select.length > 0);

            this.bubbles
                .style("fill", function(d) {
                    return _this.model.marker.color.getValue(d);
                })
                .transition().duration(speed).ease("linear")
                .attr("cy", function(d) {
                    var value = _this.model.marker.axis_y.getValue(d);
                    return _this.yScale(value);
                })
                .attr("cx", function(d) {
                    var value = _this.model.marker.axis_x.getValue(d);
                    return _this.xScale(value);
                })
                .attr("r", function(d) {
                    var value = _this.model.marker.size.getValue(d);
                    return Math.sqrt(_this.rScale(value) / Math.PI) * 10;
                });

            this.bubbles.classed("vzb-bc-selected", function(d) {
                    return some_selected && _this.model.entities.isSelected(d)
                })
            this.bubbles.classed("vzb-bc-unselected", function(d) {
                    return some_selected && !_this.model.entities.isSelected(d)
                });

            /* TOOLTIP */
            //TODO: improve tooltip
            this.bubbles.on("mousemove", function(d, i) {
                    var mouse = d3.mouse(_this.graph.node()).map(function(d) {
                        return parseInt(d);
                    });

                    //position tooltip
                    _this.tooltip.classed("vzb-hidden", false)
                        .attr("style", "left:" + (mouse[0] + 50) + "px;top:" + (mouse[1] + 50) + "px")
                        .html(_this.model.marker.label.getValue(d));

                })
                .on("mouseout", function(d, i) {
                    _this.tooltip.classed("vzb-hidden", true);
                })
                .on("click", function(d, i) {
                    _this.model.entities.selectEntity(d);
                });
        }

    });

    return BubbleChart;
});<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import chartIcon from '../../images/charts_icon.png'
import dashboardIcon from '../../images/dashboard_icon.png'
import { SquealyModal } from './SquealyUtilsComponents'

export default class SideMenu extends Component {
  constructor() {
    super()
    this.state = {
      showLeftNavContextMenu: false,
      leftMenuChartIndex: null,
      showAddChartModal: false,
      chartName: '',
      chartEditMode: false,
      leftMenuPosition: {
        top: 0
      }
    }
  }

  newChartNameChanged = (e) => {
    this.setState({chartName: e.target.value})
  }

  hideLeftMenu = () => {
    this.setState({
      leftMenuChartIndex: null,
      showLeftNavContextMenu: false
    })
  }

  toggleLeftMenu = (e, index) => {
    e.preventDefault();
    let leftMenuPosition = {
      top: e.target.offsetTop - 13
    }

    console.log(e.target.clientTop, e.target.clientLeft, e.target.offsetTop, e.target.offsetLeft)

    this.setState({
      leftMenuChartIndex: ((index !== this.state.leftMenuChartIndex) ||!this.state.showLeftNavContextMenu) ? index : null,
      showLeftNavContextMenu: ((index !== this.state.leftMenuChartIndex) ||!this.state.showLeftNavContextMenu),
      leftMenuPosition: leftMenuPosition
    })
  }

  showChartDetailsModal = (action) => {
    let charts = JSON.parse(JSON.stringify(this.props.charts)),
      selectedChartName = (action === 'EDIT') ? charts[this.state.leftMenuChartIndex].name : ''

    this.setState({
      showAddChartModal: true,
      chartName: selectedChartName,
      showLeftNavContextMenu: false,
      chartEditMode: action === 'EDIT' ? true : false
    })
  }

  selectChartHandler = (index, action) => {
    this.setState({showLeftNavContextMenu: false, leftMenuChartIndex: null})
    this.props.chartSelectionHandler(index)
  }

  chartAdditionModalSave = (e) => {
    if (this.state.chartEditMode) {
      this.props.selectedChartChangeHandler(
        'name', this.state.chartName, null, this.state.leftMenuChartIndex)
    } else {
      this.props.chartAdditionHandler(this.state.chartName)
    }
    this.setState({ showAddChartModal: false, leftMenuChartIndex: null })
  }

  render() {
    const addNewChartModalContent = (

          Name:
          <input type='text' value={this.state.chartName} onChange={this.newChartNameChanged} />

    )

    const {
      charts,
      chartAdditionHandler,
      selectedChartIndex,
      chartSelectionHandler,
      chartDeletionHandler,
      selectedChartChangeHandler} = this.props

    return (

            Charts
             this.showChartDetailsModal('ADD')} className="fa fa-plus-circle add-new" aria-hidden="true">

            {
              charts.map((chart, index) => {
                return (

 this.selectChartHandler(index)} key={index}
                    className={(index === selectedChartIndex) ? 'selected-chart' : ''}
                    onContextMenu={(e) => this.toggleLeftMenu(e, index)}>
                    {chart.name}

)
              })
            }
            {
              this.state.showLeftNavContextMenu &&

 this.showChartDetailsModal('EDIT')}>Rename Chart

                      this.props.chartDeletionHandler(this.state.leftMenuChartIndex, this.hideLeftMenu)}>
                    Delete Chart

Close

            }

        <SquealyModal
          modalId='addChartModal'
          closeModal={() => this.setState({ showAddChartModal: false })}
          showModal={this.state.showAddChartModal}
          modalHeader= {this.state.chartEditMode ? 'Rename Chart' : 'Create New Chart'}
          modalContent={addNewChartModalContent}
          saveChanges={this.chartAdditionModalSave} />

    )
  }
}<commit_msg>Removed console.log<commit_after>import React, { Component, PropTypes } from 'react'
import chartIcon from '../../images/charts_icon.png'
import dashboardIcon from '../../images/dashboard_icon.png'
import { SquealyModal } from './SquealyUtilsComponents'

export default class SideMenu extends Component {
  constructor() {
    super()
    this.state = {
      showLeftNavContextMenu: false,
      leftMenuChartIndex: null,
      showAddChartModal: false,
      chartName: '',
      chartEditMode: false,
      leftMenuPosition: {
        top: 0
      }
    }
  }

  newChartNameChanged = (e) => {
    this.setState({chartName: e.target.value})
  }

  hideLeftMenu = () => {
    this.setState({
      leftMenuChartIndex: null,
      showLeftNavContextMenu: false
    })
  }

  toggleLeftMenu = (e, index) => {
    e.preventDefault();
    let leftMenuPosition = {
      top: e.target.offsetTop - 13
    }

    this.setState({
      leftMenuChartIndex: ((index !== this.state.leftMenuChartIndex) ||!this.state.showLeftNavContextMenu) ? index : null,
      showLeftNavContextMenu: ((index !== this.state.leftMenuChartIndex) ||!this.state.showLeftNavContextMenu),
      leftMenuPosition: leftMenuPosition
    })
  }

  showChartDetailsModal = (action) => {
    let charts = JSON.parse(JSON.stringify(this.props.charts)),
      selectedChartName = (action === 'EDIT') ? charts[this.state.leftMenuChartIndex].name : ''

    this.setState({
      showAddChartModal: true,
      chartName: selectedChartName,
      showLeftNavContextMenu: false,
      chartEditMode: action === 'EDIT' ? true : false
    })
  }

  selectChartHandler = (index, action) => {
    this.setState({showLeftNavContextMenu: false, leftMenuChartIndex: null})
    this.props.chartSelectionHandler(index)
  }

  chartAdditionModalSave = (e) => {
    if (this.state.chartEditMode) {
      this.props.selectedChartChangeHandler(
        'name', this.state.chartName, null, this.state.leftMenuChartIndex)
    } else {
      this.props.chartAdditionHandler(this.state.chartName)
    }
    this.setState({ showAddChartModal: false, leftMenuChartIndex: null })
  }

  render() {
    const addNewChartModalContent = (

          Name:
          <input type='text' value={this.state.chartName} onChange={this.newChartNameChanged} />

    )

    const {
      charts,
      chartAdditionHandler,
      selectedChartIndex,
      chartSelectionHandler,
      chartDeletionHandler,
      selectedChartChangeHandler} = this.props

    return (

            Charts
             this.showChartDetailsModal('ADD')} className="fa fa-plus-circle add-new" aria-hidden="true">

            {
              charts.map((chart, index) => {
                return (

 this.selectChartHandler(index)} key={index}
                    className={(index === selectedChartIndex) ? 'selected-chart' : ''}
                    onContextMenu={(e) => this.toggleLeftMenu(e, index)}>
                    {chart.name}

)
              })
            }
            {
              this.state.showLeftNavContextMenu &&

 this.showChartDetailsModal('EDIT')}>Rename Chart

                      this.props.chartDeletionHandler(this.state.leftMenuChartIndex, this.hideLeftMenu)}>
                    Delete Chart

Close

            }

        <SquealyModal
          modalId='addChartModal'
          closeModal={() => this.setState({ showAddChartModal: false })}
          showModal={this.state.showAddChartModal}
          modalHeader= {this.state.chartEditMode ? 'Rename Chart' : 'Create New Chart'}
          modalContent={addNewChartModalContent}
          saveChanges={this.chartAdditionModalSave} />

    )
  }
}<|endoftext|>


<commit_before>/* global require, describe, it */
'use strict';

// MODULES //

var // Expectation library:
	chai = require( 'chai' ),

	// Module to be tested:
	list = require( './../lib' );

// VARIABLES //

var expect = chai.expect,
	assert = chai.assert;

// TESTS //

describe( 'datasets-male-first-names-en', function tests() {

	it( 'should export an array', function test() {
		expect( list ).to.be.an( 'array' );
	});

	it( 'should only consist of strings', function test() {
		var len = list.length,
			i;

		for ( i = 0; i < len; i++ ) {
			assert.isString( list[ i ], i );
		}
	});

});
<commit_msg>[FIX] test desc.<commit_after>/* global require, describe, it */
'use strict';

// MODULES //

var // Expectation library:
	chai = require( 'chai' ),

	// Module to be tested:
	list = require( './../lib' );

// VARIABLES //

var expect = chai.expect,
	assert = chai.assert;

// TESTS //

describe( 'datasets-female-first-names-en', function tests() {

	it( 'should export an array', function test() {
		expect( list ).to.be.an( 'array' );
	});

	it( 'should only consist of strings', function test() {
		var len = list.length,
			i;

		for ( i = 0; i < len; i++ ) {
			assert.isString( list[ i ], i );
		}
	});

});
<|endoftext|>


<commit_before>/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
    // Task configuration.
    concat: {
      options: {
        banner: '<%= banner %>',
        stripBanners: true
      },
      dist: {
        src: [
          // 'bower_components/phaser/phaser.js',
          'src/namespace.js',
          'src/modules/Util.js',
          'src/vendor/Shout.js',
          'src/modules/GameState.js',
          'src/modules/GameAI.js',
          'src/entities/Score.js',
          'src/entities/StarsBackground.js',
          'src/entities/Player.js',
          'src/entities/Asteroid.js',
          'src/app.js'
        ],
        dest: 'dist/FooFighter.js'
      }
    },
    uglify: {
      options: {
        banner: '<%= banner %>'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: 'dist/FooFighter.min.js'
      }
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        unused: true,
        boss: true,
        eqnull: true,
        browser: true,
        globals: {}
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      lib_test: {
        src: ['lib/**/*.js', 'test/**/*.js']
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    watch: {
      gruntfile: {
        files: ['src/**/*.js','src/*.js'],
        tasks: ['jshint:gruntfile', 'default']
      },
      lib_test: {
        files: '<%= jshint.lib_test.src %>',
        tasks: ['jshint:lib_test', 'qunit']
      }
    },
    connect: {
      server: {
        options: {
          port: 8000,
          base: './',
          keepalive: true
        }
      }
  }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-connect');

  // Default task.
  grunt.registerTask('default', ['jshint', 'concat', 'uglify']);

};
<commit_msg>add scaffolding for Timer entity module<commit_after>/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
    // Task configuration.
    concat: {
      options: {
        banner: '<%= banner %>',
        stripBanners: true
      },
      dist: {
        src: [
          // 'bower_components/phaser/phaser.js',
          'src/namespace.js',
          'src/modules/Util.js',
          'src/vendor/Shout.js',
          'src/modules/GameState.js',
          'src/modules/GameAI.js',
          'src/entities/Score.js',
          'src/entities/Timer.js',
          'src/entities/StarsBackground.js',
          'src/entities/Player.js',
          'src/entities/Asteroid.js',
          'src/app.js'
        ],
        dest: 'dist/FooFighter.js'
      }
    },
    uglify: {
      options: {
        banner: '<%= banner %>'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: 'dist/FooFighter.min.js'
      }
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        unused: true,
        boss: true,
        eqnull: true,
        browser: true,
        globals: {}
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      lib_test: {
        src: ['lib/**/*.js', 'test/**/*.js']
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    watch: {
      gruntfile: {
        files: ['src/**/*.js','src/*.js'],
        tasks: ['jshint:gruntfile', 'default']
      },
      lib_test: {
        files: '<%= jshint.lib_test.src %>',
        tasks: ['jshint:lib_test', 'qunit']
      }
    },
    connect: {
      server: {
        options: {
          port: 8000,
          base: './',
          keepalive: true
        }
      }
  }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-connect');

  // Default task.
  grunt.registerTask('default', ['jshint', 'concat', 'uglify']);

};
<|endoftext|>