<commit_before>/*global describe, beforeEach, module, it, expect, runTest */
describe('uiScroll Paddings spec.', () => {
  'use strict';

  let datasource;
  beforeEach(module('ui.scroll'));
  beforeEach(module('ui.scroll.test.datasources'));
  beforeEach(
    inject(function(myResponsiveDatasource) {
      datasource = myResponsiveDatasource;
    })
  );

  const itemsCount = 30;
  const itemHeight = 100;
  const viewportHeight = 500;
  const MAX = 3; // maximum scrolling interations to reach out the EOF/BOF

  const scrollSettings = {
    datasource: 'myResponsiveDatasource',
    adapter: 'adapter',
    itemHeight: itemHeight,
    viewportHeight: viewportHeight
  };

  function appendTitle(outside, indicies) {
    return (outside ? ' outside' : ' inside') + ' the buffer' + (indicies ? ' when min/max indicies are set': '');
  }

  function setUserIndicies() {
    datasource.minIndex = datasource.min;
    datasource.maxIndex = datasource.max;
  }

  function getBottomPaddingHeight(viewport) {
    const viewportChildren = viewport.children();
    const bottomPadding = viewportChildren[viewportChildren.length - 1];
    return parseInt(angular.element(bottomPadding).css('height'), 10);
  }

  function getTopPaddingHeight(viewport) {
    const viewportChildren = viewport.children();
    const topPadding = viewportChildren[0];
    return parseInt(angular.element(topPadding).css('height'), 10);
  }

  function scrollBottom(viewport, count = 1) {
    for (var i = 0; i < count; i++) {
      viewport.scrollTop(99999);
      viewport.trigger('scroll');
    }
  }

  function scrollTop(viewport, count = 1) {
    for (var i = 0; i < count; i++) {
      viewport.scrollTop(0);
      viewport.trigger('scroll');
    }
  }

  function removeItem(datasource, index) {
    if(index >= datasource.min && index <= datasource.max) {
      const indexRemoved = datasource.data.indexOf(datasource.data[index - datasource.min]);
      if(indexRemoved > -1) {
        datasource.data.splice(indexRemoved, 1);
        if(index === datasource.min) {
          datasource.min++;
        }
        else {
          datasource.max--;
        }
      }
    }
  }

  function insertItems(datasource, index, items = []) {
    if(index >= datasource.min && index <= datasource.max && items.length) {
      const indexToInsert = datasource.data.indexOf(datasource.data[index - datasource.min]);
      if(indexToInsert > -1) {
        datasource.data.splice(indexToInsert, 0, items);
        datasource.max += items.length;
      }
    }
  }

  function checkRow(viewport, row, content, tail = false) {
    var children = viewport.children();
    if(tail) {
      row = children.length - 1 - row;
    }
    const rowElement = children[row];
    expect(rowElement.innerHTML).toBe(content);
  }

  function checkRowBack(viewport, row, content) {
    checkRow(viewport, row, content, true);
  }

  it('\nshould set up properly', () => {
    runTest(scrollSettings,
      () => {
        expect(datasource.min).toBe(1);
        expect(datasource.max).toBe(itemsCount);
      }
    );
  });

  describe('Removing items via indexed-based applyUpdates\n', () => {

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove last row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              scrollBottom(viewport, MAX);
              outside && scrollTop(viewport);
              const initialBottomHeight = getBottomPaddingHeight(viewport);

              removeItem(datasource, datasource.max);
              scope.adapter.applyUpdates(itemsCount, []);
              outside && expect(getBottomPaddingHeight(viewport)).toBe(initialBottomHeight - itemHeight);

              !outside && scrollTop(viewport);
              scrollBottom(viewport, MAX);
              expect(viewport.scrollTop()).toBe(itemsCount * itemHeight - viewportHeight - itemHeight);
              checkRowBack(viewport, 1, (itemsCount - 1) + ': item' + (itemsCount - 1));
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove last row and then the next after last' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              scrollBottom(viewport, MAX);
              outside && scrollTop(viewport);

              const initialBottomHeight = getBottomPaddingHeight(viewport);
              removeItem(datasource, datasource.max);
              scope.adapter.applyUpdates(itemsCount, []);
              removeItem(datasource, datasource.max);
              scope.adapter.applyUpdates(itemsCount - 1, []);
              outside && expect(getBottomPaddingHeight(viewport)).toBe(initialBottomHeight - itemHeight * 2);

              !outside && scrollTop(viewport);
              scrollBottom(viewport, MAX);
              expect(viewport.scrollTop()).toBe(itemsCount * itemHeight - viewportHeight - itemHeight * 2);
              checkRowBack(viewport, 1, (itemsCount - 2) + ': item' + (itemsCount - 2));
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove pre-last row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              scrollBottom(viewport, MAX);
              outside && scrollTop(viewport);

              const initialBottomHeight = getBottomPaddingHeight(viewport);
              removeItem(datasource, datasource.max - 1);
              scope.adapter.applyUpdates(itemsCount - 1, []);
              outside && expect(getBottomPaddingHeight(viewport)).toBe(initialBottomHeight - itemHeight);

              !outside && scrollTop(viewport);
              scrollBottom(viewport, MAX);
              expect(viewport.scrollTop()).toBe(itemsCount * itemHeight - viewportHeight - itemHeight);
              checkRowBack(viewport, 1, (itemsCount - 1) + ': item' + itemsCount);
              checkRowBack(viewport, 2, (itemsCount - 2) + ': item' + (itemsCount - 2));
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove first row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              outside && scrollBottom(viewport, MAX);

              const initialTopHeight = getTopPaddingHeight(viewport);
              removeItem(datasource, datasource.min);
              scope.adapter.applyUpdates(1, []);
              outside && expect(getTopPaddingHeight(viewport)).toBe(initialTopHeight - itemHeight);

              !outside && scrollBottom(viewport, MAX);
              expect(getBottomPaddingHeight(viewport)).toBe(0);

              scrollTop(viewport);
              expect(getTopPaddingHeight(viewport)).toBe(0);
              checkRow(viewport, 1, '2: item2');
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove first row and then the next after first' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              outside && scrollBottom(viewport, MAX);

              const initialTopHeight = getTopPaddingHeight(viewport);
              removeItem(datasource, datasource.min);
              scope.adapter.applyUpdates(1, []);
              removeItem(datasource, datasource.min);
              scope.adapter.applyUpdates(2, []);
              outside && expect(getTopPaddingHeight(viewport)).toBe(initialTopHeight - itemHeight * 2);

              !outside && scrollBottom(viewport, MAX);
              expect(getBottomPaddingHeight(viewport)).toBe(0);

              scrollTop(viewport);
              expect(getTopPaddingHeight(viewport)).toBe(0);
              checkRow(viewport, 1, '3: item3');
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove second row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              outside && scrollBottom(viewport, MAX);

              const initialTopHeight = getTopPaddingHeight(viewport);
              removeItem(datasource, datasource.min  + 1);
              scope.adapter.applyUpdates(2, []);
              outside && expect(getTopPaddingHeight(viewport)).toBe(initialTopHeight - itemHeight * 1);

              !outside && scrollBottom(viewport, MAX);
              expect(getBottomPaddingHeight(viewport)).toBe(0);

              scrollTop(viewport);
              expect(getTopPaddingHeight(viewport)).toBe(0);
              checkRow(viewport, 1, '1: item1');
              checkRow(viewport, 2, '2: item3');
            }
          )
        )
      )
    );
  });

  describe('Appending inside the buffer\n', () => {

    it('should append 3 rows via index-based applyUpdates', () => {
      runTest(Object.assign({}, scrollSettings, { startIndex: 28 }),
        (viewport, scope) => {
          const newItems = [
            'item' + (datasource.max + 1),
            'item' + (datasource.max + 2),
            'item' + (datasource.max + 3)
          ];
          const oldMax = datasource.max;
          const _scrollTop = viewport.scrollTop();

          insertItems(datasource, datasource.max, newItems);
          scope.adapter.applyUpdates(oldMax, ['item' + oldMax, ...newItems]);

          scrollBottom(viewport);
          expect(viewport.scrollTop()).toBe(_scrollTop + newItems.length * itemHeight);

          checkRowBack(viewport, 1, (datasource.max - 0) + ': ' + newItems[2]);
          checkRowBack(viewport, 2, (datasource.max - 1) + ': ' + newItems[1]);
          checkRowBack(viewport, 3, (datasource.max - 2) + ': ' + newItems[0]);
          checkRowBack(viewport, 4, oldMax + ': item' + oldMax);
        }
      );
    });

    it('should append 3 rows via index-based applyUpdates when min/max indicies are set', () => {
      runTest(Object.assign({}, scrollSettings, { startIndex: 28 }),
        (viewport, scope) => {
          const newItems = [
            'item' + (datasource.max + 1),
            'item' + (datasource.max + 2),
            'item' + (datasource.max + 3)
          ];
          const oldMax = datasource.max;

          datasource.minIndex = datasource.min;
          datasource.maxIndex = datasource.max;
          const _scrollTop = viewport.scrollTop();

          insertItems(datasource, datasource.max, newItems);
          scope.adapter.applyUpdates(oldMax, ['item' + oldMax, ...newItems]);

          scrollBottom(viewport);
          expect(viewport.scrollTop()).toBe(_scrollTop + newItems.length * itemHeight);

          checkRowBack(viewport, 1, (datasource.max - 0) + ': ' + newItems[2]);
          checkRowBack(viewport, 2, (datasource.max - 1) + ': ' + newItems[1]);
          checkRowBack(viewport, 3, (datasource.max - 2) + ': ' + newItems[0]);
          checkRowBack(viewport, 4, oldMax + ': item' + oldMax);
        }
      );
    });
  });

  describe('Removing items via indexed-based applyUpdates when neither BOF nor EOF are reached\n', () => {

    [true, false].forEach(userIndicies =>
      it('should remove first row' + appendTitle(true, userIndicies), () =>
        runTest(Object.assign({}, scrollSettings, { startIndex: 12 }),
          (viewport, scope) => {
            userIndicies && setUserIndicies();

            removeItem(datasource, 2);
            scope.adapter.applyUpdates(2, []);

            scrollBottom(viewport);
            expect(getBottomPaddingHeight(viewport)).toBe(0);
            checkRowBack(viewport, 1, '29: item30');

            scrollTop(viewport);
            expect(getTopPaddingHeight(viewport)).toBe(0);
            checkRow(viewport, 1, '1: item1');
            checkRow(viewport, 2, '2: item3');
          }
        )
      )
    );

    [true, false].forEach(userIndicies =>
      it('should remove last row' + appendTitle(true, userIndicies), () =>
        runTest(Object.assign({}, scrollSettings, { startIndex: 12 }),
          (viewport, scope) => {
            userIndicies && setUserIndicies();

            removeItem(datasource, 19);
            scope.adapter.applyUpdates(19, []);

            scrollBottom(viewport);
            expect(getBottomPaddingHeight(viewport)).toBe(0);
            checkRowBack(viewport, 1, '29: item30');

            scrollTop(viewport);
            expect(getTopPaddingHeight(viewport)).toBe(0);
          }
        )
      )
    );
  });

});<commit_msg>test for removing absoulte first item when minIndex > minIndexUser<commit_after>/*global describe, beforeEach, module, it, expect, runTest */
describe('uiScroll Paddings spec.', () => {
  'use strict';

  let datasource;
  beforeEach(module('ui.scroll'));
  beforeEach(module('ui.scroll.test.datasources'));
  beforeEach(
    inject(function(myResponsiveDatasource) {
      datasource = myResponsiveDatasource;
    })
  );

  const itemsCount = 30;
  const itemHeight = 100;
  const viewportHeight = 500;
  const MAX = 3; // maximum scrolling interations to reach out the EOF/BOF

  const scrollSettings = {
    datasource: 'myResponsiveDatasource',
    adapter: 'adapter',
    itemHeight: itemHeight,
    viewportHeight: viewportHeight
  };

  function appendTitle(outside, indicies) {
    return (outside ? ' outside' : ' inside') + ' the buffer' + (indicies ? ' when min/max indicies are set': '');
  }

  function setUserIndicies() {
    datasource.minIndex = datasource.min;
    datasource.maxIndex = datasource.max;
  }

  function getBottomPaddingHeight(viewport) {
    const viewportChildren = viewport.children();
    const bottomPadding = viewportChildren[viewportChildren.length - 1];
    return parseInt(angular.element(bottomPadding).css('height'), 10);
  }

  function getTopPaddingHeight(viewport) {
    const viewportChildren = viewport.children();
    const topPadding = viewportChildren[0];
    return parseInt(angular.element(topPadding).css('height'), 10);
  }

  function scrollBottom(viewport, count = 1) {
    for (var i = 0; i < count; i++) {
      viewport.scrollTop(99999);
      viewport.trigger('scroll');
    }
  }

  function scrollTop(viewport, count = 1) {
    for (var i = 0; i < count; i++) {
      viewport.scrollTop(0);
      viewport.trigger('scroll');
    }
  }

  function removeItem(datasource, index) {
    if(index >= datasource.min && index <= datasource.max) {
      const indexRemoved = datasource.data.indexOf(datasource.data[index - datasource.min]);
      if(indexRemoved > -1) {
        datasource.data.splice(indexRemoved, 1);
        if(index === datasource.min) {
          datasource.min++;
        }
        else {
          datasource.max--;
        }
      }
    }
  }

  function insertItems(datasource, index, items = []) {
    if(index >= datasource.min && index <= datasource.max && items.length) {
      const indexToInsert = datasource.data.indexOf(datasource.data[index - datasource.min]);
      if(indexToInsert > -1) {
        datasource.data.splice(indexToInsert, 0, items);
        datasource.max += items.length;
      }
    }
  }

  function checkRow(viewport, row, content, tail = false) {
    var children = viewport.children();
    if(tail) {
      row = children.length - 1 - row;
    }
    const rowElement = children[row];
    expect(rowElement.innerHTML).toBe(content);
  }

  function checkRowBack(viewport, row, content) {
    checkRow(viewport, row, content, true);
  }

  it('\nshould set up properly', () => {
    runTest(scrollSettings,
      () => {
        expect(datasource.min).toBe(1);
        expect(datasource.max).toBe(itemsCount);
      }
    );
  });

  describe('Removing items via indexed-based applyUpdates\n', () => {

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove last row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              scrollBottom(viewport, MAX);
              outside && scrollTop(viewport);
              const initialBottomHeight = getBottomPaddingHeight(viewport);

              removeItem(datasource, datasource.max);
              scope.adapter.applyUpdates(itemsCount, []);
              outside && expect(getBottomPaddingHeight(viewport)).toBe(initialBottomHeight - itemHeight);

              !outside && scrollTop(viewport);
              scrollBottom(viewport, MAX);
              expect(viewport.scrollTop()).toBe(itemsCount * itemHeight - viewportHeight - itemHeight);
              checkRowBack(viewport, 1, (itemsCount - 1) + ': item' + (itemsCount - 1));
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove last row and then the next after last' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              scrollBottom(viewport, MAX);
              outside && scrollTop(viewport);

              const initialBottomHeight = getBottomPaddingHeight(viewport);
              removeItem(datasource, datasource.max);
              scope.adapter.applyUpdates(itemsCount, []);
              removeItem(datasource, datasource.max);
              scope.adapter.applyUpdates(itemsCount - 1, []);
              outside && expect(getBottomPaddingHeight(viewport)).toBe(initialBottomHeight - itemHeight * 2);

              !outside && scrollTop(viewport);
              scrollBottom(viewport, MAX);
              expect(viewport.scrollTop()).toBe(itemsCount * itemHeight - viewportHeight - itemHeight * 2);
              checkRowBack(viewport, 1, (itemsCount - 2) + ': item' + (itemsCount - 2));
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove pre-last row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              scrollBottom(viewport, MAX);
              outside && scrollTop(viewport);

              const initialBottomHeight = getBottomPaddingHeight(viewport);
              removeItem(datasource, datasource.max - 1);
              scope.adapter.applyUpdates(itemsCount - 1, []);
              outside && expect(getBottomPaddingHeight(viewport)).toBe(initialBottomHeight - itemHeight);

              !outside && scrollTop(viewport);
              scrollBottom(viewport, MAX);
              expect(viewport.scrollTop()).toBe(itemsCount * itemHeight - viewportHeight - itemHeight);
              checkRowBack(viewport, 1, (itemsCount - 1) + ': item' + itemsCount);
              checkRowBack(viewport, 2, (itemsCount - 2) + ': item' + (itemsCount - 2));
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove first row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              outside && scrollBottom(viewport, MAX);

              const initialTopHeight = getTopPaddingHeight(viewport);
              removeItem(datasource, datasource.min);
              scope.adapter.applyUpdates(1, []);
              outside && expect(getTopPaddingHeight(viewport)).toBe(initialTopHeight - itemHeight);

              !outside && scrollBottom(viewport, MAX);
              expect(getBottomPaddingHeight(viewport)).toBe(0);

              scrollTop(viewport);
              expect(getTopPaddingHeight(viewport)).toBe(0);
              checkRow(viewport, 1, '2: item2');
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove first row and then the next after first' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              outside && scrollBottom(viewport, MAX);

              const initialTopHeight = getTopPaddingHeight(viewport);
              removeItem(datasource, datasource.min);
              scope.adapter.applyUpdates(1, []);
              removeItem(datasource, datasource.min);
              scope.adapter.applyUpdates(2, []);
              outside && expect(getTopPaddingHeight(viewport)).toBe(initialTopHeight - itemHeight * 2);

              !outside && scrollBottom(viewport, MAX);
              expect(getBottomPaddingHeight(viewport)).toBe(0);

              scrollTop(viewport);
              expect(getTopPaddingHeight(viewport)).toBe(0);
              checkRow(viewport, 1, '3: item3');
            }
          )
        )
      )
    );

    [true, false].forEach(outside =>
      [true, false].forEach(userIndicies =>
        it('should remove second row' + appendTitle(outside, userIndicies), () =>
          runTest(scrollSettings,
            (viewport, scope) => {
              userIndicies && setUserIndicies();

              outside && scrollBottom(viewport, MAX);

              const initialTopHeight = getTopPaddingHeight(viewport);
              removeItem(datasource, datasource.min  + 1);
              scope.adapter.applyUpdates(2, []);
              outside && expect(getTopPaddingHeight(viewport)).toBe(initialTopHeight - itemHeight * 1);

              !outside && scrollBottom(viewport, MAX);
              expect(getBottomPaddingHeight(viewport)).toBe(0);

              scrollTop(viewport);
              expect(getTopPaddingHeight(viewport)).toBe(0);
              checkRow(viewport, 1, '1: item1');
              checkRow(viewport, 2, '2: item3');
            }
          )
        )
      )
    );
  });

  describe('Appending inside the buffer\n', () => {

    it('should append 3 rows via index-based applyUpdates', () => {
      runTest(Object.assign({}, scrollSettings, { startIndex: 28 }),
        (viewport, scope) => {
          const newItems = [
            'item' + (datasource.max + 1),
            'item' + (datasource.max + 2),
            'item' + (datasource.max + 3)
          ];
          const oldMax = datasource.max;
          const _scrollTop = viewport.scrollTop();

          insertItems(datasource, datasource.max, newItems);
          scope.adapter.applyUpdates(oldMax, ['item' + oldMax, ...newItems]);

          scrollBottom(viewport);
          expect(viewport.scrollTop()).toBe(_scrollTop + newItems.length * itemHeight);

          checkRowBack(viewport, 1, (datasource.max - 0) + ': ' + newItems[2]);
          checkRowBack(viewport, 2, (datasource.max - 1) + ': ' + newItems[1]);
          checkRowBack(viewport, 3, (datasource.max - 2) + ': ' + newItems[0]);
          checkRowBack(viewport, 4, oldMax + ': item' + oldMax);
        }
      );
    });

    it('should append 3 rows via index-based applyUpdates when min/max indicies are set', () => {
      runTest(Object.assign({}, scrollSettings, { startIndex: 28 }),
        (viewport, scope) => {
          const newItems = [
            'item' + (datasource.max + 1),
            'item' + (datasource.max + 2),
            'item' + (datasource.max + 3)
          ];
          const oldMax = datasource.max;

          datasource.minIndex = datasource.min;
          datasource.maxIndex = datasource.max;
          const _scrollTop = viewport.scrollTop();

          insertItems(datasource, datasource.max, newItems);
          scope.adapter.applyUpdates(oldMax, ['item' + oldMax, ...newItems]);

          scrollBottom(viewport);
          expect(viewport.scrollTop()).toBe(_scrollTop + newItems.length * itemHeight);

          checkRowBack(viewport, 1, (datasource.max - 0) + ': ' + newItems[2]);
          checkRowBack(viewport, 2, (datasource.max - 1) + ': ' + newItems[1]);
          checkRowBack(viewport, 3, (datasource.max - 2) + ': ' + newItems[0]);
          checkRowBack(viewport, 4, oldMax + ': item' + oldMax);
        }
      );
    });
  });

  describe('Removing items via indexed-based applyUpdates when neither BOF nor EOF are reached\n', () => {
    const _scrollSettings = Object.assign({}, scrollSettings, { startIndex: 12 });

    [true, false].forEach(userIndicies =>
      it('should remove first buffered row' + appendTitle(true, userIndicies), () =>
        runTest(_scrollSettings,
          (viewport, scope) => {
            userIndicies && setUserIndicies();

            removeItem(datasource, 2);
            scope.adapter.applyUpdates(2, []);

            scrollBottom(viewport, MAX);
            expect(getBottomPaddingHeight(viewport)).toBe(0);
            checkRowBack(viewport, 1, (itemsCount - 1) + ': item' + itemsCount);

            scrollTop(viewport);
            expect(getTopPaddingHeight(viewport)).toBe(0);
            checkRow(viewport, 1, '1: item1');
            checkRow(viewport, 2, '2: item3');
          }
        )
      )
    );

    [true, false].forEach(userIndicies =>
      it('should remove last buffered row' + appendTitle(true, userIndicies), () =>
        runTest(_scrollSettings,
          (viewport, scope) => {
            userIndicies && setUserIndicies();

            removeItem(datasource, 19);
            scope.adapter.applyUpdates(19, []);

            scrollBottom(viewport);
            expect(getBottomPaddingHeight(viewport)).toBe(0);
            checkRowBack(viewport, 1, (itemsCount - 1) + ': item' + itemsCount);

            scrollTop(viewport);
            expect(getTopPaddingHeight(viewport)).toBe(0);
          }
        )
      )
    );

    [true, false].forEach(userIndicies =>
      it('should remove absolute first row' + appendTitle(true, userIndicies), () =>
        runTest(_scrollSettings,
          (viewport, scope) => {
            userIndicies && setUserIndicies();

            removeItem(datasource, 1);
            scope.adapter.applyUpdates(1, []);

            scrollBottom(viewport, MAX);
            expect(getBottomPaddingHeight(viewport)).toBe(0);
            checkRowBack(viewport, 1, itemsCount + ': item' + itemsCount);

            scrollTop(viewport);
            expect(getTopPaddingHeight(viewport)).toBe(0);
            checkRow(viewport, 1, '2: item2');
            checkRow(viewport, 2, '3: item3');
          }
        )
      )
    );

    [true, false].forEach(userIndicies =>
      it('should remove absolute last row' + appendTitle(true, userIndicies), () =>
        runTest(_scrollSettings,
          (viewport, scope) => {
            userIndicies && setUserIndicies();

            removeItem(datasource, itemsCount);
            scope.adapter.applyUpdates(itemsCount, []);

            scrollBottom(viewport, MAX);
            expect(getBottomPaddingHeight(viewport)).toBe(0);
            checkRowBack(viewport, 1, (itemsCount - 1) + ': item' + (itemsCount - 1));

            scrollTop(viewport);
            expect(getTopPaddingHeight(viewport)).toBe(0);
            checkRow(viewport, 1, '1: item1');
            checkRow(viewport, 2, '2: item2');
          }
        )
      )
    );
  });

});<|endoftext|>


<commit_before>(function ($) {

    // TODO: We're going to need data-* attributes to prevent expanding/collapsing/etc all of
    // the buttons at once
    $('div').on('click', '.save-button', function (e) {
        var id = $(this).attr('data-id');
        $('#save-button-div-' + id).collapse('hide');
        $('#watched-buttons-' + id).collapse('show');
        return;
    });

    $('div').on('click', '.watched-btn', function (e) {
        var id = $(this).attr('data-id');

        $('#watched-buttons-' + id).collapse('hide');
        $('#save-button[data-id="' + id + '"]').attr('disabled', 'disabled');
        $('#save-button-div-' + id).collapse('show');
    });

    $('h2').on('click', '.btn-collapse', function (e) {
        var text = $(this).html() === 'Collapse' ? 'Expand' : 'Collapse';
        $(this).html(text);
    });

    // For every

, if the path is equal to the current
    // browser location, give the anchor the 'active' class so it is properly
    // highlighted by bootstrap
    $('.navbar .navbar-nav li').each(function (_, li) {
        if (li.children instanceof HTMLCollection && li.children.length > 0) {
            $(li.children).each(function (_, child) {
                if (child.href === document.location.href.split('#')[0]) {
                    li.classList.add('active');
                }
            });
        }
    });

    // Display overlay when the user hovers over a movie panel
    $('.movie-panel').on('mouseenter', function () {
        $(this).find('.movie-panel-overlay').finish();
        $(this).find('.movie-panel-overlay').fadeIn(150);
    }).on('mouseleave', function () {
        $(this).find('.movie-panel-overlay').fadeOut(150);
    });

    // Why do I need to specify this hook? I'm not sure -- mabye because
    // bootstrap = dick
    $('.input-group').on('focus', '.form-control', function () {
        $(this).closest('.form-group, .input-group').addClass('focus');
    }).on('blur', '.form-control', function () {
        $(this).closest('.form-group, .input-group').removeClass('focus');
    });

})(window.jQuery);
<commit_msg>Fixes #12<commit_after>(function ($) {

    // TODO: We're going to need data-* attributes to prevent expanding/collapsing/etc all of
    // the buttons at once
    $('div').on('click', '.save-button', function (e) {
        var id = $(this).attr('data-id');
        $('#save-button-div-' + id).collapse('hide');
        $('#watched-buttons-' + id).collapse('show');
        return;
    });

    $('div').on('click', '.watched-btn', function (e) {
        var id = $(this).attr('data-id');

        $('#watched-buttons-' + id).collapse('hide');
        $('.save-button[data-id="' + id + '"]').attr('disabled', 'disabled');
        $('#save-button-div-' + id).collapse('show');
    });

    $('h2').on('click', '.btn-collapse', function (e) {
        var text = $(this).html() === 'Collapse' ? 'Expand' : 'Collapse';
        $(this).html(text);
    });

    // For every

, if the path is equal to the current
    // browser location, give the anchor the 'active' class so it is properly
    // highlighted by bootstrap
    $('.navbar .navbar-nav li').each(function (_, li) {
        if (li.children instanceof HTMLCollection && li.children.length > 0) {
            $(li.children).each(function (_, child) {
                if (child.href === document.location.href.split('#')[0]) {
                    li.classList.add('active');
                }
            });
        }
    });

    // Display overlay when the user hovers over a movie panel
    $('.movie-panel').on('mouseenter', function () {
        $(this).find('.movie-panel-overlay').finish();
        $(this).find('.movie-panel-overlay').fadeIn(150);
    }).on('mouseleave', function () {
        $(this).find('.movie-panel-overlay').fadeOut(150);
    });

    // Why do I need to specify this hook? I'm not sure -- mabye because
    // bootstrap = dick
    $('.input-group').on('focus', '.form-control', function () {
        $(this).closest('.form-group, .input-group').addClass('focus');
    }).on('blur', '.form-control', function () {
        $(this).closest('.form-group, .input-group').removeClass('focus');
    });

})(window.jQuery);
<|endoftext|>


<commit_before>// message types
var MESSAGE_INFO = 'info';
var MESSAGE_ERROR = 'error';
var WEB_CLIENT_ID = 'YOUR_WEB_CLIENT_ID';
var SCOPES = 'https://www.googleapis.com/auth/userinfo.email';

var messageType = {
    info: "#info",
    error: "#error"
  };

// charging states:
var chargingStates = {
  0: "Not Charging",
  1: "Charging Recovery",
  2: "Charging",
  3: "Trickle Charging",
  4: "Waiting",
  5: "Charging Error"
};

var roomba_buttons = [
  '#clean',
  '#sleep',
  '#doc',
  '#status'
];

var signedIn = false;

function enableButtons(buttons) {

  for (var i = 0; i < buttons.length; i++){
    $(buttons[i]).attr({'disabled': false});
    $(buttons[i]).click(buttonClickHandler);
  }
}

function getStatus(){

  displayMessage("Requesting data from server...", MESSAGE_INFO);
  gapi.client.roomba_api.cmd.sendCommand({'command': 'status'}).execute(function(resp) {
    if (!resp.code){
      hideMessage(MESSAGE_INFO);
      $('#status').attr({'disabled': false}); //ok this is bad FIXME

      if (resp.result.result === 'RESULT_ERROR'){
        displayMessage(resp.error_message, MESSAGE_ERROR);
      } else {
        // update charging state data
        data = JSON.parse(resp.data);
        $('#charging_state').html(chargingStates[data.currentData.chargingState]);
        charge = Math.round((data.currentData.charge * 100)/data.currentData.capacity);
        $('#battery_remaining').html(charge + '%');
        updateProgressBar(charge);

        if (data.currentData.doc_state === true){
            $('#docking_state').html("connected");
        } else {
          $('#docking_state').html("disconnected");
        }
      }
    }
  });
}

function buttonClickHandler(){

    sendRoombaCommand(this.id);
    $(this).attr({disabled: true});
}

function sendRoombaCommand(cmd){

  hideMessage(MESSAGE_ERROR);
  displayMessage("Sending command: " + cmd , MESSAGE_INFO);

  if (cmd != 'status'){
    gapi.client.roomba_api.cmd.sendCommand({'command':cmd}).execute(function(resp) {

      if (!resp.code){
        hideMessage(MESSAGE_INFO);
        $('#'+cmd).attr({'disabled': false});

        if (resp.result.result === 'RESULT_ERROR'){
              displayMessage(resp.error_message + ' ' + resp.command, MESSAGE_ERROR);
        } else {
            displayMessage('Command executed successfully', MESSAGE_INFO, true);
            // TODO: update device state information with info received  from response
            // need to add this functionality to imp first
        }
      }
    });
  } else if (cmd === 'status') {
    getStatus();
  }
}

function displayMessage(message, type, autoclose, timeout){

  autoclose = autoclose || false;
  timeout = timeout || 2000;

  $(messageType[type]).html(message);
  $(messageType[type]).show();

  if (autoclose){
    window.setTimeout(function() { hideMessage(type); }, timeout);
  }

}

function hideMessage(type){

  $(messageType[type]).hide();

}

function updateProgressBar(value){

  if (value > 70){
    document.getElementById("progress_bar").className = 'progress-bar progress-bar-success';
  } else if (value > 40 & value < 70 ) {
    document.getElementById("progress_bar").className = 'progress-bar progress-bar-warning';
  } else if (value < 40) {
    document.getElementById("progress_bar").className = 'progress-bar progress-bar-danger';
  }
  $('#progress_bar').css('width', value+'%').attr('aria-valuenow', value);
}

function userAuthed(){

  var request = gapi.client.oauth2.userinfo.get().execute(function(resp) {
    if (!resp.code) {
      signedIn = true;
      getStatus();
      enableButtons(roomba_buttons);
      //document.getElementById('signinButton').innerHTML = 'Sign out';
      //document.getElementById('authedGreeting').disabled = false;
    } else {
      signin(false, userAuthed);
    }
  });
}

function signin(mode, callback) {

  gapi.auth.authorize ( {
      client_id:  WEB_CLIENT_ID,
      scope:  SCOPES, immediate: mode
    },
    callback);
}

function init(apiRoot) {

  // Loads the OAuth and helloworld APIs asynchronously, and triggers login
  // when they have completed.
  var apisToLoad;
  var callback = function() {

    if (--apisToLoad === 0) {
      signin(true, userAuthed);
    }
  };
  apisToLoad = 2; // must match number of calls to gapi.client.load()
  gapi.client.load('roomba_api', 'v1', callback, apiRoot);
  gapi.client.load('oauth2', 'v2', callback);
}
<commit_msg>fixed progress bar color issue. now if battery value = 70 or 40 collor won't be default<commit_after>// message types
var MESSAGE_INFO = 'info';
var MESSAGE_ERROR = 'error';
var WEB_CLIENT_ID = 'YOUR_WEB_CLIENT_ID';
var SCOPES = 'https://www.googleapis.com/auth/userinfo.email';

var messageType = {
    info: "#info",
    error: "#error"
  };

// charging states:
var chargingStates = {
  0: "Not Charging",
  1: "Charging Recovery",
  2: "Charging",
  3: "Trickle Charging",
  4: "Waiting",
  5: "Charging Error"
};

var roomba_buttons = [
  '#clean',
  '#sleep',
  '#doc',
  '#status'
];

var signedIn = false;

function enableButtons(buttons) {

  for (var i = 0; i < buttons.length; i++){
    $(buttons[i]).attr({'disabled': false});
    $(buttons[i]).click(buttonClickHandler);
  }
}

function getStatus(){

  displayMessage("Requesting data from server...", MESSAGE_INFO);
  gapi.client.roomba_api.cmd.sendCommand({'command': 'status'}).execute(function(resp) {
    if (!resp.code){
      hideMessage(MESSAGE_INFO);
      $('#status').attr({'disabled': false}); //ok this is bad FIXME

      if (resp.result.result === 'RESULT_ERROR'){
        displayMessage(resp.error_message, MESSAGE_ERROR);
      } else {
        // update charging state data
        data = JSON.parse(resp.data);
        $('#charging_state').html(chargingStates[data.currentData.chargingState]);
        charge = Math.round((data.currentData.charge * 100)/data.currentData.capacity);
        $('#battery_remaining').html(charge + '%');
        updateProgressBar(charge);

        if (data.currentData.doc_state === true){
            $('#docking_state').html("connected");
        } else {
          $('#docking_state').html("disconnected");
        }
      }
    }
  });
}

function buttonClickHandler(){

    sendRoombaCommand(this.id);
    $(this).attr({disabled: true});
}

function sendRoombaCommand(cmd){

  hideMessage(MESSAGE_ERROR);
  displayMessage("Sending command: " + cmd , MESSAGE_INFO);

  if (cmd != 'status'){
    gapi.client.roomba_api.cmd.sendCommand({'command':cmd}).execute(function(resp) {

      if (!resp.code){
        hideMessage(MESSAGE_INFO);
        $('#'+cmd).attr({'disabled': false});

        if (resp.result.result === 'RESULT_ERROR'){
              displayMessage(resp.error_message + ' ' + resp.command, MESSAGE_ERROR);
        } else {
            displayMessage('Command executed successfully', MESSAGE_INFO, true);
            // TODO: update device state information with info received  from response
            // need to add this functionality to imp first
        }
      }
    });
  } else if (cmd === 'status') {
    getStatus();
  }
}

function displayMessage(message, type, autoclose, timeout){

  autoclose = autoclose || false;
  timeout = timeout || 2000;

  $(messageType[type]).html(message);
  $(messageType[type]).show();

  if (autoclose){
    window.setTimeout(function() { hideMessage(type); }, timeout);
  }

}

function hideMessage(type){

  $(messageType[type]).hide();

}

function updateProgressBar(value){

  if (value > 70){
    document.getElementById("progress_bar").className = 'progress-bar progress-bar-success';
  } else if (value >= 40 & value <= 70 ) {
    document.getElementById("progress_bar").className = 'progress-bar progress-bar-warning';
  } else if (value < 40) {
    document.getElementById("progress_bar").className = 'progress-bar progress-bar-danger';
  }
  $('#progress_bar').css('width', value+'%').attr('aria-valuenow', value);
}

function userAuthed(){

  var request = gapi.client.oauth2.userinfo.get().execute(function(resp) {
    if (!resp.code) {
      signedIn = true;
      getStatus();
      enableButtons(roomba_buttons);
      //document.getElementById('signinButton').innerHTML = 'Sign out';
      //document.getElementById('authedGreeting').disabled = false;
    } else {
      signin(false, userAuthed);
    }
  });
}

function signin(mode, callback) {

  gapi.auth.authorize ( {
      client_id:  WEB_CLIENT_ID,
      scope:  SCOPES, immediate: mode
    },
    callback);
}

function init(apiRoot) {

  // Loads the OAuth and helloworld APIs asynchronously, and triggers login
  // when they have completed.
  var apisToLoad;
  var callback = function() {

    if (--apisToLoad === 0) {
      signin(true, userAuthed);
    }
  };
  apisToLoad = 2; // must match number of calls to gapi.client.load()
  gapi.client.load('roomba_api', 'v1', callback, apiRoot);
  gapi.client.load('oauth2', 'v2', callback);
}
<|endoftext|>


<commit_before>function requestTimestamps() {

  $.getJSON('/ig_data', function(data) {
    activities[Service.INSTAGRAM] = data;
    visualize();
  });

  $.getJSON('/fsq_data', function(data) {
    activities[Service.FOURSQUARE] = data;
    visualize();
  });

  $.getJSON('/gh_data', function(data) {
    activities[Service.GITHUB] = data;
    visualize();
  });
}

var Service = Object.freeze({FACEBOOK: 0, INSTAGRAM: 1, GITHUB: 2, FOURSQUARE: 3});
var className = {};
className[Service.FACEBOOK] = 'fb';
className[Service.INSTAGRAM] = 'ig';
className[Service.GITHUB] = 'gh';
className[Service.FOURSQUARE] = 'fsq';

var activities = {}

var hourFormat = d3.time.format("%H");
var minuteFormat = d3.time.format("%M");

function timestampsToMinuteInDay(t) {
  return parseInt(hourFormat(new Date(t))) * 60 + parseInt(minuteFormat(new Date(t)));
}

function visualize() {

  var margin = {top: 20, right: 20, bottom: 30, left: 20},
    width = 1000 - margin.left - margin.right,
    height = 450 - margin.top - margin.bottom;

  var svg = d3.select('#traces')
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
    .append('g')
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var x = d3.scale.linear()
    .domain([0, 24 * 60])
    .range([0, width]);

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

  var bins = {}

  for (service in activities) {
    var minuteInDay = activities[service];

    var data = d3.layout.histogram()
      .bins(x.ticks(24))
      (minuteInDay);

    bins[service] = data;
  }

  var allBins = []
  for (service in activities) {
    allBins = allBins.concat(bins[service]);
  }

  var y = d3.scale.linear()
    .domain([0, d3.max(allBins, function(d) { return d.y; })])
    .range([height, 0]);

  for (service in activities) {

    if (service in bins) {
      var data = bins[service];

      var bar = svg.selectAll('.' + className[service])
          .data(data)
        .enter().append('g')
          .attr('class', 'bar ' + className[service])
          .attr('transform', function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });

      bar.append("rect")
        .attr("x", x(data[0].dx) / 4 * service)
        .attr("width", x(data[0].dx) / 4)
        .attr("height", function(d) { return height - y(d.y); });
    }
  }

  // bar.append("text")
  //   .attr("dy", ".75em")
  //   .attr("y", 6)
  //   .attr("x", x(data[0].dx) / 2)
  //   .attr("text-anchor", "middle")
  //   .text(function(d) { return d.y; });

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);
}<commit_msg>Changed tick formatting to show times<commit_after>function requestTimestamps() {
  $.getJSON('/ig_data', function(data) {
    activities[Service.INSTAGRAM] = data;
    visualize();
  });

  $.getJSON('/fsq_data', function(data) {
    activities[Service.FOURSQUARE] = data;
    visualize();
  });

  $.getJSON('/gh_data', function(data) {
    activities[Service.GITHUB] = data;
    visualize();
  });
}

var Service = Object.freeze({FACEBOOK: 0, INSTAGRAM: 1, GITHUB: 2, FOURSQUARE: 3});
var className = {};
className[Service.FACEBOOK] = 'fb';
className[Service.INSTAGRAM] = 'ig';
className[Service.GITHUB] = 'gh';
className[Service.FOURSQUARE] = 'fsq';

var activities = {}

var hourFormat = d3.time.format("%H");
var minuteFormat = d3.time.format("%M");

function timestampsToMinuteInDay(t) {
  return parseInt(hourFormat(new Date(t))) * 60 + parseInt(minuteFormat(new Date(t)));
}

function visualize() {

  var margin = {top: 20, right: 20, bottom: 30, left: 20},
    width = 1000 - margin.left - margin.right,
    height = 450 - margin.top - margin.bottom;

  var svg = d3.select('#traces')
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
    .append('g')
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var x = d3.scale.linear()
    .domain([0, 24 * 60])
    .range([0, width]);

  var ticks = [];
  for(var i = 0; i <= 2400; i += 120){
    ticks.push(i);
  }
  var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickValues(ticks)
    .tickFormat(function(x){return d3.round(x/60)+":00"});

  var bins = {};

  for (service in activities) {
    var minuteInDay = activities[service];

    var data = d3.layout.histogram()
      .bins(x.ticks(24))
      (minuteInDay);

    bins[service] = data;
  }

  var allBins = []
  for (service in activities) {
    allBins = allBins.concat(bins[service]);
  }

  var y = d3.scale.linear()
    .domain([0, d3.max(allBins, function(d) { return d.y; })])
    .range([height, 0]);

  for (service in activities) {

    if (service in bins) {
      var data = bins[service];

      var bar = svg.selectAll('.' + className[service])
          .data(data)
        .enter().append('g')
          .attr('class', 'bar ' + className[service])
          .attr('transform', function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });

      bar.append("rect")
        .attr("x", x(data[0].dx) / 4 * service)
        .attr("width", x(data[0].dx) / 4)
        .attr("height", function(d) { return height - y(d.y); });
    }
  }

  // bar.append("text")
  //   .attr("dy", ".75em")
  //   .attr("y", 6)
  //   .attr("x", x(data[0].dx) / 2)
  //   .attr("text-anchor", "middle")
  //   .text(function(d) { return d.y; });

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);
}<|endoftext|>


<commit_before>function filter_data(data) {
    for (field in urlParams){
        if (data[field] != urlParams[field]){
            return false
        }
    }
    return true
}

function build_table(table, data, actions, selectable) {
    var body = $(table.find('tbody'));
    var headers = $(table.find('thead tr th'));
    for (index in data){
        // if (filter_data(data[index])){
            var tr = $('
');
            if (table.data().selectable){
                tr.append($('<input type="checkbox" class="checkthis">	'));
            }
            for (i = 0; i < headers.length; i++) {
                if (!(table.data().selectable && i == 0)){
                    if ($(headers[i]).hasClass('table-action')){
                        var action = $(headers[i]).data().json
                        tr.append($('
<button class="btn btn-'+action.style+'" data-target="#'+action.name+'" data-toggle="modal"></button>
	'))
                    }
                    else{
                        var value = data[index][$(headers[i]).attr('data-name')]
                        if (value && typeof(value) == "object" && value.length > 0){
                            // value = JSON.stringify(value);
                            var subt = $('
')
                            var subh = $('
')
                            var subb = $('
')
                            var subhr = $('
')
                            for (field_index in Object.keys(value[0])){
                                subhr.append($("").text(Object.keys(value[0])[field_index]))
                            }
                            subh.append(subhr)
                            for (reg_index in value){
                                var subbr = $('
')
                                for (subdata in value[reg_index]){
                                    if (typeof(value[reg_index][subdata]) == "object"){
                                        var subt2 = $('
')
                                        // var subh2 = $('
')
                                        var subb2 = $('
')
                                        // var subhr2 = $('
')
                                        // for (field_index in Object.keys(value[reg_index][subdata])){
                                        //     subhr2.append($("").text(Object.keys(value[reg_index][subdata])[field_index]))
                                        // }
                                        // subh2.append(subhr2)
                                        var subbr2 = $('
')
                                        for (reg_index2 in value[reg_index][subdata]){
                                            subbr2.append($("").text(value[reg_index][subdata][reg_index2]))
                                        }
                                        subb2.append(subbr2)
                                        // subt2.append(subh2)
                                        subt2.append(subb2)
                                        subbr.append($('').append(subt2))
                                    }
                                    else {
                                        subbr.append($("").text(value[reg_index][subdata]))
                                    }
                                }
                                subb.append(subbr)
                            }
                            subt.append(subh)
                            subt.append(subb)
                            tr.append($('').append(subt))
                        }
                        else if ($(headers[i]).data('format') == 'ImageField') {
                            if (value){
                                tr.append($('').append($('')));
                            }
                            else{
                                tr.append($(''));
                            }
                        }
                        else{
                            tr.append($('').text(value));
                        }
                    }
                }
            }
            $.each(data[index], function( key, value ) {
                if (typeof(value) == "object"){
                    value = JSON.stringify(value);
                }
                tr.attr("data-"+key, value);
            });
            body.append(tr);
        // }
    }
    $(table.find('tfoot th')).each( function () {
        var title = $(this).text();
        var width = $(this).width();
        if (title){
            $(this).html( '<input class="form-control" type="text" placeholder="Search '+title+'" style="width: 100%" />' );
            $(this).css('padding-left', '0px')
            $(this).css('padding-right', '0px')
        }
    } );
    // table.find('tfoot tr').appendTo('#'+table.attr("id")+' thead')
    build_data_table(table)
}

function build_data_table(table) {
    var unsortable = []
    var sorting = 0
    var order = 'asc'
    var headers = $(table.find('thead tr th'));
    if (table.data().selectable){
        unsortable.push(0)
        sorting = 1
    }
    if ($(headers[sorting]).data('name') == 'date'){
        order = 'desc'
    }
    for (i = 0; i < headers.length; i++){
        if ($(headers[i]).hasClass('table-action')){
            unsortable.push(i)
        }
    }
    var dt = table.DataTable({
        "sScrollY": ($(window).height()-370)+"px",
        "sScrollX": "100%",
        "bScrollCollapse": true,
        "lengthMenu": [[50, 100, -1], [50, 100, "All"]],
        "sDom": '<"top">rt<"bottom"lp><"clear">',
        "aoColumnDefs" : [{
            'bSortable' : false,
            'aTargets' : unsortable
            }],
        "aaSorting": [[sorting,order]]
    });
    // table.closest('.dataTables_scroll').find('.dataTables_scrollFoot table').appendTo('.dataTables_scrollHeadInner')
    dt.columns().every( function () {
        var that = this;
        $('input', this.footer()).on('keyup change', function () {
            if (that.search() !== this.value) {
                that.search(this.value).draw();
            }
        });
    });
}

$('table.use-rest').each(function () {
    var table = $(this);
    var name = table.attr("id");
    var useCache = table.attr("use-cache");
    $('.loading').show()
    if (useCache){
        if (name + "-update" in messages){
            var oldupdate = sessionStorage.getItem(name + "-update")
            if (oldupdate && (oldupdate < messages[name + "-update"])) {
                sessionStorage.removeItem(name)
            }
            sessionStorage.setItem(name + "-update", messages[name + "-update"]);
        }
        else{
            sessionStorage.setItem(name + "-update", new Date().getTime()+"");
        }
    }
    if (!sessionStorage.getItem(name) || !useCache){
        $.get( window.location.origin + table.data().rest, function( data ) {
            if (useCache){
                sessionStorage.setItem(name, JSON.stringify(data));
            }
            build_table(table, data)
            $('.loading').hide()
        });
        console.log("fetched");
    }
    else{
        console.log("cached");
        build_table(table, JSON.parse(sessionStorage.getItem(name)))
        $('.loading').hide()
    }
});

$(document).on('click', 'button[data-target="#edit"]', function () {
    var data = $(this).closest('tr').data()
    var editform = $("#edit form");
    editform[0].reset();
    editform.trigger("reset");
    editform.find('select').each(function (){
        $(this).val("");
    });
    for (key in data){
        var value = data[key]
        if (key == "date"){
            if (value.length == 10){
                value += " 00:00"
            }
            var d = new Date(value);
            if (editform.find('input[name="'+ key +'"]').attr('type') == "datetime-local"){
                value = d.getFullYear()+"-"+("0"+(d.getMonth()+1)).slice(-2)+"-"+("0"+d.getDate()).slice(-2)+"T"+("0"+d.getHours()).slice(-2)+":"+("0"+d.getMinutes()).slice(-2)+":"+("0"+d.getSeconds()).slice(-2);
            }
            else{
                value = d.getFullYear()+"-"+("0"+(d.getMonth()+1)).slice(-2)+"-"+("0"+d.getDate()).slice(-2);
            }
        } else if (typeof(data[key]) == "object"){
            if (data[key]){
                value = JSON.stringify(data[key]);
            }
            else{
                value = ""
            }
        }
        editform.find('input[name="'+ key +'"]').val(value);
        var field = editform.find('input[name="'+ key +'"]')
        if (field.attr("type") == "checkbox"){
            if (value == "Si" || value == "True" || value === true){
                field.prop('checked', true);
            }else {
                field.prop('checked', false);
            }
        }
        var selected = ''
        editform.find('select[name="'+ key +'"] option').each(function (){
            if ($(this).val() == data[key]){
                selected = $(this).val()
            }
        })
        editform.find('select[name="'+ key +'"]').val(selected);
        editform.find('input[type="checkbox"]').each(function () {
            $(this).val("Si")
        });
    }
});

$(document).on('click', 'button[data-target="#delete"]', function () {
    var data = $(this).closest('tr').data()
    var deleteform = $("#delete form");
    deleteform[0].reset();
    deleteform.find('input[name="id"]').val(data['id']);
});

$(document).on('click', 'button[data-target="#multi-delete"]', function () {
    var data = []
    $('input.checkthis').each(function() {
        if(this.checked){
            data.push($(this).closest('tr').data('id'));
        }
    });
    var deleteform = $("#multi-delete form");
    deleteform[0].reset();
    deleteform.find('input[name="ids"]').val(JSON.stringify(data));
});
<commit_msg>Js prints for caching system debug<commit_after>function filter_data(data) {
    for (field in urlParams){
        if (data[field] != urlParams[field]){
            return false
        }
    }
    return true
}

function build_table(table, data, actions, selectable) {
    var body = $(table.find('tbody'));
    var headers = $(table.find('thead tr th'));
    for (index in data){
        // if (filter_data(data[index])){
            var tr = $('
');
            if (table.data().selectable){
                tr.append($('<input type="checkbox" class="checkthis">	'));
            }
            for (i = 0; i < headers.length; i++) {
                if (!(table.data().selectable && i == 0)){
                    if ($(headers[i]).hasClass('table-action')){
                        var action = $(headers[i]).data().json
                        tr.append($('
<button class="btn btn-'+action.style+'" data-target="#'+action.name+'" data-toggle="modal"></button>
	'))
                    }
                    else{
                        var value = data[index][$(headers[i]).attr('data-name')]
                        if (value && typeof(value) == "object" && value.length > 0){
                            // value = JSON.stringify(value);
                            var subt = $('
')
                            var subh = $('
')
                            var subb = $('
')
                            var subhr = $('
')
                            for (field_index in Object.keys(value[0])){
                                subhr.append($("").text(Object.keys(value[0])[field_index]))
                            }
                            subh.append(subhr)
                            for (reg_index in value){
                                var subbr = $('
')
                                for (subdata in value[reg_index]){
                                    if (typeof(value[reg_index][subdata]) == "object"){
                                        var subt2 = $('
')
                                        // var subh2 = $('
')
                                        var subb2 = $('
')
                                        // var subhr2 = $('
')
                                        // for (field_index in Object.keys(value[reg_index][subdata])){
                                        //     subhr2.append($("").text(Object.keys(value[reg_index][subdata])[field_index]))
                                        // }
                                        // subh2.append(subhr2)
                                        var subbr2 = $('
')
                                        for (reg_index2 in value[reg_index][subdata]){
                                            subbr2.append($("").text(value[reg_index][subdata][reg_index2]))
                                        }
                                        subb2.append(subbr2)
                                        // subt2.append(subh2)
                                        subt2.append(subb2)
                                        subbr.append($('').append(subt2))
                                    }
                                    else {
                                        subbr.append($("").text(value[reg_index][subdata]))
                                    }
                                }
                                subb.append(subbr)
                            }
                            subt.append(subh)
                            subt.append(subb)
                            tr.append($('').append(subt))
                        }
                        else if ($(headers[i]).data('format') == 'ImageField') {
                            if (value){
                                tr.append($('').append($('')));
                            }
                            else{
                                tr.append($(''));
                            }
                        }
                        else{
                            tr.append($('').text(value));
                        }
                    }
                }
            }
            $.each(data[index], function( key, value ) {
                if (typeof(value) == "object"){
                    value = JSON.stringify(value);
                }
                tr.attr("data-"+key, value);
            });
            body.append(tr);
        // }
    }
    $(table.find('tfoot th')).each( function () {
        var title = $(this).text();
        var width = $(this).width();
        if (title){
            $(this).html( '<input class="form-control" type="text" placeholder="Search '+title+'" style="width: 100%" />' );
            $(this).css('padding-left', '0px')
            $(this).css('padding-right', '0px')
        }
    } );
    // table.find('tfoot tr').appendTo('#'+table.attr("id")+' thead')
    build_data_table(table)
}

function build_data_table(table) {
    var unsortable = []
    var sorting = 0
    var order = 'asc'
    var headers = $(table.find('thead tr th'));
    if (table.data().selectable){
        unsortable.push(0)
        sorting = 1
    }
    if ($(headers[sorting]).data('name') == 'date'){
        order = 'desc'
    }
    for (i = 0; i < headers.length; i++){
        if ($(headers[i]).hasClass('table-action')){
            unsortable.push(i)
        }
    }
    var dt = table.DataTable({
        "sScrollY": ($(window).height()-370)+"px",
        "sScrollX": "100%",
        "bScrollCollapse": true,
        "lengthMenu": [[50, 100, -1], [50, 100, "All"]],
        "sDom": '<"top">rt<"bottom"lp><"clear">',
        "aoColumnDefs" : [{
            'bSortable' : false,
            'aTargets' : unsortable
            }],
        "aaSorting": [[sorting,order]]
    });
    // table.closest('.dataTables_scroll').find('.dataTables_scrollFoot table').appendTo('.dataTables_scrollHeadInner')
    dt.columns().every( function () {
        var that = this;
        $('input', this.footer()).on('keyup change', function () {
            if (that.search() !== this.value) {
                that.search(this.value).draw();
            }
        });
    });
}

$('table.use-rest').each(function () {
    var table = $(this);
    var name = table.attr("id");
    var useCache = table.attr("use-cache");
    $('.loading').show()
    if (useCache){
        console.log("Table is set to use cache")
        if (name + "-update" in messages){
            console.log("New tsp of "+name+"-update was found in messages: "+messages[name + "-update"])
            var oldupdate = sessionStorage.getItem(name + "-update")
            console.log("Comparing to old tsp: "+oldupdate)
            if (oldupdate && (oldupdate < messages[name + "-update"])) {
                console.log("New tsp is older, removing cached table "+name+" from store")
                sessionStorage.removeItem(name)
            }
            else{
                console.log("Old tsp seems to be the latest update")
            }
            sessionStorage.setItem(name + "-update", messages[name + "-update"]);
        }
        else{
            var d = new Date().getTime()+""
            console.log("Tsp was not found in store, generating a new one: "+d)
            sessionStorage.setItem(name + "-update", d);
        }
    }
    if (!sessionStorage.getItem(name) || !useCache){
        if (!useCache) {
            console.log("Table don't use cache. Fetching")
        }
        else if (!sessionStorage.getItem(name)) {
            console.log("Cached table was not found in store. Fetching")
        }
        $.get( window.location.origin + table.data().rest, function( data ) {
            if (useCache){
                console.log("Storing table in cache")
                sessionStorage.setItem(name, JSON.stringify(data));
            }
            build_table(table, data)
            $('.loading').hide()
        });
        console.log("Fetched table data");
    }
    else{
        console.log("Using cached data");
        build_table(table, JSON.parse(sessionStorage.getItem(name)))
        $('.loading').hide()
    }
});

$(document).on('click', 'button[data-target="#edit"]', function () {
    var data = $(this).closest('tr').data()
    var editform = $("#edit form");
    editform[0].reset();
    editform.trigger("reset");
    editform.find('select').each(function (){
        $(this).val("");
    });
    for (key in data){
        var value = data[key]
        if (key == "date"){
            if (value.length == 10){
                value += " 00:00"
            }
            var d = new Date(value);
            if (editform.find('input[name="'+ key +'"]').attr('type') == "datetime-local"){
                value = d.getFullYear()+"-"+("0"+(d.getMonth()+1)).slice(-2)+"-"+("0"+d.getDate()).slice(-2)+"T"+("0"+d.getHours()).slice(-2)+":"+("0"+d.getMinutes()).slice(-2)+":"+("0"+d.getSeconds()).slice(-2);
            }
            else{
                value = d.getFullYear()+"-"+("0"+(d.getMonth()+1)).slice(-2)+"-"+("0"+d.getDate()).slice(-2);
            }
        } else if (typeof(data[key]) == "object"){
            if (data[key]){
                value = JSON.stringify(data[key]);
            }
            else{
                value = ""
            }
        }
        editform.find('input[name="'+ key +'"]').val(value);
        var field = editform.find('input[name="'+ key +'"]')
        if (field.attr("type") == "checkbox"){
            if (value == "Si" || value == "True" || value === true){
                field.prop('checked', true);
            }else {
                field.prop('checked', false);
            }
        }
        var selected = ''
        editform.find('select[name="'+ key +'"] option').each(function (){
            if ($(this).val() == data[key]){
                selected = $(this).val()
            }
        })
        editform.find('select[name="'+ key +'"]').val(selected);
        editform.find('input[type="checkbox"]').each(function () {
            $(this).val("Si")
        });
    }
});

$(document).on('click', 'button[data-target="#delete"]', function () {
    var data = $(this).closest('tr').data()
    var deleteform = $("#delete form");
    deleteform[0].reset();
    deleteform.find('input[name="id"]').val(data['id']);
});

$(document).on('click', 'button[data-target="#multi-delete"]', function () {
    var data = []
    $('input.checkthis').each(function() {
        if(this.checked){
            data.push($(this).closest('tr').data('id'));
        }
    });
    var deleteform = $("#multi-delete form");
    deleteform[0].reset();
    deleteform.find('input[name="ids"]').val(JSON.stringify(data));
});
<|endoftext|>


<commit_before>var upload = (function () {

var exports = {};

function make_upload_absolute(uri) {
    if (uri.indexOf(compose.uploads_path) === 0) {
        // Rewrite the URI to a usable link
        return compose.uploads_domain + uri;
    }
    return uri;
}

// Show the upload button only if the browser supports it.
exports.feature_check = function (upload_button) {
    if (window.XMLHttpRequest && (new XMLHttpRequest()).upload) {
        upload_button.removeClass("notdisplayed");
    }
};

exports.options = function (config) {
    var textarea;
    var send_button;
    var send_status;
    var send_status_close;
    var error_msg;
    var upload_bar;
    var should_hide_upload_status;
    var file_input;

    switch (config.mode) {
    case 'compose':
        textarea = $('#compose-textarea');
        send_button = $('#compose-send-button');
        send_status = $('#compose-send-status');
        send_status_close = $('.compose-send-status-close');
        error_msg = $('#compose-error-msg');
        upload_bar = 'compose-upload-bar';
        file_input = 'file_input';
        break;
    case 'edit':
        textarea = $('#message_edit_content_' + config.row);
        send_button = textarea.closest('.message_edit_save');
        send_status = $('#message-edit-send-status-' + config.row);
        send_status_close = send_status.find('.send-status-close');
        error_msg = send_status.find('.error-msg');
        upload_bar = 'message-edit-upload-bar-' + config.row;
        file_input = 'message_edit_file_input_' + config.row;
        break;
    default:
        throw Error("Invalid upload mode!");
    }

    var maybe_hide_upload_status = function () {
        // The first time `maybe_hide_upload_status`, it will not hide the
        // status; the second time it will. This guarantees that whether
        // `progressUpdated` or `uploadFinished` is called first, the status
        // is hidden only after the animation is finished.
        if (should_hide_upload_status) {
            setTimeout(function () {
                send_button.prop("disabled", false);
                send_status.removeClass("alert-info").hide();
                $("#" + upload_bar).parent().remove();
            }, 500);
        } else {
            should_hide_upload_status = true;
        }
    };

    var uploadStarted = function () {
        send_button.attr("disabled", "");
        send_status.addClass("alert-info").show();
        send_status_close.one('click', compose.abort_xhr);
        error_msg.html($("
").text(i18n.t("Uploading…")));
        send_status.append('
' +
                           '

' +
                           '
');
        should_hide_upload_status = false;
    };

    var progressUpdated = function (i, file, progress) {
        $("#" + upload_bar).width(progress + "%");
        if (progress === 100) {
            maybe_hide_upload_status();
        }
    };

    var uploadError = function (error_code, server_response, file) {
        var msg;
        send_status.addClass("alert-error")
            .removeClass("alert-info");
        send_button.prop("disabled", false);
        $("#" + upload_bar).parent().remove();
        switch (error_code) {
        case 'BrowserNotSupported':
            msg = i18n.t("File upload is not yet available for your browser.");
            break;
        case 'TooManyFiles':
            msg = i18n.t("Unable to upload that many files at once.");
            break;
        case 'FileTooLarge':
            // sanitization not needed as the file name is not potentially parsed as HTML, etc.
            var context = {
                file_name: file.name,
            };
            msg = i18n.t('"__file_name__" was too large; the maximum file size is 25MiB.', context);
            break;
        case 413: // HTTP status "Request Entity Too Large"
            msg = i18n.t("Sorry, the file was too large.");
            break;
        case 400:
            var server_message = server_response && server_response.msg;
            msg = server_message || i18n.t("An unknown error occurred.");
            break;
        default:
            msg = i18n.t("An unknown error occurred.");
            break;
        }
        error_msg.text(msg);
    };

    var uploadFinished = function (i, file, response) {
        if (response.uri === undefined) {
            return;
        }
        var split_uri = response.uri.split("/");
        var filename = split_uri[split_uri.length - 1];
        // Urgh, yet another hack to make sure we're "composing"
        // when text gets added into the composebox.
        if (!compose_state.composing()) {
            compose_actions.start('stream');
        }

        var uri = make_upload_absolute(response.uri);

        if (i === -1) {
            // This is a paste, so there's no filename. Show the image directly
            var pasted_image_uri = "[pasted image](" + uri + ")";
            compose_ui.insert_syntax_and_focus(pasted_image_uri, textarea);
        } else {
            // This is a dropped file, so make the filename a link to the image
            var filename_uri = "[" + filename + "](" + uri + ")";
            compose_ui.insert_syntax_and_focus(filename_uri, textarea);
        }
        compose_ui.autosize_textarea();

        maybe_hide_upload_status();

        // In order to upload the same file twice in a row, we need to clear out
        // the file input element, so that the next time we use the file dialog,
        // an actual change event is fired. IE doesn't allow .val('') so we
        // need to clone it. (Taken from the jQuery form plugin)
        if (/MSIE/.test(navigator.userAgent)) {
            $('#' + file_input).replaceWith($('#' + file_input).clone(true));
        } else {
            $('#' + file_input).val('');
        }
    };

    return {
        url: "/json/user_uploads",
        fallback_id: file_input,  // Target for standard file dialog
        paramname: "file",
        maxfilesize: page_params.maxfilesize,
        data: {
            // the token isn't automatically included in filedrop's post
            csrfmiddlewaretoken: csrf_token,
        },
        raw_droppable: ['text/uri-list', 'text/plain'],
        drop: uploadStarted,
        progressUpdated: progressUpdated,
        error: uploadError,
        uploadFinished: uploadFinished,
        rawDrop: function (contents) {
            if (!compose_state.composing()) {
                compose_actions.start('stream');
            }
            textarea.val(textarea.val() + contents);
            compose_ui.autosize_textarea();
        },
    };
};

// Expose the internal file upload functions to the desktop app,
// since the linux/windows QtWebkit based apps upload images
// directly to the server
if (window.bridge) {
    var opts = exports.options({ mode: "compose" });

    exports.uploadStarted = opts.drop;
    exports.progressUpdated = opts.progressUpdated;
    exports.uploadError = opts.error;
    exports.uploadFinished = opts.uploadFinished;
}

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = upload;
}
<commit_msg>upload: Fix stacking of progress bar on canceling the upload.<commit_after>var upload = (function () {

var exports = {};

function make_upload_absolute(uri) {
    if (uri.indexOf(compose.uploads_path) === 0) {
        // Rewrite the URI to a usable link
        return compose.uploads_domain + uri;
    }
    return uri;
}

// Show the upload button only if the browser supports it.
exports.feature_check = function (upload_button) {
    if (window.XMLHttpRequest && (new XMLHttpRequest()).upload) {
        upload_button.removeClass("notdisplayed");
    }
};

exports.options = function (config) {
    var textarea;
    var send_button;
    var send_status;
    var send_status_close;
    var error_msg;
    var upload_bar;
    var should_hide_upload_status;
    var file_input;

    switch (config.mode) {
    case 'compose':
        textarea = $('#compose-textarea');
        send_button = $('#compose-send-button');
        send_status = $('#compose-send-status');
        send_status_close = $('.compose-send-status-close');
        error_msg = $('#compose-error-msg');
        upload_bar = 'compose-upload-bar';
        file_input = 'file_input';
        break;
    case 'edit':
        textarea = $('#message_edit_content_' + config.row);
        send_button = textarea.closest('.message_edit_save');
        send_status = $('#message-edit-send-status-' + config.row);
        send_status_close = send_status.find('.send-status-close');
        error_msg = send_status.find('.error-msg');
        upload_bar = 'message-edit-upload-bar-' + config.row;
        file_input = 'message_edit_file_input_' + config.row;
        break;
    default:
        throw Error("Invalid upload mode!");
    }

    var maybe_hide_upload_status = function () {
        // The first time `maybe_hide_upload_status`, it will not hide the
        // status; the second time it will. This guarantees that whether
        // `progressUpdated` or `uploadFinished` is called first, the status
        // is hidden only after the animation is finished.
        if (should_hide_upload_status) {
            setTimeout(function () {
                send_button.prop("disabled", false);
                send_status.removeClass("alert-info").hide();
                $("#" + upload_bar).parent().remove();
            }, 500);
        } else {
            should_hide_upload_status = true;
        }
    };

    var uploadStarted = function () {
        send_button.attr("disabled", "");
        send_status.addClass("alert-info").show();
        send_status_close.one('click', function () {
            maybe_hide_upload_status();
            compose.abort_xhr();
        });
        error_msg.html($("
").text(i18n.t("Uploading…")));
        send_status.append('
' +
                           '

' +
                           '
');
        should_hide_upload_status = false;
    };

    var progressUpdated = function (i, file, progress) {
        $("#" + upload_bar).width(progress + "%");
        if (progress === 100) {
            maybe_hide_upload_status();
        }
    };

    var uploadError = function (error_code, server_response, file) {
        var msg;
        send_status.addClass("alert-error")
            .removeClass("alert-info");
        send_button.prop("disabled", false);
        $("#" + upload_bar).parent().remove();
        switch (error_code) {
        case 'BrowserNotSupported':
            msg = i18n.t("File upload is not yet available for your browser.");
            break;
        case 'TooManyFiles':
            msg = i18n.t("Unable to upload that many files at once.");
            break;
        case 'FileTooLarge':
            // sanitization not needed as the file name is not potentially parsed as HTML, etc.
            var context = {
                file_name: file.name,
            };
            msg = i18n.t('"__file_name__" was too large; the maximum file size is 25MiB.', context);
            break;
        case 413: // HTTP status "Request Entity Too Large"
            msg = i18n.t("Sorry, the file was too large.");
            break;
        case 400:
            var server_message = server_response && server_response.msg;
            msg = server_message || i18n.t("An unknown error occurred.");
            break;
        default:
            msg = i18n.t("An unknown error occurred.");
            break;
        }
        error_msg.text(msg);
    };

    var uploadFinished = function (i, file, response) {
        if (response.uri === undefined) {
            return;
        }
        var split_uri = response.uri.split("/");
        var filename = split_uri[split_uri.length - 1];
        // Urgh, yet another hack to make sure we're "composing"
        // when text gets added into the composebox.
        if (!compose_state.composing()) {
            compose_actions.start('stream');
        }

        var uri = make_upload_absolute(response.uri);

        if (i === -1) {
            // This is a paste, so there's no filename. Show the image directly
            var pasted_image_uri = "[pasted image](" + uri + ")";
            compose_ui.insert_syntax_and_focus(pasted_image_uri, textarea);
        } else {
            // This is a dropped file, so make the filename a link to the image
            var filename_uri = "[" + filename + "](" + uri + ")";
            compose_ui.insert_syntax_and_focus(filename_uri, textarea);
        }
        compose_ui.autosize_textarea();

        maybe_hide_upload_status();

        // In order to upload the same file twice in a row, we need to clear out
        // the file input element, so that the next time we use the file dialog,
        // an actual change event is fired. IE doesn't allow .val('') so we
        // need to clone it. (Taken from the jQuery form plugin)
        if (/MSIE/.test(navigator.userAgent)) {
            $('#' + file_input).replaceWith($('#' + file_input).clone(true));
        } else {
            $('#' + file_input).val('');
        }
    };

    return {
        url: "/json/user_uploads",
        fallback_id: file_input,  // Target for standard file dialog
        paramname: "file",
        maxfilesize: page_params.maxfilesize,
        data: {
            // the token isn't automatically included in filedrop's post
            csrfmiddlewaretoken: csrf_token,
        },
        raw_droppable: ['text/uri-list', 'text/plain'],
        drop: uploadStarted,
        progressUpdated: progressUpdated,
        error: uploadError,
        uploadFinished: uploadFinished,
        rawDrop: function (contents) {
            if (!compose_state.composing()) {
                compose_actions.start('stream');
            }
            textarea.val(textarea.val() + contents);
            compose_ui.autosize_textarea();
        },
    };
};

// Expose the internal file upload functions to the desktop app,
// since the linux/windows QtWebkit based apps upload images
// directly to the server
if (window.bridge) {
    var opts = exports.options({ mode: "compose" });

    exports.uploadStarted = opts.drop;
    exports.progressUpdated = opts.progressUpdated;
    exports.uploadError = opts.error;
    exports.uploadFinished = opts.uploadFinished;
}

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = upload;
}
<|endoftext|>


<commit_before>$(function() {
	var imgs = [];
	$.templates("#itemTemplate").link(".row", {imgs: imgs});

	var $row = $('.row').masonry({
		itemSelector: '.item'
	});

	$.getJSON("/data", function(data) {
		$.each(data.imgs, function(i, item) {
            $.observable(imgs).insert({src: item});
        });

        $row.masonry('reloadItems');

	    $row.imagesLoaded().progress(function() {
		    $row.masonry('layout');
	    });
	});
});
<commit_msg>bug fix<commit_after>$(function() {
	var imgs = [];
	$.templates("#itemTemplate").link(".row", {imgs: imgs});

	var $row = $('.row').masonry({
		itemSelector: '.item'
	});

	var offset = 0;

	getImages = function(offset) {
		console.log("get offset[" + offset + "]")

		url = "/data/images?offset=" + offset
		$.getJSON(url, function(data) {
			$.each(data.imgs, function(i, item) {
				$.observable(imgs).insert({src: item});
			});

			$row.masonry('reloadItems');

			$row.imagesLoaded().progress(function() {
				$row.masonry('layout');
			});
		});
	}

	getImages(offset);
	offset += 30;

	$(window).scroll(function() {
		var scrollHeight = $(document).height();
		var scrollPosition = $(window).height() + $(window).scrollTop();
		if ((scrollHeight - scrollPosition) / scrollHeight === 0) {
			getImages(offset);
			offset += 30;
		}
	});
});
<|endoftext|>


<commit_before>'use strict';

var React = require('react');

module.exports = {

    propTypes: {
        layout: React.PropTypes.string
    },

    contextTypes: {
        layout: React.PropTypes.string
    },

    getDefaultProps: function getDefaultProps() {
        return {
            disabled: false,
            validatePristine: false,
            onChange: function onChange() {},
            onFocus: function onFocus() {},
            onBlur: function onBlur() {}
        };
    },

    hashString: function hashString(string) {
        var hash = 0;
        for (var i = 0; i < string.length; i++) {
            hash = (hash << 5) - hash + string.charCodeAt(i) & 0xFFFFFFFF;
        }
        return hash;
    },

    getId: function getId() {
        return this.props.id || this.props.name.split('[').join('_').replace(']', '') + this.hashString(JSON.stringify(this.props));
    },

    getLayout: function getLayout() {
        var defaultLayout = this.context.layout || 'horizontal';
        return this.props.layout ? this.props.layout : defaultLayout;
    },

    renderHelp: function renderHelp() {
        if (!this.props.help) {
            return '';
        }
        return React.createElement(
            'span',
            { className: 'help-block' },
            this.props.help
        );
    },

    renderErrorMessage: function renderErrorMessage() {
        if (!this.showErrors()) {
            return '';
        }
        var errorMessages = this.getErrorMessages() || [];
        return errorMessages.map(function (message, key) {
            return React.createElement(
                'span',
                { key: key, className: 'help-block validation-message' },
                message
            );
        });
    },

    showErrors: function showErrors() {
        if (this.isPristine() === true) {
            if (this.props.validatePristine === false) {
                return false;
            }
        }
        return this.isValid() === false;
    }
};<commit_msg>Rebuild release.<commit_after>'use strict';

var React = require('react');

module.exports = {

    propTypes: {
        layout: React.PropTypes.string
    },

    contextTypes: {
        layout: React.PropTypes.string
    },

    getDefaultProps: function getDefaultProps() {
        return {
            disabled: false,
            validatePristine: false,
            onChange: function onChange() {},
            onFocus: function onFocus() {},
            onBlur: function onBlur() {}
        };
    },

    hashString: function hashString(string) {
        var hash = 0;
        for (var i = 0; i < string.length; i++) {
            hash = (hash << 5) - hash + string.charCodeAt(i) & 0xFFFFFFFF;
        }
        return hash;
    },

    /**
     * getId
     *
     * The ID is used as an attribute on the form control, and is used to allow
     * associating the label element with the form control.
     *
     * If we don't explicitly pass an `id` prop, we generate one based on the
     * `name` property and a hash of the component props.
     */
    getId: function getId() {
        return this.props.id || this.props.name.split('[').join('_').replace(']', '') + this.hashString(JSON.stringify(this.props));
    },

    getLayout: function getLayout() {
        var defaultLayout = this.context.layout || 'horizontal';
        return this.props.layout ? this.props.layout : defaultLayout;
    },

    renderHelp: function renderHelp() {
        if (!this.props.help) {
            return '';
        }
        return React.createElement(
            'span',
            { className: 'help-block' },
            this.props.help
        );
    },

    renderErrorMessage: function renderErrorMessage() {
        if (!this.showErrors()) {
            return '';
        }
        var errorMessages = this.getErrorMessages() || [];
        return errorMessages.map(function (message, key) {
            return React.createElement(
                'span',
                { key: key, className: 'help-block validation-message' },
                message
            );
        });
    },

    showErrors: function showErrors() {
        if (this.isPristine() === true) {
            if (this.props.validatePristine === false) {
                return false;
            }
        }
        return this.isValid() === false;
    }
};<|endoftext|>


<commit_before>const path = require('path')
const test = require('ava')
const {spawn} = require('cross-spawn')

const deployHelpMessage = '𝚫 now [options] <command | path>'
const aliasHelpMessage = '𝚫 now alias <ls | set | rm> <deployment> <alias>'

test('"now help" prints deploy help message', async t => {
  const result = await now('help')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now --help" prints deploy help message', async t => {
  const result = await now('--help')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now deploy --help" prints deploy help message', async t => {
  const result = await now('deploy', '--help')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now --help deploy" prints deploy help message', async t => {
  const result = await now('--help', 'deploy')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now help alias" prints alias help message', async t => {
  const result = await now('help', 'alias')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(aliasHelpMessage))
})

test('"now alias --help" is the same as "now --help alias"', async t => {
  const [result1, result2] = await Promise.all([now('alias', '--help'), now('--help', 'alias')])

  t.is(result1.code, 0)
  t.is(result1.code, result2.code)
  t.is(result1.stdout, result2.stdout)
})

/**
 * Run the built now binary with given arguments
 *
 * @param  {String} args  string arguements
 * @return {Promise}      promise that resolves to an object {code, stdout}
 */
function now(...args) {
  return new Promise((resolve, reject) => {
    const command = path.resolve(__dirname, '../bin/now')
    const now = spawn(command, args)

    let stdout = ''
    now.stdout.on('data', data => {
      stdout += data
    })

    now.on('error', err => {
      reject(err)
    })

    now.on('close', code => {
      resolve({
        code,
        stdout
      })
    })
  })
}
<commit_msg>Make tests use the correct binary<commit_after>const path = require('path')
const test = require('ava')
const {spawn} = require('cross-spawn')

const deployHelpMessage = '𝚫 now [options] <command | path>'
const aliasHelpMessage = '𝚫 now alias <ls | set | rm> <deployment> <alias>'

test('"now help" prints deploy help message', async t => {
  const result = await now('help')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now --help" prints deploy help message', async t => {
  const result = await now('--help')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now deploy --help" prints deploy help message', async t => {
  const result = await now('deploy', '--help')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now --help deploy" prints deploy help message', async t => {
  const result = await now('--help', 'deploy')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(deployHelpMessage))
})

test('"now help alias" prints alias help message', async t => {
  const result = await now('help', 'alias')

  t.is(result.code, 0)
  const stdout = result.stdout.split('\n')
  t.true(stdout.length > 1)
  t.true(stdout[1].includes(aliasHelpMessage))
})

test('"now alias --help" is the same as "now --help alias"', async t => {
  const [result1, result2] = await Promise.all([now('alias', '--help'), now('--help', 'alias')])

  t.is(result1.code, 0)
  t.is(result1.code, result2.code)
  t.is(result1.stdout, result2.stdout)
})

/**
 * Run the built now binary with given arguments
 *
 * @param  {String} args  string arguements
 * @return {Promise}      promise that resolves to an object {code, stdout}
 */
function now(...args) {
  return new Promise((resolve, reject) => {
    const command = path.resolve(__dirname, '../bin/now.js')
    const now = spawn(command, args)

    let stdout = ''
    now.stdout.on('data', data => {
      stdout += data
    })

    now.on('error', err => {
      reject(err)
    })

    now.on('close', code => {
      resolve({
        code,
        stdout
      })
    })
  })
}
<|endoftext|>


<commit_before>import React from 'react';

import { storiesOf } from '@storybook/react';
import { number, boolean } from '@storybook/addon-knobs';
import { Story } from '@storybook/addon-info';

import RenderCounter, { Counter, StatelessRenderCounter, withRenderCount } from '../../src/RenderCounter';
import DivRefreshable from './DivRefreshable';
import MyCounter from './MyCounter';
import createOuterHandler from './createOuterHandler';
import './RenderCounter.story.css';

const stories = storiesOf('RenderCounter', module)
  .addDecorator((getStory) => {
    const element = getStory();
    if (element.type === Story) {
      return
{element}
;
    }
    return element;
  });

const defaultDesc = `
  NOTICE: You may see some extra times.\n
  That's a bug of storybook happens when you enter this page directly.\n
  You can click another menu and re-enter this route to see real render times.
`;

const defaultOptions = {
  propTablesExclude: [DivRefreshable],
};
const addRenderCounter = (name, Component, { desc = defaultDesc, options } = {}) =>
  stories.addWithInfo(name, desc, () => {
    const blockOnRerender = boolean('blockOnRerender', true);
    const onRerender = createOuterHandler({ name: 'onRerender', block: blockOnRerender });
    const initialCount = number('initialCount', 1);
    return (

        {`blockOnRerender: ${JSON.stringify(blockOnRerender)}`}
        <DivRefreshable>
          <Component initialCount={initialCount} onRerender={onRerender} />
        </DivRefreshable>

    );
  },
  { ...defaultOptions, ...options });

addRenderCounter('RenderCounter', RenderCounter);
addRenderCounter('StatelessRenderCounter', StatelessRenderCounter, {
  options: {
    propTables: [StatelessRenderCounter, Counter],
  },
});

const MyRenderCounter = withRenderCount(MyCounter);

addRenderCounter('withRenderCount', MyRenderCounter, {
  options: {
    propTables: [MyRenderCounter, MyCounter],
  },
});
<commit_msg>fixStory(RenderCounter): use withInfo<commit_after>import React from 'react';

import { storiesOf } from '@storybook/react';
import { number, boolean } from '@storybook/addon-knobs';
import { Story, withInfo } from '@storybook/addon-info';

import RenderCounter, { Counter, StatelessRenderCounter, withRenderCount } from '../../src/RenderCounter';
import DivRefreshable from './DivRefreshable';
import MyCounter from './MyCounter';
import createOuterHandler from './createOuterHandler';
import './RenderCounter.story.css';

const stories = storiesOf('RenderCounter', module)
  .addDecorator((getStory) => {
    const element = getStory();
    if (element.type === Story) {
      return
{element}
;
    }
    return element;
  });

const defaultDesc = `
  NOTICE: You may see some extra times.\n
  That's a bug of storybook happens when you enter this page directly.\n
  You can click another menu and re-enter this route to see real render times.
`;

const defaultOptions = {
  propTablesExclude: [DivRefreshable],
};
const addRenderCounter = (name, Component, { desc = defaultDesc, options } = {}) =>
  stories.add(name, withInfo({
    text: desc,
    ...defaultOptions,
    ...options,
  })(() => {
    const blockOnRerender = boolean('blockOnRerender', true);
    const onRerender = createOuterHandler({ name: 'onRerender', block: blockOnRerender });
    const initialCount = number('initialCount', 1);
    return (

        {`blockOnRerender: ${JSON.stringify(blockOnRerender)}`}
        <DivRefreshable>
          <Component initialCount={initialCount} onRerender={onRerender} />
        </DivRefreshable>

    );
  }));

addRenderCounter('RenderCounter', RenderCounter);
addRenderCounter('StatelessRenderCounter', StatelessRenderCounter, {
  options: {
    propTables: [StatelessRenderCounter, Counter],
  },
});

const MyRenderCounter = withRenderCount(MyCounter);

addRenderCounter('withRenderCount', MyRenderCounter, {
  options: {
    propTables: [MyRenderCounter, MyCounter],
  },
});
<|endoftext|>